[med-svn] [python-xlsxwriter] 01/02: New upstream version 0.9.6
Andreas Tille
tille at debian.org
Wed Jan 25 12:30:52 UTC 2017
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository python-xlsxwriter.
commit 54e14cef98174167ebf74a99f5fa4aae9a24da3c
Author: Andreas Tille <tille at debian.org>
Date: Wed Jan 25 13:28:59 2017 +0100
New upstream version 0.9.6
---
.gitignore | 29 +
.travis.yml | 13 +
CONTRIBUTING.md | 149 +
Changes | 987 ++
ISSUE_TEMPLATE.md | 63 +
LICENSE.txt | 26 +
MANIFEST.in | 9 +
Makefile | 78 +
PULL_REQUEST_TEMPLATE.md | 85 +
README.rst | 66 +
conftest.py | 36 +
dev/Readme.txt | 6 +
dev/docs/Makefile | 152 +
dev/docs/make.bat | 190 +
dev/docs/source/_images/array_formula.png | Bin 0 -> 73370 bytes
dev/docs/source/_images/autofilter1.png | Bin 0 -> 98877 bytes
dev/docs/source/_images/autofilter2.png | Bin 0 -> 42670 bytes
dev/docs/source/_images/autofilter3.png | Bin 0 -> 86704 bytes
dev/docs/source/_images/chart_area1.png | Bin 0 -> 74823 bytes
dev/docs/source/_images/chart_area2.png | Bin 0 -> 72112 bytes
dev/docs/source/_images/chart_area3.png | Bin 0 -> 67410 bytes
dev/docs/source/_images/chart_bar1.png | Bin 0 -> 68715 bytes
dev/docs/source/_images/chart_bar2.png | Bin 0 -> 66812 bytes
dev/docs/source/_images/chart_bar3.png | Bin 0 -> 67926 bytes
dev/docs/source/_images/chart_chartarea.png | Bin 0 -> 48747 bytes
dev/docs/source/_images/chart_clustered.png | Bin 0 -> 61877 bytes
dev/docs/source/_images/chart_column1.png | Bin 0 -> 68758 bytes
dev/docs/source/_images/chart_column2.png | Bin 0 -> 68167 bytes
dev/docs/source/_images/chart_column3.png | Bin 0 -> 68439 bytes
dev/docs/source/_images/chart_combined1.png | Bin 0 -> 73011 bytes
dev/docs/source/_images/chart_combined2.png | Bin 0 -> 79308 bytes
dev/docs/source/_images/chart_data_labels1.png | Bin 0 -> 66777 bytes
dev/docs/source/_images/chart_data_table1.png | Bin 0 -> 71684 bytes
dev/docs/source/_images/chart_data_table2.png | Bin 0 -> 71565 bytes
dev/docs/source/_images/chart_data_tools1.png | Bin 0 -> 88110 bytes
dev/docs/source/_images/chart_data_tools2.png | Bin 0 -> 84129 bytes
dev/docs/source/_images/chart_data_tools3.png | Bin 0 -> 79812 bytes
dev/docs/source/_images/chart_data_tools4.png | Bin 0 -> 75653 bytes
dev/docs/source/_images/chart_data_tools5.png | Bin 0 -> 80959 bytes
dev/docs/source/_images/chart_data_tools6.png | Bin 0 -> 80323 bytes
dev/docs/source/_images/chart_date_axis.png | Bin 0 -> 82079 bytes
dev/docs/source/_images/chart_display_units.png | Bin 0 -> 61897 bytes
dev/docs/source/_images/chart_doughnut1.png | Bin 0 -> 76800 bytes
dev/docs/source/_images/chart_doughnut2.png | Bin 0 -> 99808 bytes
dev/docs/source/_images/chart_drop_lines.png | Bin 0 -> 77076 bytes
dev/docs/source/_images/chart_error_bars1.png | Bin 0 -> 65211 bytes
dev/docs/source/_images/chart_error_bars2.png | Bin 0 -> 65308 bytes
dev/docs/source/_images/chart_fill1.png | Bin 0 -> 50839 bytes
dev/docs/source/_images/chart_fill2.png | Bin 0 -> 50643 bytes
dev/docs/source/_images/chart_font1.png | Bin 0 -> 68420 bytes
dev/docs/source/_images/chart_formatting1.png | Bin 0 -> 62651 bytes
dev/docs/source/_images/chart_formatting2.png | Bin 0 -> 62735 bytes
dev/docs/source/_images/chart_formatting3.png | Bin 0 -> 52119 bytes
dev/docs/source/_images/chart_formatting4.png | Bin 0 -> 63705 bytes
dev/docs/source/_images/chart_formatting5.png | Bin 0 -> 59369 bytes
dev/docs/source/_images/chart_gradient.png | Bin 0 -> 157058 bytes
dev/docs/source/_images/chart_gridlines.png | Bin 0 -> 56400 bytes
dev/docs/source/_images/chart_high_low_lines.png | Bin 0 -> 76912 bytes
dev/docs/source/_images/chart_layout.png | Bin 0 -> 45346 bytes
dev/docs/source/_images/chart_legend_bottom.png | Bin 0 -> 50946 bytes
dev/docs/source/_images/chart_legend_delete.png | Bin 0 -> 49418 bytes
dev/docs/source/_images/chart_legend_none.png | Bin 0 -> 47487 bytes
dev/docs/source/_images/chart_line1.png | Bin 0 -> 82245 bytes
dev/docs/source/_images/chart_marker1.png | Bin 0 -> 65743 bytes
dev/docs/source/_images/chart_marker2.png | Bin 0 -> 64424 bytes
dev/docs/source/_images/chart_max_min.png | Bin 0 -> 72770 bytes
dev/docs/source/_images/chart_pareto.png | Bin 0 -> 76295 bytes
dev/docs/source/_images/chart_pattern.png | Bin 0 -> 74690 bytes
dev/docs/source/_images/chart_pie1.png | Bin 0 -> 70432 bytes
dev/docs/source/_images/chart_pie2.png | Bin 0 -> 63156 bytes
dev/docs/source/_images/chart_plotarea.png | Bin 0 -> 57144 bytes
dev/docs/source/_images/chart_points1.png | Bin 0 -> 55032 bytes
dev/docs/source/_images/chart_radar1.png | Bin 0 -> 80368 bytes
dev/docs/source/_images/chart_radar2.png | Bin 0 -> 84498 bytes
dev/docs/source/_images/chart_radar3.png | Bin 0 -> 77420 bytes
dev/docs/source/_images/chart_reverse.png | Bin 0 -> 53203 bytes
dev/docs/source/_images/chart_scatter1.png | Bin 0 -> 72426 bytes
dev/docs/source/_images/chart_scatter2.png | Bin 0 -> 85666 bytes
dev/docs/source/_images/chart_scatter3.png | Bin 0 -> 78145 bytes
dev/docs/source/_images/chart_scatter4.png | Bin 0 -> 85168 bytes
dev/docs/source/_images/chart_scatter5.png | Bin 0 -> 78915 bytes
dev/docs/source/_images/chart_secondary_axis1.png | Bin 0 -> 78944 bytes
dev/docs/source/_images/chart_secondary_axis2.png | Bin 0 -> 80706 bytes
dev/docs/source/_images/chart_simple.png | Bin 0 -> 52957 bytes
dev/docs/source/_images/chart_stock1.png | Bin 0 -> 72229 bytes
dev/docs/source/_images/chart_style.png | Bin 0 -> 54304 bytes
dev/docs/source/_images/chart_styles.png | Bin 0 -> 81961 bytes
dev/docs/source/_images/chart_table.png | Bin 0 -> 62041 bytes
dev/docs/source/_images/chart_title.png | Bin 0 -> 57415 bytes
dev/docs/source/_images/chart_trendline1.png | Bin 0 -> 73948 bytes
dev/docs/source/_images/chart_trendline2.png | Bin 0 -> 72544 bytes
dev/docs/source/_images/chart_trendline3.png | Bin 0 -> 35836 bytes
dev/docs/source/_images/chart_up_down_bars.png | Bin 0 -> 71895 bytes
dev/docs/source/_images/chart_working.png | Bin 0 -> 73073 bytes
dev/docs/source/_images/chart_x_axis.png | Bin 0 -> 57055 bytes
dev/docs/source/_images/chartsheet.png | Bin 0 -> 60366 bytes
dev/docs/source/_images/comments1.png | Bin 0 -> 67678 bytes
dev/docs/source/_images/comments2.png | Bin 0 -> 73745 bytes
dev/docs/source/_images/conditional_format1.png | Bin 0 -> 90297 bytes
dev/docs/source/_images/conditional_format2.png | Bin 0 -> 88166 bytes
dev/docs/source/_images/conditional_format3.png | Bin 0 -> 64923 bytes
dev/docs/source/_images/conditional_format4.png | Bin 0 -> 66172 bytes
dev/docs/source/_images/custom_properties.png | Bin 0 -> 75381 bytes
dev/docs/source/_images/data_validate1.png | Bin 0 -> 85482 bytes
dev/docs/source/_images/data_validate2.png | Bin 0 -> 39896 bytes
dev/docs/source/_images/data_validate3.png | Bin 0 -> 85421 bytes
dev/docs/source/_images/defined_name.png | Bin 0 -> 69426 bytes
dev/docs/source/_images/demo.png | Bin 0 -> 76404 bytes
dev/docs/source/_images/diagonal_border.png | Bin 0 -> 60684 bytes
dev/docs/source/_images/doc_properties.png | Bin 0 -> 80781 bytes
dev/docs/source/_images/format_font_align.png | Bin 0 -> 54811 bytes
dev/docs/source/_images/format_font_bold.png | Bin 0 -> 49361 bytes
dev/docs/source/_images/format_font_color.png | Bin 0 -> 49996 bytes
dev/docs/source/_images/format_font_italic.png | Bin 0 -> 50080 bytes
dev/docs/source/_images/format_font_name.png | Bin 0 -> 50617 bytes
dev/docs/source/_images/format_font_script.png | Bin 0 -> 50257 bytes
dev/docs/source/_images/format_font_size.png | Bin 0 -> 50530 bytes
dev/docs/source/_images/format_font_strikeout.png | Bin 0 -> 49724 bytes
.../source/_images/format_font_text_rotated.png | Bin 0 -> 57599 bytes
dev/docs/source/_images/format_font_text_wrap.png | Bin 0 -> 59063 bytes
dev/docs/source/_images/format_font_underlined.png | Bin 0 -> 49823 bytes
dev/docs/source/_images/formats_intro.png | Bin 0 -> 71948 bytes
dev/docs/source/_images/formats_num_str.png | Bin 0 -> 78558 bytes
dev/docs/source/_images/formats_set_bg_color.png | Bin 0 -> 55707 bytes
dev/docs/source/_images/header_image.png | Bin 0 -> 78152 bytes
dev/docs/source/_images/headers_footers.png | Bin 0 -> 61815 bytes
dev/docs/source/_images/hello01.png | Bin 0 -> 63966 bytes
dev/docs/source/_images/hide_row_col.png | Bin 0 -> 64080 bytes
dev/docs/source/_images/hide_sheet.png | Bin 0 -> 65071 bytes
dev/docs/source/_images/hyperlink.png | Bin 0 -> 76335 bytes
dev/docs/source/_images/images.png | Bin 0 -> 116344 bytes
dev/docs/source/_images/images_bytesio.png | Bin 0 -> 79257 bytes
dev/docs/source/_images/insert_image.png | Bin 0 -> 73739 bytes
dev/docs/source/_images/logo.png | Bin 0 -> 6802 bytes
dev/docs/source/_images/macros.png | Bin 0 -> 66340 bytes
dev/docs/source/_images/merge1.png | Bin 0 -> 63153 bytes
dev/docs/source/_images/merge_range.png | Bin 0 -> 61872 bytes
dev/docs/source/_images/merge_rich.png | Bin 0 -> 65797 bytes
dev/docs/source/_images/outline1.png | Bin 0 -> 85343 bytes
dev/docs/source/_images/outline2.png | Bin 0 -> 77771 bytes
dev/docs/source/_images/outline3.png | Bin 0 -> 79931 bytes
dev/docs/source/_images/outline4.png | Bin 0 -> 78798 bytes
dev/docs/source/_images/outline5.png | Bin 0 -> 76165 bytes
dev/docs/source/_images/outline6.png | Bin 0 -> 70981 bytes
dev/docs/source/_images/pandas_chart.png | Bin 0 -> 64858 bytes
dev/docs/source/_images/pandas_chart_columns.png | Bin 0 -> 29994 bytes
dev/docs/source/_images/pandas_chart_line.png | Bin 0 -> 92995 bytes
dev/docs/source/_images/pandas_chart_stock.png | Bin 0 -> 51537 bytes
dev/docs/source/_images/pandas_column_formats.png | Bin 0 -> 72536 bytes
dev/docs/source/_images/pandas_conditional.png | Bin 0 -> 61232 bytes
dev/docs/source/_images/pandas_datetime.png | Bin 0 -> 76739 bytes
dev/docs/source/_images/pandas_multiple.png | Bin 0 -> 59659 bytes
dev/docs/source/_images/pandas_positioning.png | Bin 0 -> 64105 bytes
dev/docs/source/_images/pandas_simple.png | Bin 0 -> 60664 bytes
dev/docs/source/_images/panes.png | Bin 0 -> 71852 bytes
dev/docs/source/_images/rich_strings.png | Bin 0 -> 75466 bytes
dev/docs/source/_images/rich_strings_small.png | Bin 0 -> 59203 bytes
dev/docs/source/_images/sparklines1.png | Bin 0 -> 67413 bytes
dev/docs/source/_images/sparklines2.png | Bin 0 -> 175673 bytes
dev/docs/source/_images/tab_colors.png | Bin 0 -> 66341 bytes
dev/docs/source/_images/tables1.png | Bin 0 -> 71723 bytes
dev/docs/source/_images/tables10.png | Bin 0 -> 89186 bytes
dev/docs/source/_images/tables11.png | Bin 0 -> 88077 bytes
dev/docs/source/_images/tables12.png | Bin 0 -> 90433 bytes
dev/docs/source/_images/tables2.png | Bin 0 -> 80817 bytes
dev/docs/source/_images/tables3.png | Bin 0 -> 80766 bytes
dev/docs/source/_images/tables4.png | Bin 0 -> 78824 bytes
dev/docs/source/_images/tables5.png | Bin 0 -> 84010 bytes
dev/docs/source/_images/tables6.png | Bin 0 -> 84200 bytes
dev/docs/source/_images/tables7.png | Bin 0 -> 82502 bytes
dev/docs/source/_images/tables8.png | Bin 0 -> 89454 bytes
dev/docs/source/_images/tables9.png | Bin 0 -> 85174 bytes
dev/docs/source/_images/text_indent.png | Bin 0 -> 66311 bytes
dev/docs/source/_images/textbox01.png | Bin 0 -> 73936 bytes
dev/docs/source/_images/textbox02.png | Bin 0 -> 67911 bytes
dev/docs/source/_images/textbox03.png | Bin 0 -> 58797 bytes
dev/docs/source/_images/textbox11.png | Bin 0 -> 7151 bytes
dev/docs/source/_images/textbox12.png | Bin 0 -> 25625 bytes
dev/docs/source/_images/textbox13.png | Bin 0 -> 6710 bytes
dev/docs/source/_images/textbox14.png | Bin 0 -> 6768 bytes
dev/docs/source/_images/textbox15.png | Bin 0 -> 7141 bytes
dev/docs/source/_images/textbox16.png | Bin 0 -> 9629 bytes
dev/docs/source/_images/textbox17.png | Bin 0 -> 10788 bytes
dev/docs/source/_images/textbox21.png | Bin 0 -> 58042 bytes
dev/docs/source/_images/textbox22.png | Bin 0 -> 6427 bytes
dev/docs/source/_images/textbox23.png | Bin 0 -> 21563 bytes
dev/docs/source/_images/textbox24.png | Bin 0 -> 26424 bytes
dev/docs/source/_images/textbox25.png | Bin 0 -> 62813 bytes
dev/docs/source/_images/textbox26.png | Bin 0 -> 9065 bytes
dev/docs/source/_images/textbox31.png | Bin 0 -> 7072 bytes
dev/docs/source/_images/textbox32.png | Bin 0 -> 6785 bytes
dev/docs/source/_images/textbox33.png | Bin 0 -> 57988 bytes
dev/docs/source/_images/textbox34.png | Bin 0 -> 57650 bytes
dev/docs/source/_images/textbox35.png | Bin 0 -> 57056 bytes
dev/docs/source/_images/textbox41.png | Bin 0 -> 6387 bytes
dev/docs/source/_images/textbox42.png | Bin 0 -> 5921 bytes
dev/docs/source/_images/tutorial01.png | Bin 0 -> 70503 bytes
dev/docs/source/_images/tutorial02.png | Bin 0 -> 73535 bytes
dev/docs/source/_images/tutorial03.png | Bin 0 -> 80720 bytes
dev/docs/source/_images/unicode_polish_utf8.png | Bin 0 -> 96351 bytes
dev/docs/source/_images/unicode_python2.png | Bin 0 -> 67732 bytes
dev/docs/source/_images/unicode_python3.png | Bin 0 -> 67772 bytes
dev/docs/source/_images/unicode_shift_jis.png | Bin 0 -> 80799 bytes
dev/docs/source/_images/workbook01.png | Bin 0 -> 64480 bytes
dev/docs/source/_images/workbook02.png | Bin 0 -> 59917 bytes
.../_images/working_with_dates_and_times01.png | Bin 0 -> 76822 bytes
.../_images/working_with_dates_and_times02.png | Bin 0 -> 94956 bytes
dev/docs/source/_images/working_with_formulas1.png | Bin 0 -> 49271 bytes
dev/docs/source/_images/working_with_formulas2.png | Bin 0 -> 52063 bytes
dev/docs/source/_images/worksheet00.png | Bin 0 -> 55782 bytes
dev/docs/source/_images/worksheet01.png | Bin 0 -> 66099 bytes
dev/docs/source/_images/worksheet02.png | Bin 0 -> 58156 bytes
dev/docs/source/_images/worksheet_activate.png | Bin 0 -> 55570 bytes
dev/docs/source/_images/worksheet_protection.png | Bin 0 -> 72536 bytes
dev/docs/source/_static/basic.css | 543 ++
dev/docs/source/_static/default.css | 258 +
dev/docs/source/_static/pygments.css | 61 +
dev/docs/source/_themes/bootstrap/globaltoc.html | 7 +
dev/docs/source/_themes/bootstrap/layout.html | 144 +
dev/docs/source/_themes/bootstrap/localtoc.html | 1 +
dev/docs/source/_themes/bootstrap/navbar-2.html | 45 +
dev/docs/source/_themes/bootstrap/navbar.html | 44 +
.../source/_themes/bootstrap/navbarsearchbox.html | 9 +
dev/docs/source/_themes/bootstrap/navbartoc.html | 4 +
dev/docs/source/_themes/bootstrap/relations.html | 8 +
dev/docs/source/_themes/bootstrap/search.html | 58 +
dev/docs/source/_themes/bootstrap/searchbox.html | 9 +
.../source/_themes/bootstrap/searchresults.html | 38 +
dev/docs/source/_themes/bootstrap/sourcelink.html | 6 +
.../static/bootstrap-3.0.0/css/bootstrap-theme.css | 384 +
.../bootstrap-3.0.0/css/bootstrap-theme.min.css | 356 +
.../static/bootstrap-3.0.0/css/bootstrap.css | 5909 ++++++++++++
.../static/bootstrap-3.0.0/css/bootstrap.min.css | 6435 +++++++++++++
.../fonts/glyphicons-halflings-regular.eot | Bin 0 -> 14079 bytes
.../fonts/glyphicons-halflings-regular.svg | 228 +
.../fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 29512 bytes
.../fonts/glyphicons-halflings-regular.woff | Bin 0 -> 16448 bytes
.../static/bootstrap-3.0.0/js/bootstrap.js | 1999 ++++
.../static/bootstrap-3.0.0/js/bootstrap.min.js | 6 +
.../bootstrap/static/bootstrap-sphinx.css_t | 227 +
.../_themes/bootstrap/static/bootstrap-sphinx.js_t | 160 +
.../_themes/bootstrap/static/js/jquery-1.9.1.js | 9597 ++++++++++++++++++++
.../bootstrap/static/js/jquery-1.9.1.min.js | 5 +
.../_themes/bootstrap/static/js/jquery-fix.js | 2 +
.../_themes/bootstrap/static/js/jquery.min.map | 1 +
dev/docs/source/_themes/bootstrap/theme.conf | 60 +
dev/docs/source/alternatives.rst | 52 +
dev/docs/source/author.rst | 62 +
dev/docs/source/bugs.rst | 179 +
dev/docs/source/changes.rst | 10 +
dev/docs/source/chart.rst | 1126 +++
dev/docs/source/chart_examples.rst | 34 +
dev/docs/source/chartsheet.rst | 99 +
dev/docs/source/conf.py | 306 +
dev/docs/source/contents.rst | 42 +
dev/docs/source/example_array_formula.rst | 13 +
dev/docs/source/example_autofilter.rst | 12 +
dev/docs/source/example_chart_area.rst | 23 +
dev/docs/source/example_chart_bar.rst | 23 +
dev/docs/source/example_chart_clustered.rst | 16 +
dev/docs/source/example_chart_column.rst | 23 +
dev/docs/source/example_chart_combined.rst | 20 +
dev/docs/source/example_chart_data_table.rst | 18 +
dev/docs/source/example_chart_data_tools.rst | 40 +
dev/docs/source/example_chart_date_axis.rst | 22 +
dev/docs/source/example_chart_doughnut.rst | 21 +
dev/docs/source/example_chart_gradient.rst | 12 +
dev/docs/source/example_chart_line.rst | 15 +
dev/docs/source/example_chart_pareto.rst | 11 +
dev/docs/source/example_chart_pattern.rst | 11 +
dev/docs/source/example_chart_pie.rst | 21 +
dev/docs/source/example_chart_radar.rst | 24 +
dev/docs/source/example_chart_scatter.rst | 34 +
dev/docs/source/example_chart_secondary_axis.rst | 13 +
dev/docs/source/example_chart_simple.rst | 14 +
dev/docs/source/example_chart_stock.rst | 13 +
dev/docs/source/example_chart_styles.rst | 13 +
dev/docs/source/example_chartsheet.rst | 10 +
dev/docs/source/example_comments1.rst | 11 +
dev/docs/source/example_comments2.rst | 12 +
dev/docs/source/example_conditional_format.rst | 12 +
dev/docs/source/example_data_validate.rst | 12 +
dev/docs/source/example_datetimes.rst | 13 +
dev/docs/source/example_defined_name.rst | 14 +
dev/docs/source/example_demo.rst | 19 +
dev/docs/source/example_diagonal_border.rst | 15 +
dev/docs/source/example_doc_properties.rst | 12 +
dev/docs/source/example_headers_footers.rst | 12 +
dev/docs/source/example_hello_world.rst | 11 +
dev/docs/source/example_hide_row_col.rst | 23 +
dev/docs/source/example_hide_sheet.rst | 12 +
dev/docs/source/example_http_server.rst | 19 +
dev/docs/source/example_http_server3.rst | 18 +
dev/docs/source/example_hyperlink.rst | 12 +
dev/docs/source/example_images.rst | 12 +
dev/docs/source/example_images_bytesio.rst | 16 +
dev/docs/source/example_macros.rst | 14 +
dev/docs/source/example_merge1.rst | 12 +
dev/docs/source/example_merge_rich.rst | 21 +
dev/docs/source/example_outline1.rst | 11 +
dev/docs/source/example_outline2.rst | 12 +
dev/docs/source/example_pandas_chart.rst | 11 +
dev/docs/source/example_pandas_chart_columns.rst | 12 +
dev/docs/source/example_pandas_chart_line.rst | 12 +
dev/docs/source/example_pandas_chart_stock.rst | 16 +
dev/docs/source/example_pandas_column_formats.rst | 16 +
dev/docs/source/example_pandas_conditional.rst | 11 +
dev/docs/source/example_pandas_datetime.rst | 12 +
dev/docs/source/example_pandas_multiple.rst | 11 +
dev/docs/source/example_pandas_positioning.rst | 12 +
dev/docs/source/example_pandas_simple.rst | 11 +
dev/docs/source/example_panes.rst | 13 +
dev/docs/source/example_protection.rst | 12 +
dev/docs/source/example_rich_strings.rst | 12 +
dev/docs/source/example_sparklines1.rst | 16 +
dev/docs/source/example_sparklines2.rst | 16 +
dev/docs/source/example_tab_colors.rst | 12 +
dev/docs/source/example_tables.rst | 15 +
dev/docs/source/example_textbox.rst | 12 +
dev/docs/source/example_unicode_polish_utf8.rst | 19 +
dev/docs/source/example_unicode_python2.rst | 17 +
dev/docs/source/example_unicode_python3.rst | 15 +
dev/docs/source/example_unicode_shift_jis.rst | 19 +
dev/docs/source/examples.rst | 47 +
dev/docs/source/faq.rst | 67 +
dev/docs/source/format.rst | 1037 +++
dev/docs/source/getting_started.rst | 122 +
dev/docs/source/index.rst | 112 +
dev/docs/source/introduction.rst | 36 +
dev/docs/source/license.rst | 9 +
dev/docs/source/page_setup.rst | 726 ++
dev/docs/source/pandas_examples.rst | 25 +
dev/docs/source/tutorial01.rst | 119 +
dev/docs/source/tutorial02.rst | 115 +
dev/docs/source/tutorial03.rst | 152 +
dev/docs/source/workbook.rst | 600 ++
dev/docs/source/working_with_autofilters.rst | 168 +
dev/docs/source/working_with_cell_comments.rst | 146 +
dev/docs/source/working_with_cell_notation.rst | 216 +
dev/docs/source/working_with_charts.rst | 1481 +++
dev/docs/source/working_with_colors.rst | 51 +
.../source/working_with_conditional_formats.rst | 736 ++
dev/docs/source/working_with_data_validation.rst | 399 +
dev/docs/source/working_with_dates_and_time.rst | 187 +
dev/docs/source/working_with_formulas.rst | 291 +
dev/docs/source/working_with_macros.rst | 167 +
dev/docs/source/working_with_memory.rst | 140 +
dev/docs/source/working_with_outlines.rst | 94 +
dev/docs/source/working_with_pandas.rst | 258 +
dev/docs/source/working_with_sparklines.rst | 321 +
dev/docs/source/working_with_tables.rst | 398 +
dev/docs/source/working_with_textboxes.rst | 454 +
dev/docs/source/worksheet.rst | 2120 +++++
dev/performance/Readme.txt | 6 +
dev/performance/bench_excel_writers.py | 177 +
dev/performance/perf_ewx.pl | 62 +
dev/performance/perf_pyx.py | 63 +
dev/performance/perf_test.sh | 77 +
dev/release/Readme.txt | 4 +
dev/release/modify_latex.pl | 57 +
dev/release/release_check.sh | 199 +
dev/release/update_revison.pl | 34 +
docs/Readme.txt | 3 +
docs/XlsxWriter.pdf | Bin 0 -> 9560068 bytes
docs/_static/basic.css | 541 ++
docs/_static/default.css | 258 +
docs/_static/hello01.png | Bin 0 -> 63966 bytes
docs/_static/logo.png | Bin 0 -> 6802 bytes
docs/_static/pygments.css | 61 +
docs/readme.html | 152 +
examples/array_formula.py | 36 +
examples/autofilter.py | 237 +
examples/autofilter_data.txt | 51 +
examples/cell_indentation.py | 23 +
examples/chart.py | 34 +
examples/chart_area.py | 119 +
examples/chart_bar.py | 119 +
examples/chart_clustered.py | 61 +
examples/chart_column.py | 119 +
examples/chart_combined.py | 107 +
examples/chart_data_table.py | 93 +
examples/chart_data_tools.py | 198 +
examples/chart_date_axis.py | 58 +
examples/chart_doughnut.py | 140 +
examples/chart_gradient.py | 63 +
examples/chart_line.py | 55 +
examples/chart_pareto.py | 78 +
examples/chart_pattern.py | 60 +
examples/chart_pie.py | 103 +
examples/chart_radar.py | 119 +
examples/chart_scatter.py | 186 +
examples/chart_secondary_axis.py | 52 +
examples/chart_stock.py | 62 +
examples/chart_styles.py | 42 +
examples/chartsheet.py | 63 +
examples/comments1.py | 18 +
examples/comments2.py | 261 +
examples/conditional_format.py | 241 +
examples/context_manager.py | 13 +
examples/data_validate.py | 207 +
examples/datetimes.py | 65 +
examples/defined_name.py | 33 +
examples/demo.py | 33 +
examples/diagonal_border.py | 28 +
examples/doc_properties.py | 27 +
examples/headers_footers.py | 128 +
examples/hello_world.py | 14 +
examples/hide_row_col.py | 29 +
examples/hide_sheet.py | 25 +
examples/http_server_py2.py | 53 +
examples/http_server_py3.py | 53 +
examples/hyperlink.py | 46 +
examples/images.py | 30 +
examples/images_bytesio.py | 50 +
examples/logo.png | Bin 0 -> 8560 bytes
examples/macros.py | 33 +
examples/merge1.py | 37 +
examples/merge_rich_string.py | 34 +
examples/outline.py | 190 +
examples/outline_collapsed.py | 204 +
examples/pandas_chart.py | 35 +
examples/pandas_chart_columns.py | 57 +
examples/pandas_chart_line.py | 55 +
examples/pandas_chart_stock.py | 58 +
examples/pandas_column_formats.py | 40 +
examples/pandas_conditional_format.py | 29 +
examples/pandas_datetime.py | 43 +
examples/pandas_multiple.py | 26 +
examples/pandas_positioning.py | 31 +
examples/pandas_simple.py | 22 +
examples/panes.py | 111 +
examples/python-200x80.png | Bin 0 -> 8758 bytes
examples/python.png | Bin 0 -> 12527 bytes
examples/rich_strings.py | 48 +
examples/right_to_left.py | 28 +
examples/sparklines1.py | 47 +
examples/sparklines2.py | 312 +
examples/tab_colors.py | 25 +
examples/tables.py | 340 +
examples/text_indent.py | 22 +
examples/textbox.py | 177 +
examples/tutorial1.py | 39 +
examples/tutorial2.py | 49 +
examples/tutorial3.py | 60 +
examples/unicode_polish_utf8.py | 38 +
examples/unicode_polish_utf8.txt | 34 +
examples/unicode_python2.py | 22 +
examples/unicode_python3.py | 22 +
examples/unicode_shift_jis.py | 38 +
examples/unicode_shift_jis.txt | 35 +
examples/vbaProject.bin | Bin 0 -> 15872 bytes
examples/vba_extract.py | 68 +
examples/worksheet_protection.py | 32 +
setup.cfg | 3 +
setup.py | 54 +
tox.ini | 8 +
xlsxwriter/__init__.py | 3 +
xlsxwriter/app.py | 196 +
xlsxwriter/chart.py | 3972 ++++++++
xlsxwriter/chart_area.py | 103 +
xlsxwriter/chart_bar.py | 175 +
xlsxwriter/chart_column.py | 134 +
xlsxwriter/chart_doughnut.py | 102 +
xlsxwriter/chart_line.py | 119 +
xlsxwriter/chart_pie.py | 227 +
xlsxwriter/chart_radar.py | 101 +
xlsxwriter/chart_scatter.py | 328 +
xlsxwriter/chart_stock.py | 130 +
xlsxwriter/chartsheet.py | 179 +
xlsxwriter/comments.py | 205 +
xlsxwriter/compat_collections.py | 196 +
xlsxwriter/compatibility.py | 54 +
xlsxwriter/contenttypes.py | 202 +
xlsxwriter/core.py | 192 +
xlsxwriter/custom.py | 140 +
xlsxwriter/drawing.py | 1091 +++
xlsxwriter/format.py | 991 ++
xlsxwriter/packager.py | 665 ++
xlsxwriter/relationships.py | 115 +
xlsxwriter/shape.py | 413 +
xlsxwriter/sharedstrings.py | 153 +
xlsxwriter/styles.py | 700 ++
xlsxwriter/table.py | 178 +
xlsxwriter/test/__init__.py | 0
xlsxwriter/test/app/__init__.py | 0
xlsxwriter/test/app/test_app01.py | 64 +
xlsxwriter/test/app/test_app02.py | 66 +
xlsxwriter/test/app/test_app03.py | 73 +
xlsxwriter/test/app/test_initialisation.py | 32 +
xlsxwriter/test/chart/__init__.py | 0
xlsxwriter/test/chart/test_initialisation.py | 32 +
xlsxwriter/test/chartsheet/__init__.py | 0
xlsxwriter/test/chartsheet/test_chartsheet01.py | 45 +
xlsxwriter/test/chartsheet/test_initialisation.py | 32 +
xlsxwriter/test/comments/__init__.py | 0
xlsxwriter/test/comments/test_comments01.py | 55 +
xlsxwriter/test/comments/test_initialisation.py | 32 +
xlsxwriter/test/comments/test_write_text_t.py | 72 +
xlsxwriter/test/comparison/__init__.py | 0
xlsxwriter/test/comparison/images/black_150.jpg | Bin 0 -> 566 bytes
xlsxwriter/test/comparison/images/black_150.png | Bin 0 -> 131 bytes
xlsxwriter/test/comparison/images/black_150e.png | Bin 0 -> 127 bytes
xlsxwriter/test/comparison/images/black_300.jpg | Bin 0 -> 566 bytes
xlsxwriter/test/comparison/images/black_300.png | Bin 0 -> 131 bytes
xlsxwriter/test/comparison/images/black_300e.png | Bin 0 -> 127 bytes
xlsxwriter/test/comparison/images/black_72.jpg | Bin 0 -> 566 bytes
xlsxwriter/test/comparison/images/black_72.png | Bin 0 -> 155 bytes
xlsxwriter/test/comparison/images/black_72e.png | Bin 0 -> 151 bytes
xlsxwriter/test/comparison/images/black_96.jpg | Bin 0 -> 566 bytes
xlsxwriter/test/comparison/images/black_96.png | Bin 0 -> 131 bytes
xlsxwriter/test/comparison/images/blue.jpg | Bin 0 -> 701 bytes
xlsxwriter/test/comparison/images/blue.png | Bin 0 -> 178 bytes
xlsxwriter/test/comparison/images/grey.jpg | Bin 0 -> 823 bytes
xlsxwriter/test/comparison/images/grey.png | Bin 0 -> 359 bytes
xlsxwriter/test/comparison/images/issue32.png | Bin 0 -> 24769 bytes
xlsxwriter/test/comparison/images/logo.png | Bin 0 -> 8560 bytes
xlsxwriter/test/comparison/images/mylogo.png | Bin 0 -> 5807 bytes
xlsxwriter/test/comparison/images/red.bmp | Bin 0 -> 3126 bytes
xlsxwriter/test/comparison/images/red.jpg | Bin 0 -> 701 bytes
xlsxwriter/test/comparison/images/red.png | Bin 0 -> 200 bytes
xlsxwriter/test/comparison/images/red_208.png | Bin 0 -> 439 bytes
xlsxwriter/test/comparison/images/red_64x20.png | Bin 0 -> 108 bytes
xlsxwriter/test/comparison/images/red_readonly.png | Bin 0 -> 200 bytes
xlsxwriter/test/comparison/images/train.jpg | Bin 0 -> 57105 bytes
xlsxwriter/test/comparison/images/yellow.jpg | Bin 0 -> 785 bytes
xlsxwriter/test/comparison/images/yellow.png | Bin 0 -> 316 bytes
xlsxwriter/test/comparison/images/zero_dpi.jpg | Bin 0 -> 634 bytes
xlsxwriter/test/comparison/test_array_formula01.py | 99 +
xlsxwriter/test/comparison/test_array_formula02.py | 51 +
xlsxwriter/test/comparison/test_autofilter00.py | 67 +
xlsxwriter/test/comparison/test_autofilter01.py | 72 +
xlsxwriter/test/comparison/test_autofilter02.py | 92 +
xlsxwriter/test/comparison/test_autofilter03.py | 93 +
xlsxwriter/test/comparison/test_autofilter04.py | 94 +
xlsxwriter/test/comparison/test_autofilter05.py | 96 +
xlsxwriter/test/comparison/test_autofilter06.py | 96 +
xlsxwriter/test/comparison/test_autofilter07.py | 92 +
xlsxwriter/test/comparison/test_button01.py | 41 +
xlsxwriter/test/comparison/test_button02.py | 43 +
xlsxwriter/test/comparison/test_button03.py | 42 +
xlsxwriter/test/comparison/test_button04.py | 43 +
xlsxwriter/test/comparison/test_button05.py | 44 +
xlsxwriter/test/comparison/test_button06.py | 44 +
xlsxwriter/test/comparison/test_button07.py | 83 +
xlsxwriter/test/comparison/test_button08.py | 46 +
xlsxwriter/test/comparison/test_button09.py | 46 +
xlsxwriter/test/comparison/test_button10.py | 50 +
xlsxwriter/test/comparison/test_button11.py | 50 +
xlsxwriter/test/comparison/test_button12.py | 49 +
xlsxwriter/test/comparison/test_chart_area01.py | 60 +
xlsxwriter/test/comparison/test_chart_area02.py | 60 +
xlsxwriter/test/comparison/test_chart_area03.py | 61 +
xlsxwriter/test/comparison/test_chart_area04.py | 56 +
xlsxwriter/test/comparison/test_chart_axis01.py | 62 +
xlsxwriter/test/comparison/test_chart_axis02.py | 62 +
xlsxwriter/test/comparison/test_chart_axis03.py | 79 +
xlsxwriter/test/comparison/test_chart_axis04.py | 68 +
xlsxwriter/test/comparison/test_chart_axis05.py | 62 +
xlsxwriter/test/comparison/test_chart_axis06.py | 59 +
xlsxwriter/test/comparison/test_chart_axis07.py | 68 +
xlsxwriter/test/comparison/test_chart_axis08.py | 67 +
xlsxwriter/test/comparison/test_chart_axis09.py | 61 +
xlsxwriter/test/comparison/test_chart_axis10.py | 68 +
xlsxwriter/test/comparison/test_chart_axis11.py | 61 +
xlsxwriter/test/comparison/test_chart_axis12.py | 61 +
xlsxwriter/test/comparison/test_chart_axis13.py | 68 +
xlsxwriter/test/comparison/test_chart_axis14.py | 78 +
xlsxwriter/test/comparison/test_chart_axis15.py | 61 +
xlsxwriter/test/comparison/test_chart_axis16.py | 81 +
xlsxwriter/test/comparison/test_chart_axis17.py | 61 +
xlsxwriter/test/comparison/test_chart_axis18.py | 59 +
xlsxwriter/test/comparison/test_chart_axis19.py | 62 +
xlsxwriter/test/comparison/test_chart_axis20.py | 62 +
xlsxwriter/test/comparison/test_chart_axis21.py | 67 +
xlsxwriter/test/comparison/test_chart_axis22.py | 61 +
xlsxwriter/test/comparison/test_chart_axis23.py | 62 +
xlsxwriter/test/comparison/test_chart_axis24.py | 62 +
xlsxwriter/test/comparison/test_chart_axis25.py | 63 +
xlsxwriter/test/comparison/test_chart_axis26.py | 63 +
xlsxwriter/test/comparison/test_chart_axis27.py | 63 +
xlsxwriter/test/comparison/test_chart_axis28.py | 63 +
xlsxwriter/test/comparison/test_chart_axis29.py | 63 +
xlsxwriter/test/comparison/test_chart_axis30.py | 61 +
xlsxwriter/test/comparison/test_chart_axis31.py | 61 +
xlsxwriter/test/comparison/test_chart_axis32.py | 61 +
xlsxwriter/test/comparison/test_chart_axis33.py | 62 +
xlsxwriter/test/comparison/test_chart_axis34.py | 61 +
xlsxwriter/test/comparison/test_chart_axis35.py | 61 +
xlsxwriter/test/comparison/test_chart_axis36.py | 61 +
xlsxwriter/test/comparison/test_chart_axis37.py | 62 +
xlsxwriter/test/comparison/test_chart_axis38.py | 62 +
xlsxwriter/test/comparison/test_chart_axis39.py | 68 +
xlsxwriter/test/comparison/test_chart_axis40.py | 61 +
xlsxwriter/test/comparison/test_chart_axis41.py | 62 +
xlsxwriter/test/comparison/test_chart_bar01.py | 62 +
xlsxwriter/test/comparison/test_chart_bar02.py | 66 +
xlsxwriter/test/comparison/test_chart_bar03.py | 78 +
xlsxwriter/test/comparison/test_chart_bar04.py | 83 +
xlsxwriter/test/comparison/test_chart_bar05.py | 58 +
xlsxwriter/test/comparison/test_chart_bar06.py | 62 +
xlsxwriter/test/comparison/test_chart_bar07.py | 62 +
xlsxwriter/test/comparison/test_chart_bar08.py | 64 +
xlsxwriter/test/comparison/test_chart_bar09.py | 58 +
xlsxwriter/test/comparison/test_chart_bar10.py | 58 +
xlsxwriter/test/comparison/test_chart_bar11.py | 76 +
xlsxwriter/test/comparison/test_chart_bar18.py | 66 +
xlsxwriter/test/comparison/test_chart_bar19.py | 63 +
xlsxwriter/test/comparison/test_chart_bar20.py | 61 +
xlsxwriter/test/comparison/test_chart_bar21.py | 69 +
xlsxwriter/test/comparison/test_chart_bar22.py | 82 +
xlsxwriter/test/comparison/test_chart_bar23.py | 76 +
xlsxwriter/test/comparison/test_chart_bar24.py | 53 +
xlsxwriter/test/comparison/test_chart_blank01.py | 61 +
xlsxwriter/test/comparison/test_chart_blank02.py | 61 +
xlsxwriter/test/comparison/test_chart_blank03.py | 61 +
xlsxwriter/test/comparison/test_chart_blank04.py | 61 +
xlsxwriter/test/comparison/test_chart_blank05.py | 63 +
xlsxwriter/test/comparison/test_chart_blank06.py | 61 +
.../test/comparison/test_chart_chartarea01.py | 69 +
.../test/comparison/test_chart_chartarea02.py | 69 +
.../test/comparison/test_chart_chartarea03.py | 75 +
.../test/comparison/test_chart_chartarea04.py | 85 +
.../test/comparison/test_chart_chartarea05.py | 65 +
.../test/comparison/test_chart_clustered01.py | 81 +
xlsxwriter/test/comparison/test_chart_column01.py | 92 +
xlsxwriter/test/comparison/test_chart_column02.py | 58 +
xlsxwriter/test/comparison/test_chart_column03.py | 58 +
xlsxwriter/test/comparison/test_chart_column04.py | 54 +
xlsxwriter/test/comparison/test_chart_column05.py | 59 +
xlsxwriter/test/comparison/test_chart_column06.py | 60 +
xlsxwriter/test/comparison/test_chart_column07.py | 60 +
xlsxwriter/test/comparison/test_chart_column08.py | 62 +
xlsxwriter/test/comparison/test_chart_column09.py | 58 +
xlsxwriter/test/comparison/test_chart_column10.py | 58 +
xlsxwriter/test/comparison/test_chart_column11.py | 61 +
xlsxwriter/test/comparison/test_chart_column12.py | 61 +
.../test/comparison/test_chart_combined01.py | 56 +
.../test/comparison/test_chart_combined02.py | 57 +
.../test/comparison/test_chart_combined03.py | 61 +
.../test/comparison/test_chart_combined04.py | 60 +
.../test/comparison/test_chart_combined05.py | 64 +
.../test/comparison/test_chart_combined06.py | 61 +
.../test/comparison/test_chart_combined07.py | 68 +
.../test/comparison/test_chart_combined08.py | 82 +
.../test/comparison/test_chart_combined09.py | 65 +
.../test/comparison/test_chart_crossing01.py | 62 +
.../test/comparison/test_chart_crossing02.py | 61 +
.../test/comparison/test_chart_crossing03.py | 67 +
.../test/comparison/test_chart_crossing04.py | 67 +
.../test/comparison/test_chart_data_labels01.py | 67 +
.../test/comparison/test_chart_data_labels02.py | 67 +
.../test/comparison/test_chart_data_labels03.py | 67 +
.../test/comparison/test_chart_data_labels04.py | 67 +
.../test/comparison/test_chart_data_labels05.py | 70 +
.../test/comparison/test_chart_data_labels06.py | 70 +
.../test/comparison/test_chart_data_labels07.py | 60 +
.../test/comparison/test_chart_data_labels08.py | 66 +
.../test/comparison/test_chart_data_labels09.py | 67 +
.../test/comparison/test_chart_data_labels10.py | 67 +
.../test/comparison/test_chart_data_labels11.py | 58 +
.../test/comparison/test_chart_data_labels12.py | 58 +
.../test/comparison/test_chart_data_labels13.py | 58 +
.../test/comparison/test_chart_data_labels14.py | 58 +
.../test/comparison/test_chart_data_labels15.py | 58 +
.../test/comparison/test_chart_data_labels16.py | 60 +
.../test/comparison/test_chart_data_labels17.py | 76 +
.../test/comparison/test_chart_data_labels18.py | 70 +
.../test/comparison/test_chart_data_labels19.py | 70 +
.../test/comparison/test_chart_data_labels20.py | 64 +
.../test/comparison/test_chart_data_labels21.py | 69 +
.../test/comparison/test_chart_data_labels22.py | 67 +
.../test/comparison/test_chart_data_labels23.py | 70 +
.../test/comparison/test_chart_data_labels24.py | 74 +
.../test/comparison/test_chart_data_labels25.py | 71 +
xlsxwriter/test/comparison/test_chart_date01.py | 75 +
xlsxwriter/test/comparison/test_chart_date02.py | 77 +
xlsxwriter/test/comparison/test_chart_date03.py | 77 +
xlsxwriter/test/comparison/test_chart_date04.py | 79 +
xlsxwriter/test/comparison/test_chart_date05.py | 75 +
.../test/comparison/test_chart_display_units01.py | 52 +
.../test/comparison/test_chart_display_units02.py | 54 +
.../test/comparison/test_chart_display_units03.py | 54 +
.../test/comparison/test_chart_display_units04.py | 54 +
.../test/comparison/test_chart_display_units05.py | 54 +
.../test/comparison/test_chart_display_units06.py | 54 +
.../test/comparison/test_chart_display_units07.py | 54 +
.../test/comparison/test_chart_display_units08.py | 54 +
.../test/comparison/test_chart_display_units09.py | 54 +
.../test/comparison/test_chart_display_units10.py | 54 +
.../test/comparison/test_chart_display_units11.py | 54 +
.../test/comparison/test_chart_display_units12.py | 61 +
.../test/comparison/test_chart_doughnut01.py | 55 +
.../test/comparison/test_chart_doughnut02.py | 56 +
.../test/comparison/test_chart_doughnut03.py | 56 +
.../test/comparison/test_chart_doughnut04.py | 56 +
.../test/comparison/test_chart_doughnut05.py | 56 +
.../test/comparison/test_chart_doughnut06.py | 57 +
.../test/comparison/test_chart_drop_lines01.py | 67 +
.../test/comparison/test_chart_drop_lines02.py | 69 +
.../test/comparison/test_chart_drop_lines03.py | 67 +
.../test/comparison/test_chart_drop_lines04.py | 77 +
.../test/comparison/test_chart_errorbars01.py | 66 +
.../test/comparison/test_chart_errorbars02.py | 77 +
.../test/comparison/test_chart_errorbars03.py | 69 +
.../test/comparison/test_chart_errorbars04.py | 66 +
.../test/comparison/test_chart_errorbars05.py | 66 +
.../test/comparison/test_chart_errorbars06.py | 66 +
.../test/comparison/test_chart_errorbars07.py | 78 +
.../test/comparison/test_chart_errorbars08.py | 70 +
.../test/comparison/test_chart_errorbars09.py | 70 +
.../test/comparison/test_chart_errorbars10.py | 72 +
.../test/comparison/test_chart_errorbars11.py | 75 +
xlsxwriter/test/comparison/test_chart_font01.py | 70 +
xlsxwriter/test/comparison/test_chart_font02.py | 73 +
xlsxwriter/test/comparison/test_chart_font03.py | 76 +
xlsxwriter/test/comparison/test_chart_font04.py | 74 +
xlsxwriter/test/comparison/test_chart_font05.py | 71 +
xlsxwriter/test/comparison/test_chart_font06.py | 101 +
xlsxwriter/test/comparison/test_chart_font07.py | 61 +
xlsxwriter/test/comparison/test_chart_font08.py | 61 +
xlsxwriter/test/comparison/test_chart_font09.py | 62 +
xlsxwriter/test/comparison/test_chart_format01.py | 65 +
xlsxwriter/test/comparison/test_chart_format02.py | 66 +
xlsxwriter/test/comparison/test_chart_format03.py | 67 +
xlsxwriter/test/comparison/test_chart_format04.py | 66 +
xlsxwriter/test/comparison/test_chart_format05.py | 66 +
xlsxwriter/test/comparison/test_chart_format06.py | 66 +
xlsxwriter/test/comparison/test_chart_format07.py | 71 +
xlsxwriter/test/comparison/test_chart_format08.py | 66 +
xlsxwriter/test/comparison/test_chart_format09.py | 70 +
xlsxwriter/test/comparison/test_chart_format10.py | 73 +
xlsxwriter/test/comparison/test_chart_format11.py | 76 +
xlsxwriter/test/comparison/test_chart_format12.py | 74 +
xlsxwriter/test/comparison/test_chart_format13.py | 66 +
xlsxwriter/test/comparison/test_chart_format14.py | 66 +
xlsxwriter/test/comparison/test_chart_format15.py | 68 +
xlsxwriter/test/comparison/test_chart_format16.py | 66 +
xlsxwriter/test/comparison/test_chart_format17.py | 59 +
xlsxwriter/test/comparison/test_chart_format18.py | 63 +
xlsxwriter/test/comparison/test_chart_format19.py | 65 +
xlsxwriter/test/comparison/test_chart_format20.py | 81 +
xlsxwriter/test/comparison/test_chart_format21.py | 67 +
xlsxwriter/test/comparison/test_chart_format22.py | 67 +
xlsxwriter/test/comparison/test_chart_format23.py | 67 +
xlsxwriter/test/comparison/test_chart_format24.py | 69 +
xlsxwriter/test/comparison/test_chart_format25.py | 66 +
xlsxwriter/test/comparison/test_chart_format26.py | 68 +
xlsxwriter/test/comparison/test_chart_format27.py | 77 +
xlsxwriter/test/comparison/test_chart_format28.py | 70 +
xlsxwriter/test/comparison/test_chart_format29.py | 78 +
xlsxwriter/test/comparison/test_chart_format30.py | 71 +
xlsxwriter/test/comparison/test_chart_format31.py | 79 +
xlsxwriter/test/comparison/test_chart_gap01.py | 67 +
xlsxwriter/test/comparison/test_chart_gap02.py | 67 +
xlsxwriter/test/comparison/test_chart_gap03.py | 67 +
xlsxwriter/test/comparison/test_chart_gap04.py | 59 +
xlsxwriter/test/comparison/test_chart_gap05.py | 61 +
.../test/comparison/test_chart_gradient01.py | 63 +
.../test/comparison/test_chart_gradient02.py | 63 +
.../test/comparison/test_chart_gradient03.py | 66 +
.../test/comparison/test_chart_gradient04.py | 65 +
.../test/comparison/test_chart_gradient05.py | 66 +
.../test/comparison/test_chart_gradient06.py | 65 +
.../test/comparison/test_chart_gradient07.py | 63 +
.../test/comparison/test_chart_gradient08.py | 63 +
.../test/comparison/test_chart_gradient09.py | 63 +
.../test/comparison/test_chart_gradient10.py | 62 +
.../test/comparison/test_chart_gradient11.py | 63 +
.../test/comparison/test_chart_gradient12.py | 65 +
.../test/comparison/test_chart_gradient13.py | 63 +
.../test/comparison/test_chart_gridlines01.py | 62 +
.../test/comparison/test_chart_gridlines02.py | 68 +
.../test/comparison/test_chart_gridlines03.py | 79 +
.../test/comparison/test_chart_gridlines04.py | 62 +
.../test/comparison/test_chart_gridlines05.py | 69 +
.../test/comparison/test_chart_gridlines06.py | 75 +
.../test/comparison/test_chart_gridlines07.py | 86 +
.../test/comparison/test_chart_gridlines08.py | 65 +
.../test/comparison/test_chart_gridlines09.py | 83 +
.../test/comparison/test_chart_high_low_lines01.py | 67 +
.../test/comparison/test_chart_high_low_lines02.py | 72 +
xlsxwriter/test/comparison/test_chart_layout01.py | 67 +
xlsxwriter/test/comparison/test_chart_layout02.py | 67 +
xlsxwriter/test/comparison/test_chart_layout03.py | 68 +
xlsxwriter/test/comparison/test_chart_layout04.py | 66 +
xlsxwriter/test/comparison/test_chart_layout05.py | 80 +
xlsxwriter/test/comparison/test_chart_layout06.py | 67 +
xlsxwriter/test/comparison/test_chart_layout07.py | 66 +
xlsxwriter/test/comparison/test_chart_layout08.py | 68 +
xlsxwriter/test/comparison/test_chart_legend01.py | 60 +
xlsxwriter/test/comparison/test_chart_legend02.py | 60 +
xlsxwriter/test/comparison/test_chart_line01.py | 59 +
xlsxwriter/test/comparison/test_chart_line02.py | 57 +
xlsxwriter/test/comparison/test_chart_line03.py | 59 +
xlsxwriter/test/comparison/test_chart_line04.py | 59 +
xlsxwriter/test/comparison/test_chart_name01.py | 61 +
xlsxwriter/test/comparison/test_chart_name02.py | 74 +
xlsxwriter/test/comparison/test_chart_name03.py | 74 +
xlsxwriter/test/comparison/test_chart_name04.py | 74 +
xlsxwriter/test/comparison/test_chart_order01.py | 79 +
xlsxwriter/test/comparison/test_chart_order02.py | 80 +
xlsxwriter/test/comparison/test_chart_pattern01.py | 73 +
xlsxwriter/test/comparison/test_chart_pattern02.py | 129 +
xlsxwriter/test/comparison/test_chart_pattern03.py | 129 +
xlsxwriter/test/comparison/test_chart_pattern04.py | 129 +
xlsxwriter/test/comparison/test_chart_pattern05.py | 129 +
xlsxwriter/test/comparison/test_chart_pattern06.py | 129 +
xlsxwriter/test/comparison/test_chart_pattern07.py | 129 +
xlsxwriter/test/comparison/test_chart_pattern08.py | 129 +
xlsxwriter/test/comparison/test_chart_pattern09.py | 81 +
xlsxwriter/test/comparison/test_chart_pattern10.py | 81 +
xlsxwriter/test/comparison/test_chart_pie01.py | 54 +
xlsxwriter/test/comparison/test_chart_pie02.py | 57 +
xlsxwriter/test/comparison/test_chart_pie03.py | 57 +
xlsxwriter/test/comparison/test_chart_pie04.py | 57 +
xlsxwriter/test/comparison/test_chart_pie05.py | 56 +
xlsxwriter/test/comparison/test_chart_points01.py | 51 +
xlsxwriter/test/comparison/test_chart_points02.py | 54 +
xlsxwriter/test/comparison/test_chart_points03.py | 57 +
xlsxwriter/test/comparison/test_chart_points04.py | 70 +
xlsxwriter/test/comparison/test_chart_points05.py | 68 +
xlsxwriter/test/comparison/test_chart_points06.py | 68 +
xlsxwriter/test/comparison/test_chart_radar01.py | 58 +
xlsxwriter/test/comparison/test_chart_radar02.py | 60 +
xlsxwriter/test/comparison/test_chart_radar03.py | 60 +
xlsxwriter/test/comparison/test_chart_scatter01.py | 61 +
xlsxwriter/test/comparison/test_chart_scatter02.py | 62 +
xlsxwriter/test/comparison/test_chart_scatter03.py | 61 +
xlsxwriter/test/comparison/test_chart_scatter04.py | 62 +
xlsxwriter/test/comparison/test_chart_scatter05.py | 63 +
xlsxwriter/test/comparison/test_chart_scatter06.py | 65 +
xlsxwriter/test/comparison/test_chart_scatter07.py | 66 +
xlsxwriter/test/comparison/test_chart_scatter09.py | 68 +
xlsxwriter/test/comparison/test_chart_scatter10.py | 68 +
xlsxwriter/test/comparison/test_chart_scatter11.py | 69 +
xlsxwriter/test/comparison/test_chart_scatter12.py | 65 +
xlsxwriter/test/comparison/test_chart_scatter13.py | 66 +
xlsxwriter/test/comparison/test_chart_scatter14.py | 68 +
xlsxwriter/test/comparison/test_chart_scatter15.py | 62 +
xlsxwriter/test/comparison/test_chart_size01.py | 60 +
xlsxwriter/test/comparison/test_chart_size02.py | 61 +
xlsxwriter/test/comparison/test_chart_size03.py | 60 +
xlsxwriter/test/comparison/test_chart_size04.py | 61 +
xlsxwriter/test/comparison/test_chart_size05.py | 59 +
xlsxwriter/test/comparison/test_chart_sparse01.py | 64 +
xlsxwriter/test/comparison/test_chart_stock01.py | 72 +
xlsxwriter/test/comparison/test_chart_stock02.py | 76 +
xlsxwriter/test/comparison/test_chart_str01.py | 61 +
xlsxwriter/test/comparison/test_chart_str02.py | 64 +
xlsxwriter/test/comparison/test_chart_table01.py | 61 +
xlsxwriter/test/comparison/test_chart_table02.py | 65 +
xlsxwriter/test/comparison/test_chart_table03.py | 67 +
xlsxwriter/test/comparison/test_chart_title01.py | 60 +
xlsxwriter/test/comparison/test_chart_title02.py | 59 +
.../test/comparison/test_chart_up_down_bars01.py | 67 +
.../test/comparison/test_chart_up_down_bars02.py | 78 +
.../test/comparison/test_chart_up_down_bars03.py | 77 +
xlsxwriter/test/comparison/test_chartsheet01.py | 62 +
xlsxwriter/test/comparison/test_chartsheet02.py | 64 +
xlsxwriter/test/comparison/test_chartsheet03.py | 62 +
xlsxwriter/test/comparison/test_chartsheet04.py | 62 +
xlsxwriter/test/comparison/test_chartsheet05.py | 62 +
xlsxwriter/test/comparison/test_chartsheet06.py | 62 +
xlsxwriter/test/comparison/test_chartsheet07.py | 65 +
xlsxwriter/test/comparison/test_chartsheet08.py | 69 +
xlsxwriter/test/comparison/test_chartsheet09.py | 67 +
xlsxwriter/test/comparison/test_comment01.py | 45 +
xlsxwriter/test/comparison/test_comment02.py | 46 +
xlsxwriter/test/comparison/test_comment03.py | 45 +
xlsxwriter/test/comparison/test_comment04.py | 50 +
xlsxwriter/test/comparison/test_comment05.py | 55 +
xlsxwriter/test/comparison/test_comment06.py | 47 +
xlsxwriter/test/comparison/test_comment07.py | 49 +
xlsxwriter/test/comparison/test_comment08.py | 49 +
xlsxwriter/test/comparison/test_comment09.py | 45 +
xlsxwriter/test/comparison/test_comment10.py | 44 +
xlsxwriter/test/comparison/test_comment12.py | 47 +
xlsxwriter/test/comparison/test_cond_format01.py | 58 +
xlsxwriter/test/comparison/test_cond_format02.py | 53 +
xlsxwriter/test/comparison/test_cond_format03.py | 62 +
xlsxwriter/test/comparison/test_cond_format04.py | 61 +
xlsxwriter/test/comparison/test_cond_format05.py | 53 +
xlsxwriter/test/comparison/test_cond_format06.py | 57 +
xlsxwriter/test/comparison/test_cond_format07.py | 73 +
xlsxwriter/test/comparison/test_cond_format08.py | 58 +
xlsxwriter/test/comparison/test_cond_format09.py | 58 +
xlsxwriter/test/comparison/test_cond_format10.py | 55 +
xlsxwriter/test/comparison/test_cond_format11.py | 57 +
xlsxwriter/test/comparison/test_cond_format12.py | 57 +
xlsxwriter/test/comparison/test_cond_format13.py | 62 +
.../test/comparison/test_data_validation01.py | 45 +
.../test/comparison/test_data_validation02.py | 47 +
.../test/comparison/test_data_validation03.py | 68 +
.../test/comparison/test_data_validation04.py | 72 +
.../test/comparison/test_data_validation05.py | 72 +
.../test/comparison/test_data_validation06.py | 72 +
.../test/comparison/test_data_validation07.py | 46 +
.../test/comparison/test_data_validation08.py | 46 +
xlsxwriter/test/comparison/test_date_1904_01.py | 50 +
xlsxwriter/test/comparison/test_date_1904_02.py | 50 +
xlsxwriter/test/comparison/test_date_examples01.py | 66 +
.../test/comparison/test_default_date_format01.py | 116 +
.../test/comparison/test_default_date_format02.py | 65 +
.../test/comparison/test_default_format01.py | 45 +
xlsxwriter/test/comparison/test_default_row01.py | 44 +
xlsxwriter/test/comparison/test_default_row02.py | 47 +
xlsxwriter/test/comparison/test_default_row03.py | 47 +
xlsxwriter/test/comparison/test_default_row04.py | 48 +
xlsxwriter/test/comparison/test_default_row05.py | 51 +
xlsxwriter/test/comparison/test_defined_name01.py | 58 +
xlsxwriter/test/comparison/test_defined_name02.py | 41 +
xlsxwriter/test/comparison/test_defined_name03.py | 41 +
xlsxwriter/test/comparison/test_defined_name04.py | 71 +
xlsxwriter/test/comparison/test_escapes01.py | 90 +
xlsxwriter/test/comparison/test_escapes02.py | 44 +
xlsxwriter/test/comparison/test_escapes03.py | 46 +
xlsxwriter/test/comparison/test_escapes04.py | 44 +
xlsxwriter/test/comparison/test_escapes05.py | 45 +
xlsxwriter/test/comparison/test_escapes06.py | 44 +
xlsxwriter/test/comparison/test_escapes07.py | 44 +
xlsxwriter/test/comparison/test_escapes08.py | 44 +
.../test/comparison/test_excel2003_style01.py | 38 +
.../test/comparison/test_excel2003_style02.py | 48 +
.../test/comparison/test_excel2003_style03.py | 51 +
.../test/comparison/test_excel2003_style04.py | 42 +
.../test/comparison/test_excel2003_style05.py | 45 +
.../test/comparison/test_excel2003_style06.py | 46 +
.../test/comparison/test_excel2003_style07.py | 46 +
.../test/comparison/test_excel2003_style08.py | 46 +
xlsxwriter/test/comparison/test_firstsheet01.py | 61 +
xlsxwriter/test/comparison/test_fit_to_pages01.py | 46 +
xlsxwriter/test/comparison/test_fit_to_pages02.py | 46 +
xlsxwriter/test/comparison/test_fit_to_pages03.py | 46 +
xlsxwriter/test/comparison/test_fit_to_pages04.py | 46 +
xlsxwriter/test/comparison/test_fit_to_pages05.py | 46 +
xlsxwriter/test/comparison/test_format01.py | 53 +
xlsxwriter/test/comparison/test_format11.py | 46 +
xlsxwriter/test/comparison/test_format12.py | 59 +
xlsxwriter/test/comparison/test_format13.py | 49 +
xlsxwriter/test/comparison/test_format14.py | 58 +
.../test/comparison/test_formula_results01.py | 54 +
xlsxwriter/test/comparison/test_header01.py | 41 +
xlsxwriter/test/comparison/test_header02.py | 41 +
xlsxwriter/test/comparison/test_header03.py | 42 +
xlsxwriter/test/comparison/test_header_image01.py | 96 +
xlsxwriter/test/comparison/test_header_image02.py | 44 +
xlsxwriter/test/comparison/test_header_image03.py | 94 +
xlsxwriter/test/comparison/test_header_image04.py | 94 +
xlsxwriter/test/comparison/test_header_image05.py | 46 +
xlsxwriter/test/comparison/test_header_image06.py | 48 +
xlsxwriter/test/comparison/test_header_image07.py | 45 +
xlsxwriter/test/comparison/test_header_image08.py | 48 +
xlsxwriter/test/comparison/test_header_image09.py | 50 +
xlsxwriter/test/comparison/test_header_image10.py | 50 +
xlsxwriter/test/comparison/test_header_image11.py | 43 +
xlsxwriter/test/comparison/test_header_image12.py | 43 +
xlsxwriter/test/comparison/test_header_image13.py | 46 +
xlsxwriter/test/comparison/test_header_image14.py | 46 +
xlsxwriter/test/comparison/test_hide01.py | 43 +
xlsxwriter/test/comparison/test_hyperlink01.py | 60 +
xlsxwriter/test/comparison/test_hyperlink02.py | 48 +
xlsxwriter/test/comparison/test_hyperlink03.py | 53 +
xlsxwriter/test/comparison/test_hyperlink04.py | 76 +
xlsxwriter/test/comparison/test_hyperlink05.py | 47 +
xlsxwriter/test/comparison/test_hyperlink06.py | 64 +
xlsxwriter/test/comparison/test_hyperlink07.py | 45 +
xlsxwriter/test/comparison/test_hyperlink08.py | 45 +
xlsxwriter/test/comparison/test_hyperlink09.py | 46 +
xlsxwriter/test/comparison/test_hyperlink10.py | 56 +
xlsxwriter/test/comparison/test_hyperlink11.py | 68 +
xlsxwriter/test/comparison/test_hyperlink12.py | 60 +
xlsxwriter/test/comparison/test_hyperlink13.py | 43 +
xlsxwriter/test/comparison/test_hyperlink14.py | 43 +
xlsxwriter/test/comparison/test_hyperlink15.py | 44 +
xlsxwriter/test/comparison/test_hyperlink16.py | 44 +
xlsxwriter/test/comparison/test_hyperlink17.py | 44 +
xlsxwriter/test/comparison/test_hyperlink18.py | 44 +
xlsxwriter/test/comparison/test_hyperlink19.py | 48 +
xlsxwriter/test/comparison/test_hyperlink20.py | 66 +
xlsxwriter/test/comparison/test_hyperlink21.py | 44 +
xlsxwriter/test/comparison/test_hyperlink22.py | 44 +
xlsxwriter/test/comparison/test_hyperlink23.py | 44 +
xlsxwriter/test/comparison/test_hyperlink24.py | 44 +
xlsxwriter/test/comparison/test_hyperlink25.py | 44 +
xlsxwriter/test/comparison/test_hyperlink26.py | 44 +
xlsxwriter/test/comparison/test_hyperlink27.py | 44 +
xlsxwriter/test/comparison/test_image01.py | 55 +
xlsxwriter/test/comparison/test_image02.py | 42 +
xlsxwriter/test/comparison/test_image03.py | 42 +
xlsxwriter/test/comparison/test_image04.py | 42 +
xlsxwriter/test/comparison/test_image05.py | 45 +
xlsxwriter/test/comparison/test_image07.py | 44 +
xlsxwriter/test/comparison/test_image08.py | 42 +
xlsxwriter/test/comparison/test_image09.py | 42 +
xlsxwriter/test/comparison/test_image10.py | 42 +
xlsxwriter/test/comparison/test_image11.py | 42 +
xlsxwriter/test/comparison/test_image12.py | 45 +
xlsxwriter/test/comparison/test_image13.py | 45 +
xlsxwriter/test/comparison/test_image14.py | 47 +
xlsxwriter/test/comparison/test_image15.py | 47 +
xlsxwriter/test/comparison/test_image16.py | 42 +
xlsxwriter/test/comparison/test_image17.py | 45 +
xlsxwriter/test/comparison/test_image18.py | 45 +
xlsxwriter/test/comparison/test_image19.py | 42 +
xlsxwriter/test/comparison/test_image20.py | 55 +
xlsxwriter/test/comparison/test_image21.py | 43 +
xlsxwriter/test/comparison/test_image22.py | 42 +
xlsxwriter/test/comparison/test_image23.py | 45 +
xlsxwriter/test/comparison/test_image24.py | 42 +
xlsxwriter/test/comparison/test_image25.py | 42 +
xlsxwriter/test/comparison/test_image26.py | 45 +
xlsxwriter/test/comparison/test_image27.py | 42 +
xlsxwriter/test/comparison/test_image28.py | 42 +
xlsxwriter/test/comparison/test_image29.py | 42 +
xlsxwriter/test/comparison/test_image30.py | 42 +
xlsxwriter/test/comparison/test_image31.py | 45 +
xlsxwriter/test/comparison/test_image32.py | 42 +
xlsxwriter/test/comparison/test_image33.py | 47 +
xlsxwriter/test/comparison/test_image34.py | 42 +
xlsxwriter/test/comparison/test_image35.py | 42 +
xlsxwriter/test/comparison/test_image_anchor01.py | 57 +
xlsxwriter/test/comparison/test_image_anchor02.py | 57 +
xlsxwriter/test/comparison/test_image_anchor03.py | 57 +
xlsxwriter/test/comparison/test_image_anchor04.py | 44 +
xlsxwriter/test/comparison/test_image_anchor05.py | 44 +
xlsxwriter/test/comparison/test_image_anchor06.py | 44 +
xlsxwriter/test/comparison/test_image_anchor07.py | 48 +
xlsxwriter/test/comparison/test_image_bytes01.py | 64 +
xlsxwriter/test/comparison/test_image_bytes02.py | 47 +
xlsxwriter/test/comparison/test_image_bytes03.py | 47 +
xlsxwriter/test/comparison/test_landscape01.py | 45 +
xlsxwriter/test/comparison/test_macro01.py | 98 +
xlsxwriter/test/comparison/test_merge_cells01.py | 47 +
xlsxwriter/test/comparison/test_merge_range01.py | 43 +
xlsxwriter/test/comparison/test_merge_range02.py | 43 +
xlsxwriter/test/comparison/test_merge_range03.py | 45 +
xlsxwriter/test/comparison/test_merge_range04.py | 43 +
xlsxwriter/test/comparison/test_merge_range05.py | 43 +
xlsxwriter/test/comparison/test_optimize01.py | 43 +
xlsxwriter/test/comparison/test_optimize02.py | 43 +
xlsxwriter/test/comparison/test_optimize03.py | 91 +
xlsxwriter/test/comparison/test_optimize04.py | 45 +
xlsxwriter/test/comparison/test_optimize05.py | 50 +
xlsxwriter/test/comparison/test_optimize06.py | 47 +
xlsxwriter/test/comparison/test_optimize07.py | 57 +
xlsxwriter/test/comparison/test_optimize08.py | 46 +
xlsxwriter/test/comparison/test_optimize09.py | 46 +
xlsxwriter/test/comparison/test_optimize10.py | 62 +
xlsxwriter/test/comparison/test_optimize11.py | 81 +
xlsxwriter/test/comparison/test_outline01.py | 87 +
xlsxwriter/test/comparison/test_outline02.py | 90 +
xlsxwriter/test/comparison/test_outline03.py | 66 +
xlsxwriter/test/comparison/test_outline04.py | 63 +
xlsxwriter/test/comparison/test_outline05.py | 90 +
xlsxwriter/test/comparison/test_outline06.py | 89 +
xlsxwriter/test/comparison/test_page_breaks01.py | 45 +
xlsxwriter/test/comparison/test_page_breaks02.py | 45 +
xlsxwriter/test/comparison/test_page_breaks03.py | 45 +
xlsxwriter/test/comparison/test_page_breaks04.py | 45 +
xlsxwriter/test/comparison/test_page_breaks05.py | 45 +
xlsxwriter/test/comparison/test_page_breaks06.py | 46 +
xlsxwriter/test/comparison/test_page_view01.py | 45 +
xlsxwriter/test/comparison/test_panes01.py | 79 +
xlsxwriter/test/comparison/test_print_across01.py | 45 +
xlsxwriter/test/comparison/test_print_area01.py | 60 +
xlsxwriter/test/comparison/test_print_area02.py | 45 +
xlsxwriter/test/comparison/test_print_area03.py | 45 +
xlsxwriter/test/comparison/test_print_area04.py | 45 +
xlsxwriter/test/comparison/test_print_area05.py | 45 +
xlsxwriter/test/comparison/test_print_area06.py | 45 +
xlsxwriter/test/comparison/test_print_area07.py | 45 +
xlsxwriter/test/comparison/test_print_options01.py | 45 +
xlsxwriter/test/comparison/test_print_options02.py | 45 +
xlsxwriter/test/comparison/test_print_options03.py | 45 +
xlsxwriter/test/comparison/test_print_options04.py | 45 +
xlsxwriter/test/comparison/test_print_options05.py | 48 +
xlsxwriter/test/comparison/test_print_options06.py | 46 +
xlsxwriter/test/comparison/test_properties01.py | 53 +
xlsxwriter/test/comparison/test_properties02.py | 41 +
xlsxwriter/test/comparison/test_properties03.py | 44 +
xlsxwriter/test/comparison/test_properties04.py | 87 +
xlsxwriter/test/comparison/test_properties05.py | 45 +
xlsxwriter/test/comparison/test_protect01.py | 46 +
xlsxwriter/test/comparison/test_protect02.py | 48 +
xlsxwriter/test/comparison/test_protect03.py | 48 +
xlsxwriter/test/comparison/test_quote_name01.py | 71 +
xlsxwriter/test/comparison/test_quote_name02.py | 62 +
xlsxwriter/test/comparison/test_quote_name03.py | 61 +
xlsxwriter/test/comparison/test_quote_name04.py | 58 +
xlsxwriter/test/comparison/test_remove_timezone.py | 95 +
xlsxwriter/test/comparison/test_repeat01.py | 45 +
xlsxwriter/test/comparison/test_repeat02.py | 45 +
xlsxwriter/test/comparison/test_repeat03.py | 46 +
xlsxwriter/test/comparison/test_repeat04.py | 45 +
xlsxwriter/test/comparison/test_repeat05.py | 52 +
xlsxwriter/test/comparison/test_rich_string01.py | 46 +
xlsxwriter/test/comparison/test_rich_string02.py | 46 +
xlsxwriter/test/comparison/test_rich_string03.py | 46 +
xlsxwriter/test/comparison/test_rich_string04.py | 46 +
xlsxwriter/test/comparison/test_rich_string05.py | 48 +
xlsxwriter/test/comparison/test_rich_string06.py | 45 +
xlsxwriter/test/comparison/test_rich_string07.py | 51 +
xlsxwriter/test/comparison/test_rich_string08.py | 47 +
xlsxwriter/test/comparison/test_rich_string09.py | 48 +
xlsxwriter/test/comparison/test_rich_string10.py | 47 +
xlsxwriter/test/comparison/test_rich_string11.py | 52 +
xlsxwriter/test/comparison/test_rich_string12.py | 51 +
xlsxwriter/test/comparison/test_selection01.py | 41 +
xlsxwriter/test/comparison/test_selection02.py | 51 +
xlsxwriter/test/comparison/test_set_column01.py | 72 +
xlsxwriter/test/comparison/test_set_column02.py | 72 +
xlsxwriter/test/comparison/test_set_column03.py | 56 +
xlsxwriter/test/comparison/test_set_column04.py | 60 +
xlsxwriter/test/comparison/test_set_column05.py | 69 +
xlsxwriter/test/comparison/test_set_column06.py | 66 +
xlsxwriter/test/comparison/test_set_column07.py | 63 +
xlsxwriter/test/comparison/test_set_column08.py | 60 +
xlsxwriter/test/comparison/test_set_column09.py | 46 +
.../test/comparison/test_set_print_scale01.py | 46 +
.../test/comparison/test_set_start_page01.py | 46 +
.../test/comparison/test_set_start_page02.py | 46 +
.../test/comparison/test_set_start_page03.py | 46 +
.../test/comparison/test_shared_strings01.py | 48 +
.../test/comparison/test_shared_strings02.py | 58 +
xlsxwriter/test/comparison/test_simple01.py | 132 +
xlsxwriter/test/comparison/test_simple02.py | 94 +
xlsxwriter/test/comparison/test_simple03.py | 84 +
xlsxwriter/test/comparison/test_simple04.py | 161 +
xlsxwriter/test/comparison/test_simple05.py | 88 +
xlsxwriter/test/comparison/test_simple06.py | 161 +
xlsxwriter/test/comparison/test_simple07.py | 62 +
xlsxwriter/test/comparison/test_simple08.py | 43 +
xlsxwriter/test/comparison/test_simple09.py | 47 +
xlsxwriter/test/comparison/test_tab_color01.py | 42 +
xlsxwriter/test/comparison/test_table01.py | 43 +
xlsxwriter/test/comparison/test_table02.py | 50 +
xlsxwriter/test/comparison/test_table03.py | 48 +
xlsxwriter/test/comparison/test_table04.py | 52 +
xlsxwriter/test/comparison/test_table05.py | 55 +
xlsxwriter/test/comparison/test_table06.py | 58 +
xlsxwriter/test/comparison/test_table07.py | 45 +
xlsxwriter/test/comparison/test_table08.py | 54 +
xlsxwriter/test/comparison/test_table09.py | 70 +
xlsxwriter/test/comparison/test_table10.py | 71 +
xlsxwriter/test/comparison/test_table11.py | 50 +
xlsxwriter/test/comparison/test_table12.py | 49 +
xlsxwriter/test/comparison/test_table13.py | 58 +
xlsxwriter/test/comparison/test_table14.py | 58 +
xlsxwriter/test/comparison/test_table15.py | 50 +
xlsxwriter/test/comparison/test_table16.py | 50 +
xlsxwriter/test/comparison/test_table17.py | 75 +
xlsxwriter/test/comparison/test_table18.py | 52 +
xlsxwriter/test/comparison/test_table19.py | 47 +
xlsxwriter/test/comparison/test_table20.py | 52 +
xlsxwriter/test/comparison/test_table21.py | 46 +
xlsxwriter/test/comparison/test_table22.py | 48 +
xlsxwriter/test/comparison/test_textbox01.py | 41 +
xlsxwriter/test/comparison/test_textbox02.py | 43 +
xlsxwriter/test/comparison/test_textbox03.py | 45 +
xlsxwriter/test/comparison/test_textbox04.py | 59 +
xlsxwriter/test/comparison/test_textbox05.py | 62 +
xlsxwriter/test/comparison/test_textbox06.py | 42 +
xlsxwriter/test/comparison/test_textbox07.py | 41 +
xlsxwriter/test/comparison/test_textbox08.py | 41 +
xlsxwriter/test/comparison/test_textbox09.py | 41 +
xlsxwriter/test/comparison/test_textbox10.py | 41 +
xlsxwriter/test/comparison/test_textbox11.py | 42 +
xlsxwriter/test/comparison/test_textbox12.py | 42 +
xlsxwriter/test/comparison/test_textbox13.py | 45 +
xlsxwriter/test/comparison/test_textbox14.py | 42 +
xlsxwriter/test/comparison/test_textbox15.py | 42 +
xlsxwriter/test/comparison/test_textbox16.py | 42 +
xlsxwriter/test/comparison/test_textbox17.py | 42 +
xlsxwriter/test/comparison/test_textbox18.py | 59 +
xlsxwriter/test/comparison/test_textbox19.py | 44 +
xlsxwriter/test/comparison/test_textbox20.py | 42 +
xlsxwriter/test/comparison/test_textbox21.py | 45 +
xlsxwriter/test/comparison/test_textbox22.py | 44 +
xlsxwriter/test/comparison/test_textbox23.py | 42 +
xlsxwriter/test/comparison/test_textbox24.py | 42 +
xlsxwriter/test/comparison/test_textbox25.py | 42 +
xlsxwriter/test/comparison/test_textbox26.py | 45 +
xlsxwriter/test/comparison/test_tutorial01.py | 62 +
xlsxwriter/test/comparison/test_tutorial02.py | 72 +
xlsxwriter/test/comparison/test_tutorial03.py | 137 +
xlsxwriter/test/comparison/test_types01.py | 54 +
xlsxwriter/test/comparison/test_types02.py | 54 +
xlsxwriter/test/comparison/test_types03.py | 69 +
xlsxwriter/test/comparison/test_types04.py | 70 +
xlsxwriter/test/comparison/test_types05.py | 69 +
xlsxwriter/test/comparison/test_types06.py | 42 +
xlsxwriter/test/comparison/test_types07.py | 71 +
.../test/comparison/test_unicode_polish_utf8.py | 62 +
.../test/comparison/test_unicode_shift_jis.py | 62 +
xlsxwriter/test/comparison/test_utf8_01.py | 42 +
xlsxwriter/test/comparison/test_utf8_03.py | 42 +
xlsxwriter/test/comparison/test_utf8_04.py | 42 +
xlsxwriter/test/comparison/test_utf8_05.py | 42 +
xlsxwriter/test/comparison/test_utf8_06.py | 47 +
xlsxwriter/test/comparison/test_utf8_07.py | 46 +
xlsxwriter/test/comparison/test_utf8_08.py | 48 +
xlsxwriter/test/comparison/test_utf8_09.py | 43 +
xlsxwriter/test/comparison/test_utf8_10.py | 63 +
xlsxwriter/test/comparison/test_utf8_11.py | 42 +
.../comparison/xlsx_files/array_formula01.xlsx | Bin 0 -> 7382 bytes
.../comparison/xlsx_files/array_formula02.xlsx | Bin 0 -> 7427 bytes
.../test/comparison/xlsx_files/autofilter00.xlsx | Bin 0 -> 8625 bytes
.../test/comparison/xlsx_files/autofilter01.xlsx | Bin 0 -> 8701 bytes
.../test/comparison/xlsx_files/autofilter02.xlsx | Bin 0 -> 8835 bytes
.../test/comparison/xlsx_files/autofilter03.xlsx | Bin 0 -> 8842 bytes
.../test/comparison/xlsx_files/autofilter04.xlsx | Bin 0 -> 8893 bytes
.../test/comparison/xlsx_files/autofilter05.xlsx | Bin 0 -> 8796 bytes
.../test/comparison/xlsx_files/autofilter06.xlsx | Bin 0 -> 8793 bytes
.../test/comparison/xlsx_files/autofilter07.xlsx | Bin 0 -> 8850 bytes
.../test/comparison/xlsx_files/autofilter_data.txt | 51 +
.../test/comparison/xlsx_files/button01.xlsx | Bin 0 -> 8162 bytes
.../test/comparison/xlsx_files/button02.xlsx | Bin 0 -> 8173 bytes
.../test/comparison/xlsx_files/button03.xlsx | Bin 0 -> 8217 bytes
.../test/comparison/xlsx_files/button04.xlsx | Bin 0 -> 9770 bytes
.../test/comparison/xlsx_files/button05.xlsx | Bin 0 -> 8164 bytes
.../test/comparison/xlsx_files/button07.xlsm | Bin 0 -> 12335 bytes
.../test/comparison/xlsx_files/button08.xlsx | Bin 0 -> 10097 bytes
.../test/comparison/xlsx_files/button09.xlsx | Bin 0 -> 10097 bytes
.../test/comparison/xlsx_files/button10.xlsx | Bin 0 -> 12010 bytes
.../test/comparison/xlsx_files/button11.xlsx | Bin 0 -> 12018 bytes
.../test/comparison/xlsx_files/button12.xlsx | Bin 0 -> 11163 bytes
.../test/comparison/xlsx_files/chart_area01.xlsx | Bin 0 -> 9267 bytes
.../test/comparison/xlsx_files/chart_area02.xlsx | Bin 0 -> 9275 bytes
.../test/comparison/xlsx_files/chart_area03.xlsx | Bin 0 -> 9286 bytes
.../test/comparison/xlsx_files/chart_area04.xlsx | Bin 0 -> 9274 bytes
.../test/comparison/xlsx_files/chart_axis01.xlsx | Bin 0 -> 9403 bytes
.../test/comparison/xlsx_files/chart_axis02.xlsx | Bin 0 -> 9377 bytes
.../test/comparison/xlsx_files/chart_axis03.xlsx | Bin 0 -> 9736 bytes
.../test/comparison/xlsx_files/chart_axis04.xlsx | Bin 0 -> 9411 bytes
.../test/comparison/xlsx_files/chart_axis05.xlsx | Bin 0 -> 9419 bytes
.../test/comparison/xlsx_files/chart_axis06.xlsx | Bin 0 -> 9054 bytes
.../test/comparison/xlsx_files/chart_axis07.xlsx | Bin 0 -> 9404 bytes
.../test/comparison/xlsx_files/chart_axis08.xlsx | Bin 0 -> 9304 bytes
.../test/comparison/xlsx_files/chart_axis09.xlsx | Bin 0 -> 9286 bytes
.../test/comparison/xlsx_files/chart_axis10.xlsx | Bin 0 -> 9297 bytes
.../test/comparison/xlsx_files/chart_axis11.xlsx | Bin 0 -> 9287 bytes
.../test/comparison/xlsx_files/chart_axis12.xlsx | Bin 0 -> 9286 bytes
.../test/comparison/xlsx_files/chart_axis13.xlsx | Bin 0 -> 9299 bytes
.../test/comparison/xlsx_files/chart_axis14.xlsx | Bin 0 -> 9635 bytes
.../test/comparison/xlsx_files/chart_axis15.xlsx | Bin 0 -> 9285 bytes
.../test/comparison/xlsx_files/chart_axis16.xlsx | Bin 0 -> 9639 bytes
.../test/comparison/xlsx_files/chart_axis17.xlsx | Bin 0 -> 9277 bytes
.../test/comparison/xlsx_files/chart_axis18.xlsx | Bin 0 -> 9301 bytes
.../test/comparison/xlsx_files/chart_axis19.xlsx | Bin 0 -> 9289 bytes
.../test/comparison/xlsx_files/chart_axis20.xlsx | Bin 0 -> 9290 bytes
.../test/comparison/xlsx_files/chart_axis21.xlsx | Bin 0 -> 9296 bytes
.../test/comparison/xlsx_files/chart_axis22.xlsx | Bin 0 -> 9299 bytes
.../test/comparison/xlsx_files/chart_axis23.xlsx | Bin 0 -> 9284 bytes
.../test/comparison/xlsx_files/chart_axis24.xlsx | Bin 0 -> 9301 bytes
.../test/comparison/xlsx_files/chart_axis25.xlsx | Bin 0 -> 9310 bytes
.../test/comparison/xlsx_files/chart_axis26.xlsx | Bin 0 -> 9350 bytes
.../test/comparison/xlsx_files/chart_axis27.xlsx | Bin 0 -> 9349 bytes
.../test/comparison/xlsx_files/chart_axis28.xlsx | Bin 0 -> 9351 bytes
.../test/comparison/xlsx_files/chart_axis29.xlsx | Bin 0 -> 9366 bytes
.../test/comparison/xlsx_files/chart_axis30.xlsx | Bin 0 -> 9281 bytes
.../test/comparison/xlsx_files/chart_axis31.xlsx | Bin 0 -> 9266 bytes
.../test/comparison/xlsx_files/chart_axis32.xlsx | Bin 0 -> 9251 bytes
.../test/comparison/xlsx_files/chart_axis33.xlsx | Bin 0 -> 9399 bytes
.../test/comparison/xlsx_files/chart_axis34.xlsx | Bin 0 -> 9286 bytes
.../test/comparison/xlsx_files/chart_axis35.xlsx | Bin 0 -> 9291 bytes
.../test/comparison/xlsx_files/chart_axis36.xlsx | Bin 0 -> 9293 bytes
.../test/comparison/xlsx_files/chart_axis37.xlsx | Bin 0 -> 9339 bytes
.../test/comparison/xlsx_files/chart_axis38.xlsx | Bin 0 -> 9336 bytes
.../test/comparison/xlsx_files/chart_axis39.xlsx | Bin 0 -> 9290 bytes
.../test/comparison/xlsx_files/chart_axis40.xlsx | Bin 0 -> 9301 bytes
.../test/comparison/xlsx_files/chart_axis41.xlsx | Bin 0 -> 9310 bytes
.../test/comparison/xlsx_files/chart_bar01.xlsx | Bin 0 -> 9278 bytes
.../test/comparison/xlsx_files/chart_bar02.xlsx | Bin 0 -> 10063 bytes
.../test/comparison/xlsx_files/chart_bar03.xlsx | Bin 0 -> 10279 bytes
.../test/comparison/xlsx_files/chart_bar04.xlsx | Bin 0 -> 12037 bytes
.../test/comparison/xlsx_files/chart_bar05.xlsx | Bin 0 -> 9269 bytes
.../test/comparison/xlsx_files/chart_bar06.xlsx | Bin 0 -> 9420 bytes
.../test/comparison/xlsx_files/chart_bar07.xlsx | Bin 0 -> 9459 bytes
.../test/comparison/xlsx_files/chart_bar08.xlsx | Bin 0 -> 9656 bytes
.../test/comparison/xlsx_files/chart_bar09.xlsx | Bin 0 -> 9281 bytes
.../test/comparison/xlsx_files/chart_bar10.xlsx | Bin 0 -> 9286 bytes
.../test/comparison/xlsx_files/chart_bar11.xlsx | Bin 0 -> 11560 bytes
.../test/comparison/xlsx_files/chart_bar18.xlsx | Bin 0 -> 10057 bytes
.../test/comparison/xlsx_files/chart_bar19.xlsx | Bin 0 -> 9459 bytes
.../test/comparison/xlsx_files/chart_bar20.xlsx | Bin 0 -> 9649 bytes
.../test/comparison/xlsx_files/chart_bar21.xlsx | Bin 0 -> 9278 bytes
.../test/comparison/xlsx_files/chart_bar22.xlsx | Bin 0 -> 9768 bytes
.../test/comparison/xlsx_files/chart_bar23.xlsx | Bin 0 -> 9768 bytes
.../test/comparison/xlsx_files/chart_bar24.xlsx | Bin 0 -> 9306 bytes
.../test/comparison/xlsx_files/chart_blank01.xlsx | Bin 0 -> 9285 bytes
.../test/comparison/xlsx_files/chart_blank02.xlsx | Bin 0 -> 9301 bytes
.../test/comparison/xlsx_files/chart_blank03.xlsx | Bin 0 -> 9300 bytes
.../test/comparison/xlsx_files/chart_blank04.xlsx | Bin 0 -> 9262 bytes
.../test/comparison/xlsx_files/chart_blank05.xlsx | Bin 0 -> 9630 bytes
.../test/comparison/xlsx_files/chart_blank06.xlsx | Bin 0 -> 9273 bytes
.../comparison/xlsx_files/chart_chartarea01.xlsx | Bin 0 -> 9372 bytes
.../comparison/xlsx_files/chart_chartarea03.xlsx | Bin 0 -> 9351 bytes
.../comparison/xlsx_files/chart_chartarea04.xlsx | Bin 0 -> 9679 bytes
.../comparison/xlsx_files/chart_chartarea05.xlsx | Bin 0 -> 9065 bytes
.../comparison/xlsx_files/chart_clustered01.xlsx | Bin 0 -> 9911 bytes
.../test/comparison/xlsx_files/chart_column01.xlsx | Bin 0 -> 9263 bytes
.../test/comparison/xlsx_files/chart_column02.xlsx | Bin 0 -> 9287 bytes
.../test/comparison/xlsx_files/chart_column03.xlsx | Bin 0 -> 9290 bytes
.../test/comparison/xlsx_files/chart_column04.xlsx | Bin 0 -> 9282 bytes
.../test/comparison/xlsx_files/chart_column05.xlsx | Bin 0 -> 9260 bytes
.../test/comparison/xlsx_files/chart_column06.xlsx | Bin 0 -> 9723 bytes
.../test/comparison/xlsx_files/chart_column07.xlsx | Bin 0 -> 9178 bytes
.../test/comparison/xlsx_files/chart_column08.xlsx | Bin 0 -> 9230 bytes
.../test/comparison/xlsx_files/chart_column09.xlsx | Bin 0 -> 9162 bytes
.../test/comparison/xlsx_files/chart_column10.xlsx | Bin 0 -> 9509 bytes
.../test/comparison/xlsx_files/chart_column11.xlsx | Bin 0 -> 9293 bytes
.../test/comparison/xlsx_files/chart_column12.xlsx | Bin 0 -> 9294 bytes
.../comparison/xlsx_files/chart_combined01.xlsx | Bin 0 -> 9198 bytes
.../comparison/xlsx_files/chart_combined02.xlsx | Bin 0 -> 7764 bytes
.../comparison/xlsx_files/chart_combined03.xlsx | Bin 0 -> 7801 bytes
.../comparison/xlsx_files/chart_combined04.xlsx | Bin 0 -> 7812 bytes
.../comparison/xlsx_files/chart_combined05.xlsx | Bin 0 -> 9320 bytes
.../comparison/xlsx_files/chart_combined06.xlsx | Bin 0 -> 9212 bytes
.../comparison/xlsx_files/chart_combined07.xlsx | Bin 0 -> 9360 bytes
.../comparison/xlsx_files/chart_combined08.xlsx | Bin 0 -> 9437 bytes
.../comparison/xlsx_files/chart_combined09.xlsx | Bin 0 -> 9392 bytes
.../comparison/xlsx_files/chart_crossing01.xlsx | Bin 0 -> 9269 bytes
.../comparison/xlsx_files/chart_crossing02.xlsx | Bin 0 -> 9263 bytes
.../comparison/xlsx_files/chart_crossing03.xlsx | Bin 0 -> 9275 bytes
.../comparison/xlsx_files/chart_crossing04.xlsx | Bin 0 -> 9284 bytes
.../comparison/xlsx_files/chart_data_labels01.xlsx | Bin 0 -> 9312 bytes
.../comparison/xlsx_files/chart_data_labels02.xlsx | Bin 0 -> 9303 bytes
.../comparison/xlsx_files/chart_data_labels03.xlsx | Bin 0 -> 9314 bytes
.../comparison/xlsx_files/chart_data_labels04.xlsx | Bin 0 -> 9315 bytes
.../comparison/xlsx_files/chart_data_labels05.xlsx | Bin 0 -> 9316 bytes
.../comparison/xlsx_files/chart_data_labels06.xlsx | Bin 0 -> 9314 bytes
.../comparison/xlsx_files/chart_data_labels07.xlsx | Bin 0 -> 9186 bytes
.../comparison/xlsx_files/chart_data_labels08.xlsx | Bin 0 -> 9301 bytes
.../comparison/xlsx_files/chart_data_labels09.xlsx | Bin 0 -> 9315 bytes
.../comparison/xlsx_files/chart_data_labels10.xlsx | Bin 0 -> 9313 bytes
.../comparison/xlsx_files/chart_data_labels11.xlsx | Bin 0 -> 9056 bytes
.../comparison/xlsx_files/chart_data_labels12.xlsx | Bin 0 -> 9063 bytes
.../comparison/xlsx_files/chart_data_labels13.xlsx | Bin 0 -> 9063 bytes
.../comparison/xlsx_files/chart_data_labels14.xlsx | Bin 0 -> 9061 bytes
.../comparison/xlsx_files/chart_data_labels15.xlsx | Bin 0 -> 9069 bytes
.../comparison/xlsx_files/chart_data_labels16.xlsx | Bin 0 -> 9210 bytes
.../comparison/xlsx_files/chart_data_labels17.xlsx | Bin 0 -> 9624 bytes
.../comparison/xlsx_files/chart_data_labels18.xlsx | Bin 0 -> 9329 bytes
.../comparison/xlsx_files/chart_data_labels19.xlsx | Bin 0 -> 9329 bytes
.../comparison/xlsx_files/chart_data_labels20.xlsx | Bin 0 -> 9297 bytes
.../comparison/xlsx_files/chart_data_labels21.xlsx | Bin 0 -> 9206 bytes
.../comparison/xlsx_files/chart_data_labels22.xlsx | Bin 0 -> 9334 bytes
.../comparison/xlsx_files/chart_data_labels23.xlsx | Bin 0 -> 9414 bytes
.../comparison/xlsx_files/chart_data_labels24.xlsx | Bin 0 -> 9425 bytes
.../comparison/xlsx_files/chart_data_labels25.xlsx | Bin 0 -> 9392 bytes
.../test/comparison/xlsx_files/chart_date01.xlsx | Bin 0 -> 9435 bytes
.../test/comparison/xlsx_files/chart_date02.xlsx | Bin 0 -> 9456 bytes
.../test/comparison/xlsx_files/chart_date03.xlsx | Bin 0 -> 9458 bytes
.../test/comparison/xlsx_files/chart_date04.xlsx | Bin 0 -> 9462 bytes
.../test/comparison/xlsx_files/chart_date05.xlsx | Bin 0 -> 9443 bytes
.../xlsx_files/chart_display_units01.xlsx | Bin 0 -> 9125 bytes
.../xlsx_files/chart_display_units02.xlsx | Bin 0 -> 9135 bytes
.../xlsx_files/chart_display_units03.xlsx | Bin 0 -> 9155 bytes
.../xlsx_files/chart_display_units04.xlsx | Bin 0 -> 9157 bytes
.../xlsx_files/chart_display_units05.xlsx | Bin 0 -> 9162 bytes
.../xlsx_files/chart_display_units06.xlsx | Bin 0 -> 9153 bytes
.../xlsx_files/chart_display_units07.xlsx | Bin 0 -> 9156 bytes
.../xlsx_files/chart_display_units08.xlsx | Bin 0 -> 9159 bytes
.../xlsx_files/chart_display_units09.xlsx | Bin 0 -> 9153 bytes
.../xlsx_files/chart_display_units10.xlsx | Bin 0 -> 9154 bytes
.../xlsx_files/chart_display_units11.xlsx | Bin 0 -> 9166 bytes
.../xlsx_files/chart_display_units12.xlsx | Bin 0 -> 9184 bytes
.../comparison/xlsx_files/chart_doughnut01.xlsx | Bin 0 -> 9005 bytes
.../comparison/xlsx_files/chart_doughnut02.xlsx | Bin 0 -> 9016 bytes
.../comparison/xlsx_files/chart_doughnut03.xlsx | Bin 0 -> 9020 bytes
.../comparison/xlsx_files/chart_doughnut04.xlsx | Bin 0 -> 9021 bytes
.../comparison/xlsx_files/chart_doughnut05.xlsx | Bin 0 -> 9023 bytes
.../comparison/xlsx_files/chart_doughnut06.xlsx | Bin 0 -> 9010 bytes
.../comparison/xlsx_files/chart_drop_lines01.xlsx | Bin 0 -> 9324 bytes
.../comparison/xlsx_files/chart_drop_lines02.xlsx | Bin 0 -> 9385 bytes
.../comparison/xlsx_files/chart_drop_lines03.xlsx | Bin 0 -> 9278 bytes
.../comparison/xlsx_files/chart_drop_lines04.xlsx | Bin 0 -> 9615 bytes
.../comparison/xlsx_files/chart_errorbars01.xlsx | Bin 0 -> 9357 bytes
.../comparison/xlsx_files/chart_errorbars02.xlsx | Bin 0 -> 9395 bytes
.../comparison/xlsx_files/chart_errorbars03.xlsx | Bin 0 -> 9411 bytes
.../comparison/xlsx_files/chart_errorbars04.xlsx | Bin 0 -> 9364 bytes
.../comparison/xlsx_files/chart_errorbars05.xlsx | Bin 0 -> 9316 bytes
.../comparison/xlsx_files/chart_errorbars06.xlsx | Bin 0 -> 9317 bytes
.../comparison/xlsx_files/chart_errorbars07.xlsx | Bin 0 -> 9653 bytes
.../comparison/xlsx_files/chart_errorbars08.xlsx | Bin 0 -> 9391 bytes
.../comparison/xlsx_files/chart_errorbars09.xlsx | Bin 0 -> 9402 bytes
.../comparison/xlsx_files/chart_errorbars10.xlsx | Bin 0 -> 9410 bytes
.../test/comparison/xlsx_files/chart_font01.xlsx | Bin 0 -> 9488 bytes
.../test/comparison/xlsx_files/chart_font02.xlsx | Bin 0 -> 9512 bytes
.../test/comparison/xlsx_files/chart_font03.xlsx | Bin 0 -> 9502 bytes
.../test/comparison/xlsx_files/chart_font04.xlsx | Bin 0 -> 9491 bytes
.../test/comparison/xlsx_files/chart_font05.xlsx | Bin 0 -> 9522 bytes
.../test/comparison/xlsx_files/chart_font06.xlsx | Bin 0 -> 9647 bytes
.../test/comparison/xlsx_files/chart_font07.xlsx | Bin 0 -> 9331 bytes
.../test/comparison/xlsx_files/chart_font08.xlsx | Bin 0 -> 9349 bytes
.../test/comparison/xlsx_files/chart_font09.xlsx | Bin 0 -> 9355 bytes
.../test/comparison/xlsx_files/chart_format01.xlsx | Bin 0 -> 9301 bytes
.../test/comparison/xlsx_files/chart_format02.xlsx | Bin 0 -> 9337 bytes
.../test/comparison/xlsx_files/chart_format03.xlsx | Bin 0 -> 9327 bytes
.../test/comparison/xlsx_files/chart_format04.xlsx | Bin 0 -> 9314 bytes
.../test/comparison/xlsx_files/chart_format05.xlsx | Bin 0 -> 9295 bytes
.../test/comparison/xlsx_files/chart_format06.xlsx | Bin 0 -> 9326 bytes
.../test/comparison/xlsx_files/chart_format07.xlsx | Bin 0 -> 9374 bytes
.../test/comparison/xlsx_files/chart_format08.xlsx | Bin 0 -> 9337 bytes
.../test/comparison/xlsx_files/chart_format09.xlsx | Bin 0 -> 9373 bytes
.../test/comparison/xlsx_files/chart_format10.xlsx | Bin 0 -> 9398 bytes
.../test/comparison/xlsx_files/chart_format11.xlsx | Bin 0 -> 9431 bytes
.../test/comparison/xlsx_files/chart_format12.xlsx | Bin 0 -> 9411 bytes
.../test/comparison/xlsx_files/chart_format13.xlsx | Bin 0 -> 9331 bytes
.../test/comparison/xlsx_files/chart_format14.xlsx | Bin 0 -> 9347 bytes
.../test/comparison/xlsx_files/chart_format15.xlsx | Bin 0 -> 9370 bytes
.../test/comparison/xlsx_files/chart_format16.xlsx | Bin 0 -> 9356 bytes
.../test/comparison/xlsx_files/chart_format17.xlsx | Bin 0 -> 9286 bytes
.../test/comparison/xlsx_files/chart_format18.xlsx | Bin 0 -> 9294 bytes
.../test/comparison/xlsx_files/chart_format19.xlsx | Bin 0 -> 9322 bytes
.../test/comparison/xlsx_files/chart_format20.xlsx | Bin 0 -> 10337 bytes
.../test/comparison/xlsx_files/chart_format21.xlsx | Bin 0 -> 9364 bytes
.../test/comparison/xlsx_files/chart_format22.xlsx | Bin 0 -> 9360 bytes
.../test/comparison/xlsx_files/chart_format23.xlsx | Bin 0 -> 9357 bytes
.../test/comparison/xlsx_files/chart_format24.xlsx | Bin 0 -> 9347 bytes
.../test/comparison/xlsx_files/chart_format25.xlsx | Bin 0 -> 9351 bytes
.../test/comparison/xlsx_files/chart_format26.xlsx | Bin 0 -> 9392 bytes
.../test/comparison/xlsx_files/chart_format27.xlsx | Bin 0 -> 9461 bytes
.../test/comparison/xlsx_files/chart_format28.xlsx | Bin 0 -> 9397 bytes
.../test/comparison/xlsx_files/chart_format29.xlsx | Bin 0 -> 9472 bytes
.../test/comparison/xlsx_files/chart_format30.xlsx | Bin 0 -> 9408 bytes
.../test/comparison/xlsx_files/chart_format31.xlsx | Bin 0 -> 9478 bytes
.../test/comparison/xlsx_files/chart_gap01.xlsx | Bin 0 -> 9318 bytes
.../test/comparison/xlsx_files/chart_gap02.xlsx | Bin 0 -> 9316 bytes
.../test/comparison/xlsx_files/chart_gap03.xlsx | Bin 0 -> 9316 bytes
.../test/comparison/xlsx_files/chart_gap04.xlsx | Bin 0 -> 9316 bytes
.../test/comparison/xlsx_files/chart_gap05.xlsx | Bin 0 -> 9312 bytes
.../comparison/xlsx_files/chart_gradient01.xlsx | Bin 0 -> 9365 bytes
.../comparison/xlsx_files/chart_gradient02.xlsx | Bin 0 -> 9383 bytes
.../comparison/xlsx_files/chart_gradient03.xlsx | Bin 0 -> 9384 bytes
.../comparison/xlsx_files/chart_gradient04.xlsx | Bin 0 -> 9436 bytes
.../comparison/xlsx_files/chart_gradient05.xlsx | Bin 0 -> 9434 bytes
.../comparison/xlsx_files/chart_gradient06.xlsx | Bin 0 -> 9431 bytes
.../comparison/xlsx_files/chart_gradient07.xlsx | Bin 0 -> 9405 bytes
.../comparison/xlsx_files/chart_gradient08.xlsx | Bin 0 -> 9387 bytes
.../comparison/xlsx_files/chart_gradient09.xlsx | Bin 0 -> 9388 bytes
.../comparison/xlsx_files/chart_gradient10.xlsx | Bin 0 -> 9373 bytes
.../comparison/xlsx_files/chart_gradient11.xlsx | Bin 0 -> 9393 bytes
.../comparison/xlsx_files/chart_gradient12.xlsx | Bin 0 -> 9374 bytes
.../comparison/xlsx_files/chart_gradient13.xlsx | Bin 0 -> 9389 bytes
.../comparison/xlsx_files/chart_gridlines01.xlsx | Bin 0 -> 9272 bytes
.../comparison/xlsx_files/chart_gridlines02.xlsx | Bin 0 -> 9285 bytes
.../comparison/xlsx_files/chart_gridlines03.xlsx | Bin 0 -> 9611 bytes
.../comparison/xlsx_files/chart_gridlines04.xlsx | Bin 0 -> 9290 bytes
.../comparison/xlsx_files/chart_gridlines05.xlsx | Bin 0 -> 9277 bytes
.../comparison/xlsx_files/chart_gridlines06.xlsx | Bin 0 -> 9303 bytes
.../comparison/xlsx_files/chart_gridlines07.xlsx | Bin 0 -> 9610 bytes
.../comparison/xlsx_files/chart_gridlines08.xlsx | Bin 0 -> 9312 bytes
.../comparison/xlsx_files/chart_gridlines09.xlsx | Bin 0 -> 9407 bytes
.../xlsx_files/chart_high_low_lines01.xlsx | Bin 0 -> 9326 bytes
.../xlsx_files/chart_high_low_lines02.xlsx | Bin 0 -> 9386 bytes
.../test/comparison/xlsx_files/chart_layout01.xlsx | Bin 0 -> 9384 bytes
.../test/comparison/xlsx_files/chart_layout02.xlsx | Bin 0 -> 9383 bytes
.../test/comparison/xlsx_files/chart_layout03.xlsx | Bin 0 -> 9390 bytes
.../test/comparison/xlsx_files/chart_layout04.xlsx | Bin 0 -> 9424 bytes
.../test/comparison/xlsx_files/chart_layout05.xlsx | Bin 0 -> 9506 bytes
.../test/comparison/xlsx_files/chart_layout06.xlsx | Bin 0 -> 9433 bytes
.../test/comparison/xlsx_files/chart_layout07.xlsx | Bin 0 -> 9443 bytes
.../test/comparison/xlsx_files/chart_layout08.xlsx | Bin 0 -> 9450 bytes
.../test/comparison/xlsx_files/chart_legend01.xlsx | Bin 0 -> 9246 bytes
.../test/comparison/xlsx_files/chart_line01.xlsx | Bin 0 -> 9280 bytes
.../test/comparison/xlsx_files/chart_line02.xlsx | Bin 0 -> 9295 bytes
.../test/comparison/xlsx_files/chart_line03.xlsx | Bin 0 -> 9287 bytes
.../test/comparison/xlsx_files/chart_line04.xlsx | Bin 0 -> 9305 bytes
.../test/comparison/xlsx_files/chart_name01.xlsx | Bin 0 -> 9293 bytes
.../test/comparison/xlsx_files/chart_name02.xlsx | Bin 0 -> 10281 bytes
.../test/comparison/xlsx_files/chart_name03.xlsx | Bin 0 -> 10281 bytes
.../test/comparison/xlsx_files/chart_order01.xlsx | Bin 0 -> 15149 bytes
.../comparison/xlsx_files/chart_pattern01.xlsx | Bin 0 -> 9242 bytes
.../comparison/xlsx_files/chart_pattern02.xlsx | Bin 0 -> 9407 bytes
.../comparison/xlsx_files/chart_pattern03.xlsx | Bin 0 -> 9423 bytes
.../comparison/xlsx_files/chart_pattern04.xlsx | Bin 0 -> 9433 bytes
.../comparison/xlsx_files/chart_pattern05.xlsx | Bin 0 -> 9436 bytes
.../comparison/xlsx_files/chart_pattern06.xlsx | Bin 0 -> 9437 bytes
.../comparison/xlsx_files/chart_pattern07.xlsx | Bin 0 -> 9433 bytes
.../comparison/xlsx_files/chart_pattern08.xlsx | Bin 0 -> 9435 bytes
.../comparison/xlsx_files/chart_pattern09.xlsx | Bin 0 -> 9322 bytes
.../comparison/xlsx_files/chart_pattern10.xlsx | Bin 0 -> 9325 bytes
.../test/comparison/xlsx_files/chart_pie01.xlsx | Bin 0 -> 8986 bytes
.../test/comparison/xlsx_files/chart_pie02.xlsx | Bin 0 -> 9015 bytes
.../test/comparison/xlsx_files/chart_pie03.xlsx | Bin 0 -> 9027 bytes
.../test/comparison/xlsx_files/chart_pie04.xlsx | Bin 0 -> 9014 bytes
.../test/comparison/xlsx_files/chart_pie05.xlsx | Bin 0 -> 9012 bytes
.../test/comparison/xlsx_files/chart_points01.xlsx | Bin 0 -> 8998 bytes
.../test/comparison/xlsx_files/chart_points02.xlsx | Bin 0 -> 9054 bytes
.../test/comparison/xlsx_files/chart_points03.xlsx | Bin 0 -> 9022 bytes
.../test/comparison/xlsx_files/chart_points04.xlsx | Bin 0 -> 9356 bytes
.../test/comparison/xlsx_files/chart_points05.xlsx | Bin 0 -> 9326 bytes
.../test/comparison/xlsx_files/chart_points06.xlsx | Bin 0 -> 9321 bytes
.../test/comparison/xlsx_files/chart_radar01.xlsx | Bin 0 -> 9291 bytes
.../test/comparison/xlsx_files/chart_radar02.xlsx | Bin 0 -> 9271 bytes
.../test/comparison/xlsx_files/chart_radar03.xlsx | Bin 0 -> 9271 bytes
.../comparison/xlsx_files/chart_scatter01.xlsx | Bin 0 -> 9278 bytes
.../comparison/xlsx_files/chart_scatter02.xlsx | Bin 0 -> 9246 bytes
.../comparison/xlsx_files/chart_scatter03.xlsx | Bin 0 -> 9269 bytes
.../comparison/xlsx_files/chart_scatter04.xlsx | Bin 0 -> 9257 bytes
.../comparison/xlsx_files/chart_scatter05.xlsx | Bin 0 -> 9278 bytes
.../comparison/xlsx_files/chart_scatter06.xlsx | Bin 0 -> 9312 bytes
.../comparison/xlsx_files/chart_scatter07.xlsx | Bin 0 -> 9400 bytes
.../comparison/xlsx_files/chart_scatter09.xlsx | Bin 0 -> 9269 bytes
.../comparison/xlsx_files/chart_scatter10.xlsx | Bin 0 -> 9296 bytes
.../comparison/xlsx_files/chart_scatter11.xlsx | Bin 0 -> 9275 bytes
.../comparison/xlsx_files/chart_scatter12.xlsx | Bin 0 -> 9262 bytes
.../comparison/xlsx_files/chart_scatter14.xlsx | Bin 0 -> 9275 bytes
.../comparison/xlsx_files/chart_scatter15.xlsx | Bin 0 -> 9584 bytes
.../test/comparison/xlsx_files/chart_size01.xlsx | Bin 0 -> 9258 bytes
.../test/comparison/xlsx_files/chart_size04.xlsx | Bin 0 -> 9276 bytes
.../test/comparison/xlsx_files/chart_sparse01.xlsx | Bin 0 -> 9289 bytes
.../test/comparison/xlsx_files/chart_stock01.xlsx | Bin 0 -> 9586 bytes
.../test/comparison/xlsx_files/chart_stock02.xlsx | Bin 0 -> 9723 bytes
.../test/comparison/xlsx_files/chart_str01.xlsx | Bin 0 -> 9601 bytes
.../test/comparison/xlsx_files/chart_str02.xlsx | Bin 0 -> 9608 bytes
.../test/comparison/xlsx_files/chart_table01.xlsx | Bin 0 -> 9304 bytes
.../test/comparison/xlsx_files/chart_table02.xlsx | Bin 0 -> 9301 bytes
.../test/comparison/xlsx_files/chart_table03.xlsx | Bin 0 -> 9399 bytes
.../test/comparison/xlsx_files/chart_title01.xlsx | Bin 0 -> 9197 bytes
.../test/comparison/xlsx_files/chart_title02.xlsx | Bin 0 -> 9245 bytes
.../xlsx_files/chart_up_down_bars01.xlsx | Bin 0 -> 9349 bytes
.../xlsx_files/chart_up_down_bars02.xlsx | Bin 0 -> 9435 bytes
.../xlsx_files/chart_up_down_bars03.xlsx | Bin 0 -> 9643 bytes
.../test/comparison/xlsx_files/chartsheet01.xlsx | Bin 0 -> 9606 bytes
.../test/comparison/xlsx_files/chartsheet02.xlsx | Bin 0 -> 10067 bytes
.../test/comparison/xlsx_files/chartsheet03.xlsx | Bin 0 -> 9603 bytes
.../test/comparison/xlsx_files/chartsheet04.xlsx | Bin 0 -> 9626 bytes
.../test/comparison/xlsx_files/chartsheet05.xlsx | Bin 0 -> 9609 bytes
.../test/comparison/xlsx_files/chartsheet06.xlsx | Bin 0 -> 9619 bytes
.../test/comparison/xlsx_files/chartsheet07.xlsx | Bin 0 -> 9979 bytes
.../test/comparison/xlsx_files/chartsheet08.xlsx | Bin 0 -> 10056 bytes
.../test/comparison/xlsx_files/chartsheet09.xlsx | Bin 0 -> 9650 bytes
.../test/comparison/xlsx_files/comment01.xlsx | Bin 0 -> 8828 bytes
.../test/comparison/xlsx_files/comment02.xlsx | Bin 0 -> 8916 bytes
.../test/comparison/xlsx_files/comment03.xlsx | Bin 0 -> 8955 bytes
.../test/comparison/xlsx_files/comment04.xlsx | Bin 0 -> 11207 bytes
.../test/comparison/xlsx_files/comment05.xlsx | Bin 0 -> 72801 bytes
.../test/comparison/xlsx_files/comment06.xlsx | Bin 0 -> 8693 bytes
.../test/comparison/xlsx_files/comment07.xlsx | Bin 0 -> 8663 bytes
.../test/comparison/xlsx_files/comment08.xlsx | Bin 0 -> 8693 bytes
.../test/comparison/xlsx_files/comment09.xlsx | Bin 0 -> 8602 bytes
.../test/comparison/xlsx_files/comment10.xlsx | Bin 0 -> 7379 bytes
.../test/comparison/xlsx_files/comment12.xlsx | Bin 0 -> 8877 bytes
.../test/comparison/xlsx_files/cond_format01.xlsx | Bin 0 -> 7184 bytes
.../test/comparison/xlsx_files/cond_format02.xlsx | Bin 0 -> 7116 bytes
.../test/comparison/xlsx_files/cond_format03.xlsx | Bin 0 -> 7170 bytes
.../test/comparison/xlsx_files/cond_format04.xlsx | Bin 0 -> 7190 bytes
.../test/comparison/xlsx_files/cond_format05.xlsx | Bin 0 -> 7183 bytes
.../test/comparison/xlsx_files/cond_format06.xlsx | Bin 0 -> 7166 bytes
.../test/comparison/xlsx_files/cond_format07.xlsx | Bin 0 -> 7630 bytes
.../test/comparison/xlsx_files/cond_format08.xlsx | Bin 0 -> 7189 bytes
.../test/comparison/xlsx_files/cond_format10.xlsx | Bin 0 -> 7498 bytes
.../test/comparison/xlsx_files/cond_format11.xlsx | Bin 0 -> 7521 bytes
.../test/comparison/xlsx_files/cond_format12.xlsx | Bin 0 -> 7517 bytes
.../test/comparison/xlsx_files/cond_format13.xlsx | Bin 0 -> 7221 bytes
.../comparison/xlsx_files/data_validation01.xlsx | Bin 0 -> 7064 bytes
.../comparison/xlsx_files/data_validation02.xlsx | Bin 0 -> 7096 bytes
.../comparison/xlsx_files/data_validation03.xlsx | Bin 0 -> 7216 bytes
.../comparison/xlsx_files/data_validation07.xlsx | Bin 0 -> 7075 bytes
.../comparison/xlsx_files/data_validation08.xlsx | Bin 0 -> 7074 bytes
.../test/comparison/xlsx_files/date_1904_01.xlsx | Bin 0 -> 7136 bytes
.../test/comparison/xlsx_files/date_1904_02.xlsx | Bin 0 -> 7143 bytes
.../comparison/xlsx_files/date_examples01.xlsx | Bin 0 -> 7275 bytes
.../xlsx_files/default_date_format01.xlsx | Bin 0 -> 7108 bytes
.../xlsx_files/default_date_format02.xlsx | Bin 0 -> 7109 bytes
.../comparison/xlsx_files/default_format01.xlsx | Bin 0 -> 6975 bytes
.../test/comparison/xlsx_files/default_row01.xlsx | Bin 0 -> 7353 bytes
.../test/comparison/xlsx_files/default_row02.xlsx | Bin 0 -> 7379 bytes
.../test/comparison/xlsx_files/default_row03.xlsx | Bin 0 -> 7400 bytes
.../test/comparison/xlsx_files/default_row04.xlsx | Bin 0 -> 8871 bytes
.../test/comparison/xlsx_files/default_row05.xlsx | Bin 0 -> 7453 bytes
.../test/comparison/xlsx_files/defined_name01.xlsx | Bin 0 -> 9203 bytes
.../test/comparison/xlsx_files/defined_name02.xlsx | Bin 0 -> 7042 bytes
.../test/comparison/xlsx_files/defined_name03.xlsx | Bin 0 -> 7037 bytes
.../test/comparison/xlsx_files/defined_name04.xlsx | Bin 0 -> 7136 bytes
.../test/comparison/xlsx_files/escapes01.xlsx | Bin 0 -> 7772 bytes
.../test/comparison/xlsx_files/escapes02.xlsx | Bin 0 -> 8853 bytes
.../test/comparison/xlsx_files/escapes03.xlsx | Bin 0 -> 7497 bytes
.../test/comparison/xlsx_files/escapes04.xlsx | Bin 0 -> 7728 bytes
.../test/comparison/xlsx_files/escapes05.xlsx | Bin 0 -> 7824 bytes
.../test/comparison/xlsx_files/escapes06.xlsx | Bin 0 -> 7122 bytes
.../test/comparison/xlsx_files/escapes07.xlsx | Bin 0 -> 7912 bytes
.../test/comparison/xlsx_files/escapes08.xlsx | Bin 0 -> 7707 bytes
.../comparison/xlsx_files/excel2003_style01.xlsx | Bin 0 -> 6977 bytes
.../comparison/xlsx_files/excel2003_style02.xlsx | Bin 0 -> 8077 bytes
.../comparison/xlsx_files/excel2003_style03.xlsx | Bin 0 -> 8113 bytes
.../comparison/xlsx_files/excel2003_style04.xlsx | Bin 0 -> 7333 bytes
.../comparison/xlsx_files/excel2003_style05.xlsx | Bin 0 -> 9158 bytes
.../comparison/xlsx_files/excel2003_style06.xlsx | Bin 0 -> 9166 bytes
.../comparison/xlsx_files/excel2003_style07.xlsx | Bin 0 -> 9249 bytes
.../comparison/xlsx_files/excel2003_style08.xlsx | Bin 0 -> 7399 bytes
.../test/comparison/xlsx_files/firstsheet01.xlsx | Bin 0 -> 15093 bytes
.../test/comparison/xlsx_files/fit_to_pages01.xlsx | Bin 0 -> 8062 bytes
.../test/comparison/xlsx_files/fit_to_pages02.xlsx | Bin 0 -> 8072 bytes
.../test/comparison/xlsx_files/fit_to_pages03.xlsx | Bin 0 -> 8068 bytes
.../test/comparison/xlsx_files/fit_to_pages04.xlsx | Bin 0 -> 8079 bytes
.../test/comparison/xlsx_files/fit_to_pages05.xlsx | Bin 0 -> 8070 bytes
.../test/comparison/xlsx_files/format01.xlsx | Bin 0 -> 8313 bytes
.../test/comparison/xlsx_files/format11.xlsx | Bin 0 -> 7361 bytes
.../test/comparison/xlsx_files/format12.xlsx | Bin 0 -> 7429 bytes
.../test/comparison/xlsx_files/format13.xlsx | Bin 0 -> 7375 bytes
.../test/comparison/xlsx_files/format14.xlsx | Bin 0 -> 7362 bytes
.../comparison/xlsx_files/formula_results01.xlsx | Bin 0 -> 7535 bytes
.../test/comparison/xlsx_files/header01.xlsx | Bin 0 -> 7100 bytes
.../test/comparison/xlsx_files/header02.xlsx | Bin 0 -> 7101 bytes
.../test/comparison/xlsx_files/header03.xlsx | Bin 0 -> 7107 bytes
.../test/comparison/xlsx_files/header_image01.xlsx | Bin 0 -> 9318 bytes
.../test/comparison/xlsx_files/header_image02.xlsx | Bin 0 -> 10191 bytes
.../test/comparison/xlsx_files/header_image03.xlsx | Bin 0 -> 11128 bytes
.../test/comparison/xlsx_files/header_image04.xlsx | Bin 0 -> 11128 bytes
.../test/comparison/xlsx_files/header_image05.xlsx | Bin 0 -> 10190 bytes
.../test/comparison/xlsx_files/header_image06.xlsx | Bin 0 -> 12071 bytes
.../test/comparison/xlsx_files/header_image07.xlsx | Bin 0 -> 11126 bytes
.../test/comparison/xlsx_files/header_image08.xlsx | Bin 0 -> 10817 bytes
.../test/comparison/xlsx_files/header_image09.xlsx | Bin 0 -> 11643 bytes
.../test/comparison/xlsx_files/header_image10.xlsx | Bin 0 -> 11643 bytes
.../test/comparison/xlsx_files/header_image11.xlsx | Bin 0 -> 9187 bytes
.../test/comparison/xlsx_files/header_image12.xlsx | Bin 0 -> 8743 bytes
.../test/comparison/xlsx_files/header_image13.xlsx | Bin 0 -> 10648 bytes
.../test/comparison/xlsx_files/header_image14.xlsx | Bin 0 -> 9339 bytes
xlsxwriter/test/comparison/xlsx_files/hide01.xlsx | Bin 0 -> 7843 bytes
.../test/comparison/xlsx_files/hyperlink01.xlsx | Bin 0 -> 7700 bytes
.../test/comparison/xlsx_files/hyperlink02.xlsx | Bin 0 -> 7824 bytes
.../test/comparison/xlsx_files/hyperlink03.xlsx | Bin 0 -> 8740 bytes
.../test/comparison/xlsx_files/hyperlink04.xlsx | Bin 0 -> 8434 bytes
.../test/comparison/xlsx_files/hyperlink05.xlsx | Bin 0 -> 7819 bytes
.../test/comparison/xlsx_files/hyperlink06.xlsx | Bin 0 -> 7813 bytes
.../test/comparison/xlsx_files/hyperlink07.xlsx | Bin 0 -> 7753 bytes
.../test/comparison/xlsx_files/hyperlink08.xlsx | Bin 0 -> 7753 bytes
.../test/comparison/xlsx_files/hyperlink09.xlsx | Bin 0 -> 7819 bytes
.../test/comparison/xlsx_files/hyperlink10.xlsx | Bin 0 -> 7753 bytes
.../test/comparison/xlsx_files/hyperlink11.xlsx | Bin 0 -> 7751 bytes
.../test/comparison/xlsx_files/hyperlink12.xlsx | Bin 0 -> 7809 bytes
.../test/comparison/xlsx_files/hyperlink13.xlsx | Bin 0 -> 7794 bytes
.../test/comparison/xlsx_files/hyperlink14.xlsx | Bin 0 -> 7787 bytes
.../test/comparison/xlsx_files/hyperlink15.xlsx | Bin 0 -> 7706 bytes
.../test/comparison/xlsx_files/hyperlink16.xlsx | Bin 0 -> 7709 bytes
.../test/comparison/xlsx_files/hyperlink17.xlsx | Bin 0 -> 7706 bytes
.../test/comparison/xlsx_files/hyperlink18.xlsx | Bin 0 -> 7796 bytes
.../test/comparison/xlsx_files/hyperlink19.xlsx | Bin 0 -> 7689 bytes
.../test/comparison/xlsx_files/hyperlink20.xlsx | Bin 0 -> 7836 bytes
.../test/comparison/xlsx_files/hyperlink21.xlsx | Bin 0 -> 7707 bytes
.../test/comparison/xlsx_files/hyperlink22.xlsx | Bin 0 -> 7732 bytes
.../test/comparison/xlsx_files/hyperlink23.xlsx | Bin 0 -> 7739 bytes
.../test/comparison/xlsx_files/hyperlink24.xlsx | Bin 0 -> 7860 bytes
.../test/comparison/xlsx_files/hyperlink25.xlsx | Bin 0 -> 7719 bytes
.../test/comparison/xlsx_files/hyperlink26.xlsx | Bin 0 -> 7726 bytes
.../test/comparison/xlsx_files/hyperlink27.xlsx | Bin 0 -> 7777 bytes
xlsxwriter/test/comparison/xlsx_files/image01.xlsx | Bin 0 -> 8607 bytes
xlsxwriter/test/comparison/xlsx_files/image02.xlsx | Bin 0 -> 8741 bytes
xlsxwriter/test/comparison/xlsx_files/image03.xlsx | Bin 0 -> 9117 bytes
xlsxwriter/test/comparison/xlsx_files/image04.xlsx | Bin 0 -> 7046 bytes
xlsxwriter/test/comparison/xlsx_files/image05.xlsx | Bin 0 -> 11075 bytes
xlsxwriter/test/comparison/xlsx_files/image07.xlsx | Bin 0 -> 10786 bytes
xlsxwriter/test/comparison/xlsx_files/image08.xlsx | Bin 0 -> 8772 bytes
xlsxwriter/test/comparison/xlsx_files/image09.xlsx | Bin 0 -> 8522 bytes
xlsxwriter/test/comparison/xlsx_files/image10.xlsx | Bin 0 -> 16969 bytes
xlsxwriter/test/comparison/xlsx_files/image11.xlsx | Bin 0 -> 16977 bytes
xlsxwriter/test/comparison/xlsx_files/image12.xlsx | Bin 0 -> 17023 bytes
xlsxwriter/test/comparison/xlsx_files/image13.xlsx | Bin 0 -> 17035 bytes
xlsxwriter/test/comparison/xlsx_files/image14.xlsx | Bin 0 -> 17075 bytes
xlsxwriter/test/comparison/xlsx_files/image15.xlsx | Bin 0 -> 17088 bytes
xlsxwriter/test/comparison/xlsx_files/image16.xlsx | Bin 0 -> 33191 bytes
xlsxwriter/test/comparison/xlsx_files/image17.xlsx | Bin 0 -> 33235 bytes
xlsxwriter/test/comparison/xlsx_files/image18.xlsx | Bin 0 -> 33243 bytes
xlsxwriter/test/comparison/xlsx_files/image19.xlsx | Bin 0 -> 65520 bytes
xlsxwriter/test/comparison/xlsx_files/image20.xlsx | Bin 0 -> 176790 bytes
xlsxwriter/test/comparison/xlsx_files/image21.xlsx | Bin 0 -> 17329 bytes
xlsxwriter/test/comparison/xlsx_files/image22.xlsx | Bin 0 -> 8991 bytes
xlsxwriter/test/comparison/xlsx_files/image23.xlsx | Bin 0 -> 11271 bytes
xlsxwriter/test/comparison/xlsx_files/image24.xlsx | Bin 0 -> 8546 bytes
xlsxwriter/test/comparison/xlsx_files/image25.xlsx | Bin 0 -> 8544 bytes
xlsxwriter/test/comparison/xlsx_files/image26.xlsx | Bin 0 -> 9547 bytes
xlsxwriter/test/comparison/xlsx_files/image27.xlsx | Bin 0 -> 14234 bytes
xlsxwriter/test/comparison/xlsx_files/image28.xlsx | Bin 0 -> 8871 bytes
xlsxwriter/test/comparison/xlsx_files/image29.xlsx | Bin 0 -> 8871 bytes
xlsxwriter/test/comparison/xlsx_files/image30.xlsx | Bin 0 -> 8634 bytes
xlsxwriter/test/comparison/xlsx_files/image31.xlsx | Bin 0 -> 8688 bytes
xlsxwriter/test/comparison/xlsx_files/image32.xlsx | Bin 0 -> 8601 bytes
xlsxwriter/test/comparison/xlsx_files/image33.xlsx | Bin 0 -> 8731 bytes
xlsxwriter/test/comparison/xlsx_files/image34.xlsx | Bin 0 -> 8615 bytes
xlsxwriter/test/comparison/xlsx_files/image35.xlsx | Bin 0 -> 9053 bytes
.../test/comparison/xlsx_files/image_anchor01.xlsx | Bin 0 -> 8613 bytes
.../test/comparison/xlsx_files/image_anchor02.xlsx | Bin 0 -> 8609 bytes
.../test/comparison/xlsx_files/image_anchor03.xlsx | Bin 0 -> 8602 bytes
.../test/comparison/xlsx_files/image_anchor04.xlsx | Bin 0 -> 8742 bytes
.../test/comparison/xlsx_files/image_anchor05.xlsx | Bin 0 -> 8740 bytes
.../test/comparison/xlsx_files/image_anchor06.xlsx | Bin 0 -> 8732 bytes
.../test/comparison/xlsx_files/image_anchor07.xlsx | Bin 0 -> 11092 bytes
.../test/comparison/xlsx_files/landscape01.xlsx | Bin 0 -> 7366 bytes
xlsxwriter/test/comparison/xlsx_files/macro01.xlsm | Bin 0 -> 10365 bytes
.../test/comparison/xlsx_files/merge_cells01.xlsx | Bin 0 -> 7487 bytes
.../test/comparison/xlsx_files/merge_range01.xlsx | Bin 0 -> 7394 bytes
.../test/comparison/xlsx_files/merge_range02.xlsx | Bin 0 -> 7454 bytes
.../test/comparison/xlsx_files/merge_range03.xlsx | Bin 0 -> 7427 bytes
.../test/comparison/xlsx_files/merge_range04.xlsx | Bin 0 -> 7417 bytes
.../test/comparison/xlsx_files/merge_range05.xlsx | Bin 0 -> 7087 bytes
.../test/comparison/xlsx_files/optimize01.xlsx | Bin 0 -> 4962 bytes
.../test/comparison/xlsx_files/optimize02.xlsx | Bin 0 -> 4962 bytes
.../test/comparison/xlsx_files/optimize03.xlsx | Bin 0 -> 5803 bytes
.../test/comparison/xlsx_files/optimize04.xlsx | Bin 0 -> 5089 bytes
.../test/comparison/xlsx_files/optimize05.xlsx | Bin 0 -> 5174 bytes
.../test/comparison/xlsx_files/optimize06.xlsx | Bin 0 -> 5952 bytes
.../test/comparison/xlsx_files/optimize07.xlsx | Bin 0 -> 5078 bytes
.../test/comparison/xlsx_files/optimize08.xlsx | Bin 0 -> 5110 bytes
.../test/comparison/xlsx_files/optimize09.xlsx | Bin 0 -> 4950 bytes
.../test/comparison/xlsx_files/optimize10.xlsx | Bin 0 -> 5550 bytes
.../test/comparison/xlsx_files/optimize11.xlsx | Bin 0 -> 8822 bytes
.../test/comparison/xlsx_files/outline01.xlsx | Bin 0 -> 8015 bytes
.../test/comparison/xlsx_files/outline02.xlsx | Bin 0 -> 8061 bytes
.../test/comparison/xlsx_files/outline03.xlsx | Bin 0 -> 8130 bytes
.../test/comparison/xlsx_files/outline04.xlsx | Bin 0 -> 7525 bytes
.../test/comparison/xlsx_files/outline05.xlsx | Bin 0 -> 8065 bytes
.../test/comparison/xlsx_files/outline06.xlsx | Bin 0 -> 8076 bytes
.../test/comparison/xlsx_files/page_breaks01.xlsx | Bin 0 -> 8044 bytes
.../test/comparison/xlsx_files/page_breaks02.xlsx | Bin 0 -> 8058 bytes
.../test/comparison/xlsx_files/page_breaks03.xlsx | Bin 0 -> 10681 bytes
.../test/comparison/xlsx_files/page_breaks04.xlsx | Bin 0 -> 8043 bytes
.../test/comparison/xlsx_files/page_breaks05.xlsx | Bin 0 -> 8058 bytes
.../test/comparison/xlsx_files/page_breaks06.xlsx | Bin 0 -> 8103 bytes
.../test/comparison/xlsx_files/page_view01.xlsx | Bin 0 -> 8015 bytes
xlsxwriter/test/comparison/xlsx_files/panes01.xlsx | Bin 0 -> 13870 bytes
.../test/comparison/xlsx_files/print_across01.xlsx | Bin 0 -> 8052 bytes
.../test/comparison/xlsx_files/print_area01.xlsx | Bin 0 -> 8121 bytes
.../test/comparison/xlsx_files/print_area02.xlsx | Bin 0 -> 8126 bytes
.../test/comparison/xlsx_files/print_area03.xlsx | Bin 0 -> 8122 bytes
.../test/comparison/xlsx_files/print_area04.xlsx | Bin 0 -> 8126 bytes
.../test/comparison/xlsx_files/print_area05.xlsx | Bin 0 -> 8124 bytes
.../test/comparison/xlsx_files/print_area06.xlsx | Bin 0 -> 8127 bytes
.../test/comparison/xlsx_files/print_area07.xlsx | Bin 0 -> 7314 bytes
.../comparison/xlsx_files/print_options01.xlsx | Bin 0 -> 8050 bytes
.../comparison/xlsx_files/print_options02.xlsx | Bin 0 -> 8053 bytes
.../comparison/xlsx_files/print_options03.xlsx | Bin 0 -> 8052 bytes
.../comparison/xlsx_files/print_options04.xlsx | Bin 0 -> 8047 bytes
.../comparison/xlsx_files/print_options05.xlsx | Bin 0 -> 8076 bytes
.../comparison/xlsx_files/print_options06.xlsx | Bin 0 -> 8150 bytes
.../test/comparison/xlsx_files/properties01.xlsx | Bin 0 -> 7594 bytes
.../test/comparison/xlsx_files/properties02.xlsx | Bin 0 -> 6989 bytes
.../test/comparison/xlsx_files/properties03.xlsx | Bin 0 -> 8028 bytes
.../test/comparison/xlsx_files/properties04.xlsx | Bin 0 -> 8222 bytes
.../test/comparison/xlsx_files/properties05.xlsx | Bin 0 -> 8031 bytes
.../test/comparison/xlsx_files/protect01.xlsx | Bin 0 -> 7083 bytes
.../test/comparison/xlsx_files/protect02.xlsx | Bin 0 -> 7110 bytes
.../test/comparison/xlsx_files/protect03.xlsx | Bin 0 -> 7123 bytes
.../test/comparison/xlsx_files/quote_name01.xlsx | Bin 0 -> 26399 bytes
.../test/comparison/xlsx_files/quote_name02.xlsx | Bin 0 -> 26528 bytes
.../test/comparison/xlsx_files/quote_name03.xlsx | Bin 0 -> 26531 bytes
.../test/comparison/xlsx_files/quote_name04.xlsx | Bin 0 -> 9114 bytes
.../comparison/xlsx_files/remove_timezone01.xlsx | Bin 0 -> 7127 bytes
.../test/comparison/xlsx_files/repeat01.xlsx | Bin 0 -> 8122 bytes
.../test/comparison/xlsx_files/repeat02.xlsx | Bin 0 -> 8126 bytes
.../test/comparison/xlsx_files/repeat03.xlsx | Bin 0 -> 8132 bytes
.../test/comparison/xlsx_files/repeat04.xlsx | Bin 0 -> 8136 bytes
.../test/comparison/xlsx_files/repeat05.xlsx | Bin 0 -> 9713 bytes
.../test/comparison/xlsx_files/rich_string01.xlsx | Bin 0 -> 7488 bytes
.../test/comparison/xlsx_files/rich_string02.xlsx | Bin 0 -> 7487 bytes
.../test/comparison/xlsx_files/rich_string03.xlsx | Bin 0 -> 7490 bytes
.../test/comparison/xlsx_files/rich_string04.xlsx | Bin 0 -> 7493 bytes
.../test/comparison/xlsx_files/rich_string05.xlsx | Bin 0 -> 7570 bytes
.../test/comparison/xlsx_files/rich_string06.xlsx | Bin 0 -> 7484 bytes
.../test/comparison/xlsx_files/rich_string07.xlsx | Bin 0 -> 7596 bytes
.../test/comparison/xlsx_files/rich_string08.xlsx | Bin 0 -> 7527 bytes
.../test/comparison/xlsx_files/rich_string09.xlsx | Bin 0 -> 7488 bytes
.../test/comparison/xlsx_files/rich_string10.xlsx | Bin 0 -> 7512 bytes
.../test/comparison/xlsx_files/rich_string11.xlsx | Bin 0 -> 7503 bytes
.../test/comparison/xlsx_files/rich_string12.xlsx | Bin 0 -> 7619 bytes
.../test/comparison/xlsx_files/selection01.xlsx | Bin 0 -> 7008 bytes
.../test/comparison/xlsx_files/selection02.xlsx | Bin 0 -> 9294 bytes
.../test/comparison/xlsx_files/set_column01.xlsx | Bin 0 -> 7318 bytes
.../test/comparison/xlsx_files/set_column03.xlsx | Bin 0 -> 7545 bytes
.../test/comparison/xlsx_files/set_column04.xlsx | Bin 0 -> 7580 bytes
.../test/comparison/xlsx_files/set_column05.xlsx | Bin 0 -> 9747 bytes
.../test/comparison/xlsx_files/set_column06.xlsx | Bin 0 -> 9717 bytes
.../test/comparison/xlsx_files/set_column07.xlsx | Bin 0 -> 17583 bytes
.../test/comparison/xlsx_files/set_column08.xlsx | Bin 0 -> 17555 bytes
.../test/comparison/xlsx_files/set_column09.xlsx | Bin 0 -> 7059 bytes
.../comparison/xlsx_files/set_print_scale01.xlsx | Bin 0 -> 8044 bytes
.../comparison/xlsx_files/set_start_page01.xlsx | Bin 0 -> 7423 bytes
.../comparison/xlsx_files/set_start_page02.xlsx | Bin 0 -> 7427 bytes
.../comparison/xlsx_files/set_start_page03.xlsx | Bin 0 -> 7428 bytes
.../comparison/xlsx_files/shared_strings01.xlsx | Bin 0 -> 8638 bytes
.../comparison/xlsx_files/shared_strings02.xlsx | Bin 0 -> 7508 bytes
.../test/comparison/xlsx_files/simple01.xlsx | Bin 0 -> 7333 bytes
.../test/comparison/xlsx_files/simple02.xlsx | Bin 0 -> 8313 bytes
.../test/comparison/xlsx_files/simple03.xlsx | Bin 0 -> 8339 bytes
.../test/comparison/xlsx_files/simple04.xlsx | Bin 0 -> 7109 bytes
.../test/comparison/xlsx_files/simple05.xlsx | Bin 0 -> 7510 bytes
.../test/comparison/xlsx_files/simple07.xlsx | Bin 0 -> 7391 bytes
.../test/comparison/xlsx_files/simple08.xlsx | Bin 0 -> 7034 bytes
.../test/comparison/xlsx_files/simple09.xlsx | Bin 0 -> 6972 bytes
.../test/comparison/xlsx_files/tab_color01.xlsx | Bin 0 -> 7341 bytes
xlsxwriter/test/comparison/xlsx_files/table01.xlsx | Bin 0 -> 8180 bytes
xlsxwriter/test/comparison/xlsx_files/table02.xlsx | Bin 0 -> 10913 bytes
xlsxwriter/test/comparison/xlsx_files/table03.xlsx | Bin 0 -> 8275 bytes
xlsxwriter/test/comparison/xlsx_files/table04.xlsx | Bin 0 -> 9513 bytes
xlsxwriter/test/comparison/xlsx_files/table05.xlsx | Bin 0 -> 10802 bytes
xlsxwriter/test/comparison/xlsx_files/table06.xlsx | Bin 0 -> 11718 bytes
xlsxwriter/test/comparison/xlsx_files/table07.xlsx | Bin 0 -> 8137 bytes
xlsxwriter/test/comparison/xlsx_files/table08.xlsx | Bin 0 -> 8579 bytes
xlsxwriter/test/comparison/xlsx_files/table09.xlsx | Bin 0 -> 8870 bytes
xlsxwriter/test/comparison/xlsx_files/table10.xlsx | Bin 0 -> 9032 bytes
xlsxwriter/test/comparison/xlsx_files/table11.xlsx | Bin 0 -> 8324 bytes
xlsxwriter/test/comparison/xlsx_files/table12.xlsx | Bin 0 -> 8267 bytes
xlsxwriter/test/comparison/xlsx_files/table13.xlsx | Bin 0 -> 8401 bytes
xlsxwriter/test/comparison/xlsx_files/table14.xlsx | Bin 0 -> 8492 bytes
xlsxwriter/test/comparison/xlsx_files/table15.xlsx | Bin 0 -> 8329 bytes
xlsxwriter/test/comparison/xlsx_files/table17.xlsx | Bin 0 -> 8895 bytes
xlsxwriter/test/comparison/xlsx_files/table18.xlsx | Bin 0 -> 8268 bytes
xlsxwriter/test/comparison/xlsx_files/table19.xlsx | Bin 0 -> 8209 bytes
xlsxwriter/test/comparison/xlsx_files/table21.xlsx | Bin 0 -> 8154 bytes
xlsxwriter/test/comparison/xlsx_files/table22.xlsx | Bin 0 -> 8167 bytes
.../test/comparison/xlsx_files/textbox01.xlsx | Bin 0 -> 8091 bytes
.../test/comparison/xlsx_files/textbox02.xlsx | Bin 0 -> 8169 bytes
.../test/comparison/xlsx_files/textbox03.xlsx | Bin 0 -> 9698 bytes
.../test/comparison/xlsx_files/textbox04.xlsx | Bin 0 -> 9661 bytes
.../test/comparison/xlsx_files/textbox05.xlsx | Bin 0 -> 10203 bytes
.../test/comparison/xlsx_files/textbox06.xlsx | Bin 0 -> 8075 bytes
.../test/comparison/xlsx_files/textbox07.xlsx | Bin 0 -> 8100 bytes
.../test/comparison/xlsx_files/textbox08.xlsx | Bin 0 -> 8116 bytes
.../test/comparison/xlsx_files/textbox09.xlsx | Bin 0 -> 8109 bytes
.../test/comparison/xlsx_files/textbox10.xlsx | Bin 0 -> 8071 bytes
.../test/comparison/xlsx_files/textbox11.xlsx | Bin 0 -> 8102 bytes
.../test/comparison/xlsx_files/textbox12.xlsx | Bin 0 -> 8090 bytes
.../test/comparison/xlsx_files/textbox13.xlsx | Bin 0 -> 8097 bytes
.../test/comparison/xlsx_files/textbox14.xlsx | Bin 0 -> 8072 bytes
.../test/comparison/xlsx_files/textbox15.xlsx | Bin 0 -> 8098 bytes
.../test/comparison/xlsx_files/textbox16.xlsx | Bin 0 -> 8098 bytes
.../test/comparison/xlsx_files/textbox17.xlsx | Bin 0 -> 8097 bytes
.../test/comparison/xlsx_files/textbox18.xlsx | Bin 0 -> 8292 bytes
.../test/comparison/xlsx_files/textbox19.xlsx | Bin 0 -> 8165 bytes
.../test/comparison/xlsx_files/textbox20.xlsx | Bin 0 -> 8097 bytes
.../test/comparison/xlsx_files/textbox21.xlsx | Bin 0 -> 8105 bytes
.../test/comparison/xlsx_files/textbox22.xlsx | Bin 0 -> 8139 bytes
.../test/comparison/xlsx_files/textbox23.xlsx | Bin 0 -> 8113 bytes
.../test/comparison/xlsx_files/textbox24.xlsx | Bin 0 -> 8139 bytes
.../test/comparison/xlsx_files/textbox25.xlsx | Bin 0 -> 8094 bytes
.../test/comparison/xlsx_files/textbox26.xlsx | Bin 0 -> 8140 bytes
.../test/comparison/xlsx_files/tutorial01.xlsx | Bin 0 -> 7718 bytes
.../test/comparison/xlsx_files/tutorial02.xlsx | Bin 0 -> 7848 bytes
.../test/comparison/xlsx_files/tutorial03.xlsx | Bin 0 -> 7964 bytes
xlsxwriter/test/comparison/xlsx_files/types01.xlsx | Bin 0 -> 5814 bytes
xlsxwriter/test/comparison/xlsx_files/types02.xlsx | Bin 0 -> 7021 bytes
xlsxwriter/test/comparison/xlsx_files/types03.xlsx | Bin 0 -> 7017 bytes
xlsxwriter/test/comparison/xlsx_files/types04.xlsx | Bin 0 -> 7765 bytes
xlsxwriter/test/comparison/xlsx_files/types05.xlsx | Bin 0 -> 6142 bytes
xlsxwriter/test/comparison/xlsx_files/types06.xlsx | Bin 0 -> 7309 bytes
xlsxwriter/test/comparison/xlsx_files/types07.xlsx | Bin 0 -> 7338 bytes
.../comparison/xlsx_files/unicode_polish_utf8.txt | 34 +
.../comparison/xlsx_files/unicode_polish_utf8.xlsx | Bin 0 -> 5884 bytes
.../comparison/xlsx_files/unicode_shift_jis.txt | 35 +
.../comparison/xlsx_files/unicode_shift_jis.xlsx | Bin 0 -> 7738 bytes
xlsxwriter/test/comparison/xlsx_files/utf8_01.xlsx | Bin 0 -> 7355 bytes
xlsxwriter/test/comparison/xlsx_files/utf8_03.xlsx | Bin 0 -> 7330 bytes
xlsxwriter/test/comparison/xlsx_files/utf8_04.xlsx | Bin 0 -> 7352 bytes
xlsxwriter/test/comparison/xlsx_files/utf8_05.xlsx | Bin 0 -> 7311 bytes
xlsxwriter/test/comparison/xlsx_files/utf8_06.xlsx | Bin 0 -> 7482 bytes
xlsxwriter/test/comparison/xlsx_files/utf8_07.xlsx | Bin 0 -> 8823 bytes
xlsxwriter/test/comparison/xlsx_files/utf8_08.xlsx | Bin 0 -> 8049 bytes
xlsxwriter/test/comparison/xlsx_files/utf8_09.xlsx | Bin 0 -> 7080 bytes
xlsxwriter/test/comparison/xlsx_files/utf8_10.xlsx | Bin 0 -> 9416 bytes
xlsxwriter/test/comparison/xlsx_files/utf8_11.xlsx | Bin 0 -> 7333 bytes
.../test/comparison/xlsx_files/vbaProject01.bin | Bin 0 -> 9216 bytes
.../test/comparison/xlsx_files/vbaProject02.bin | Bin 0 -> 12800 bytes
xlsxwriter/test/contenttypes/__init__.py | 0
.../test/contenttypes/test_contenttypes01.py | 55 +
.../test/contenttypes/test_initialisation.py | 32 +
xlsxwriter/test/core/__init__.py | 0
xlsxwriter/test/core/test_core01.py | 49 +
xlsxwriter/test/core/test_core02.py | 63 +
xlsxwriter/test/core/test_initialisation.py | 32 +
xlsxwriter/test/drawing/__init__.py | 0
xlsxwriter/test/drawing/test_drawing_chart01.py | 70 +
xlsxwriter/test/drawing/test_drawing_image01.py | 146 +
.../drawing/test_write_a_graphic_frame_locks.py | 32 +
xlsxwriter/test/drawing/test_write_c_chart.py | 32 +
.../drawing/test_write_c_nv_graphic_frame_pr.py | 32 +
xlsxwriter/test/drawing/test_write_c_nv_pr.py | 40 +
xlsxwriter/test/drawing/test_write_col.py | 32 +
xlsxwriter/test/drawing/test_write_col_off.py | 32 +
xlsxwriter/test/drawing/test_write_ext.py | 32 +
xlsxwriter/test/drawing/test_write_pos.py | 32 +
xlsxwriter/test/drawing/test_write_row.py | 32 +
xlsxwriter/test/drawing/test_write_row_off.py | 32 +
xlsxwriter/test/drawing/test_xml_declaration.py | 32 +
xlsxwriter/test/excel_comparsion_test.py | 32 +
xlsxwriter/test/format/__init__.py | 0
xlsxwriter/test/format/test_initialisation.py | 32 +
xlsxwriter/test/helperfunctions.py | 224 +
xlsxwriter/test/relationships/__init__.py | 0
.../test/relationships/test_initialisation.py | 32 +
.../test/relationships/test_relationships01.py | 48 +
xlsxwriter/test/sharedstrings/__init__.py | 0
.../test/sharedstrings/test_initialisation.py | 32 +
.../test/sharedstrings/test_sharedstrings01.py | 73 +
.../test/sharedstrings/test_sharedstrings02.py | 61 +
xlsxwriter/test/sharedstrings/test_write_si.py | 32 +
xlsxwriter/test/sharedstrings/test_write_sst.py | 45 +
xlsxwriter/test/styles/__init__.py | 0
xlsxwriter/test/styles/test_initialisation.py | 32 +
xlsxwriter/test/styles/test_styles01.py | 90 +
xlsxwriter/test/styles/test_styles02.py | 124 +
xlsxwriter/test/styles/test_styles03.py | 110 +
xlsxwriter/test/styles/test_styles04.py | 236 +
xlsxwriter/test/styles/test_styles05.py | 170 +
xlsxwriter/test/styles/test_styles06.py | 123 +
xlsxwriter/test/styles/test_styles07.py | 124 +
xlsxwriter/test/styles/test_styles08.py | 129 +
xlsxwriter/test/styles/test_styles09.py | 116 +
xlsxwriter/test/styles/test_write_border.py | 36 +
xlsxwriter/test/styles/test_write_borders.py | 38 +
xlsxwriter/test/styles/test_write_cell_style.py | 32 +
.../test/styles/test_write_cell_style_xfs.py | 32 +
xlsxwriter/test/styles/test_write_cell_styles.py | 32 +
xlsxwriter/test/styles/test_write_cell_xfs.py | 38 +
xlsxwriter/test/styles/test_write_colors.py | 59 +
xlsxwriter/test/styles/test_write_default_fill.py | 32 +
xlsxwriter/test/styles/test_write_dxfs.py | 32 +
xlsxwriter/test/styles/test_write_fills.py | 34 +
xlsxwriter/test/styles/test_write_font.py | 254 +
xlsxwriter/test/styles/test_write_fonts.py | 38 +
xlsxwriter/test/styles/test_write_mru_colors.py | 32 +
xlsxwriter/test/styles/test_write_num_fmt.py | 32 +
xlsxwriter/test/styles/test_write_num_fmts.py | 39 +
xlsxwriter/test/styles/test_write_style_sheet.py | 32 +
xlsxwriter/test/styles/test_write_style_xf.py | 32 +
xlsxwriter/test/styles/test_write_xf.py | 491 +
xlsxwriter/test/styles/test_write_xf_methods.py | 501 +
xlsxwriter/test/table/__init__.py | 0
xlsxwriter/test/table/test_initialisation.py | 32 +
xlsxwriter/test/table/test_table01.py | 57 +
xlsxwriter/test/table/test_table02.py | 59 +
xlsxwriter/test/table/test_table03.py | 59 +
xlsxwriter/test/table/test_table04.py | 56 +
xlsxwriter/test/table/test_table05.py | 56 +
xlsxwriter/test/table/test_table06.py | 62 +
xlsxwriter/test/table/test_table07.py | 58 +
xlsxwriter/test/table/test_table08.py | 63 +
xlsxwriter/test/table/test_table09.py | 75 +
xlsxwriter/test/table/test_table10.py | 58 +
xlsxwriter/test/table/test_table11.py | 70 +
xlsxwriter/test/table/test_table12.py | 70 +
xlsxwriter/test/table/test_write_auto_filter.py | 34 +
xlsxwriter/test/table/test_write_table_column.py | 32 +
.../test/table/test_write_table_style_info.py | 40 +
xlsxwriter/test/theme/__init__.py | 0
xlsxwriter/test/utility/__init__.py | 0
xlsxwriter/test/utility/test_xl_cell_to_rowcol.py | 59 +
.../test/utility/test_xl_cell_to_rowcol_abs.py | 59 +
xlsxwriter/test/utility/test_xl_col_to_name.py | 53 +
xlsxwriter/test/utility/test_xl_range.py | 65 +
xlsxwriter/test/utility/test_xl_rowcol_to_cell.py | 87 +
xlsxwriter/test/vml/__init__.py | 0
xlsxwriter/test/vml/test_vml01.py | 62 +
xlsxwriter/test/vml/test_vml02.py | 70 +
xlsxwriter/test/vml/test_vml03.py | 67 +
xlsxwriter/test/vml/test_write_anchor.py | 32 +
xlsxwriter/test/vml/test_write_auto_fill.py | 32 +
xlsxwriter/test/vml/test_write_column.py | 32 +
xlsxwriter/test/vml/test_write_div.py | 32 +
xlsxwriter/test/vml/test_write_fill.py | 42 +
xlsxwriter/test/vml/test_write_idmap.py | 32 +
xlsxwriter/test/vml/test_write_move_with_cells.py | 32 +
xlsxwriter/test/vml/test_write_path.py | 52 +
xlsxwriter/test/vml/test_write_row.py | 32 +
xlsxwriter/test/vml/test_write_shadow.py | 32 +
xlsxwriter/test/vml/test_write_shapelayout.py | 32 +
xlsxwriter/test/vml/test_write_shapetype.py | 42 +
xlsxwriter/test/vml/test_write_size_with_cells.py | 32 +
xlsxwriter/test/vml/test_write_stroke.py | 32 +
xlsxwriter/test/vml/test_write_textbox.py | 32 +
xlsxwriter/test/vml/test_xml_declaration.py | 32 +
xlsxwriter/test/workbook/__init__.py | 0
xlsxwriter/test/workbook/test_check_sheetname.py | 58 +
.../test/workbook/test_get_worksheet_by_name.py | 78 +
xlsxwriter/test/workbook/test_initialisation.py | 35 +
xlsxwriter/test/workbook/test_workbook01.py | 49 +
xlsxwriter/test/workbook/test_workbook02.py | 51 +
xlsxwriter/test/workbook/test_workbook03.py | 51 +
xlsxwriter/test/workbook/test_write_book_views.py | 35 +
xlsxwriter/test/workbook/test_write_calc_pr.py | 76 +
.../test/workbook/test_write_defined_name.py | 35 +
.../test/workbook/test_write_defined_names.py | 62 +
.../test/workbook/test_write_file_version.py | 35 +
xlsxwriter/test/workbook/test_write_sheet.py | 55 +
xlsxwriter/test/workbook/test_write_sheets.py | 36 +
xlsxwriter/test/workbook/test_write_workbook.py | 35 +
xlsxwriter/test/workbook/test_write_workbook_pr.py | 35 +
.../test/workbook/test_write_workbook_view.py | 113 +
xlsxwriter/test/worksheet/__init__.py | 0
xlsxwriter/test/worksheet/test_calcuate_spans.py | 326 +
xlsxwriter/test/worksheet/test_cond_format01.py | 81 +
xlsxwriter/test/worksheet/test_cond_format02.py | 88 +
xlsxwriter/test/worksheet/test_cond_format03.py | 97 +
xlsxwriter/test/worksheet/test_cond_format04.py | 85 +
xlsxwriter/test/worksheet/test_cond_format05.py | 143 +
xlsxwriter/test/worksheet/test_cond_format06.py | 103 +
xlsxwriter/test/worksheet/test_cond_format07.py | 112 +
xlsxwriter/test/worksheet/test_cond_format08.py | 162 +
xlsxwriter/test/worksheet/test_cond_format09.py | 104 +
xlsxwriter/test/worksheet/test_cond_format10.py | 86 +
xlsxwriter/test/worksheet/test_cond_format11.py | 86 +
xlsxwriter/test/worksheet/test_cond_format12.py | 131 +
xlsxwriter/test/worksheet/test_cond_format13.py | 133 +
xlsxwriter/test/worksheet/test_cond_format14.py | 130 +
xlsxwriter/test/worksheet/test_cond_format15.py | 111 +
xlsxwriter/test/worksheet/test_cond_format16.py | 138 +
xlsxwriter/test/worksheet/test_cond_format17.py | 141 +
xlsxwriter/test/worksheet/test_cond_format18.py | 136 +
xlsxwriter/test/worksheet/test_cond_format19.py | 139 +
xlsxwriter/test/worksheet/test_cond_format20.py | 113 +
xlsxwriter/test/worksheet/test_cond_format21.py | 141 +
xlsxwriter/test/worksheet/test_date_time_01.py | 140 +
xlsxwriter/test/worksheet/test_date_time_02.py | 465 +
xlsxwriter/test/worksheet/test_date_time_03.py | 259 +
.../test/worksheet/test_extract_filter_tokens.py | 125 +
xlsxwriter/test/worksheet/test_initialisation.py | 32 +
xlsxwriter/test/worksheet/test_merge_range01.py | 138 +
xlsxwriter/test/worksheet/test_merge_range02.py | 147 +
.../test/worksheet/test_parse_filter_expression.py | 157 +
xlsxwriter/test/worksheet/test_sparkline01.py | 69 +
xlsxwriter/test/worksheet/test_sparkline02.py | 93 +
xlsxwriter/test/worksheet/test_sparkline03.py | 128 +
xlsxwriter/test/worksheet/test_sparkline04.py | 95 +
xlsxwriter/test/worksheet/test_sparkline05.py | 96 +
xlsxwriter/test/worksheet/test_sparkline06.py | 117 +
xlsxwriter/test/worksheet/test_sparkline07.py | 328 +
xlsxwriter/test/worksheet/test_sparkline08.py | 166 +
xlsxwriter/test/worksheet/test_sparkline09.py | 1255 +++
xlsxwriter/test/worksheet/test_sparkline10.py | 107 +
xlsxwriter/test/worksheet/test_sparkline11.py | 208 +
xlsxwriter/test/worksheet/test_sparkline12.py | 94 +
xlsxwriter/test/worksheet/test_worksheet01.py | 45 +
xlsxwriter/test/worksheet/test_worksheet02.py | 52 +
xlsxwriter/test/worksheet/test_worksheet03.py | 105 +
xlsxwriter/test/worksheet/test_worksheet04.py | 61 +
xlsxwriter/test/worksheet/test_worksheet05.py | 67 +
xlsxwriter/test/worksheet/test_worksheet06.py | 138 +
xlsxwriter/test/worksheet/test_worksheet07.py | 76 +
xlsxwriter/test/worksheet/test_worksheet08.py | 91 +
xlsxwriter/test/worksheet/test_worksheet09.py | 132 +
.../test/worksheet/test_write_auto_filter.py | 294 +
xlsxwriter/test/worksheet/test_write_cell.py | 75 +
xlsxwriter/test/worksheet/test_write_col_breaks.py | 46 +
.../test/worksheet/test_write_custom_filter.py | 32 +
.../test/worksheet/test_write_custom_filters.py | 42 +
.../worksheet/test_write_data_validations01.py | 174 +
.../worksheet/test_write_data_validations02.py | 944 ++
xlsxwriter/test/worksheet/test_write_dimension.py | 32 +
xlsxwriter/test/worksheet/test_write_filter.py | 32 +
.../test/worksheet/test_write_filter_column.py | 32 +
xlsxwriter/test/worksheet/test_write_filters.py | 52 +
.../test/worksheet/test_write_header_footer.py | 56 +
xlsxwriter/test/worksheet/test_write_hyperlink.py | 62 +
xlsxwriter/test/worksheet/test_write_merge_cell.py | 32 +
.../test/worksheet/test_write_merge_cells.py | 65 +
.../test/worksheet/test_write_page_margins.py | 109 +
xlsxwriter/test/worksheet/test_write_page_setup.py | 80 +
.../test/worksheet/test_write_print_options.py | 88 +
xlsxwriter/test/worksheet/test_write_row.py | 105 +
xlsxwriter/test/worksheet/test_write_row_breaks.py | 46 +
xlsxwriter/test/worksheet/test_write_sheet_data.py | 32 +
.../test/worksheet/test_write_sheet_format_pr.py | 32 +
xlsxwriter/test/worksheet/test_write_sheet_pr.py | 56 +
.../test/worksheet/test_write_sheet_protection.py | 290 +
xlsxwriter/test/worksheet/test_write_sheet_view.py | 91 +
.../test/worksheet/test_write_sheet_views1.py | 93 +
.../test/worksheet/test_write_sheet_views2.py | 92 +
.../test/worksheet/test_write_sheet_views3.py | 134 +
.../test/worksheet/test_write_sheet_views4.py | 135 +
.../test/worksheet/test_write_sheet_views5.py | 122 +
.../test/worksheet/test_write_sheet_views6.py | 82 +
.../test/worksheet/test_write_sheet_views7.py | 112 +
.../test/worksheet/test_write_sheet_views8.py | 82 +
.../test/worksheet/test_write_sheet_views9.py | 83 +
xlsxwriter/test/worksheet/test_write_tab_color.py | 33 +
xlsxwriter/test/worksheet/test_write_worksheet.py | 32 +
xlsxwriter/test/xmlwriter/__init__.py | 0
xlsxwriter/test/xmlwriter/test_xmlwriter.py | 192 +
xlsxwriter/theme.py | 74 +
xlsxwriter/utility.py | 673 ++
xlsxwriter/vml.py | 706 ++
xlsxwriter/workbook.py | 1637 ++++
xlsxwriter/worksheet.py | 6514 +++++++++++++
xlsxwriter/xmlwriter.py | 210 +
2086 files changed, 131237 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..80aa5ae
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,29 @@
+MANIFEST
+dist/*
+*.tar.gz
+*~
+*.bak
+TAGS
+.#*
+*#
+~*xlsx
+*.pyc
+.project
+.pydevproject
+.coverage
+cover/
+.settings/
+xlsxwriter/test/comparison/_test_*.xlsx
+dev/docs/build/
+dev/performance/*.xlsx
+examples/*.xlsx
+.DS_Store
+build/
+.buildinfo
+objects.inv
+__pycache__
+.tox/
+.idea
+.cache/
+XlsxWriter.egg-info/
+
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..124013c
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+language: python
+python:
+ - "2.6"
+ - "2.7"
+ - "3.2"
+ - "3.3"
+ - "3.4"
+ - "3.5"
+ - "nightly"
+ - "pypy"
+install: "pip install pytest"
+script: py.test
+sudo: false
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..46ce28a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,149 @@
+# XlsxWriter: Bug Reports and Pull Requests
+
+
+## Reporting Bugs
+
+Here are some tips on reporting bugs in XlsxWriter.
+
+
+### Upgrade to the latest version of the module
+
+The bug you are reporting may already be fixed in the latest version of the
+module. You can check which version of xlsxwriter that you are using as follows:
+
+ python -c 'import xlsxwriter; print(xlsxwriter.__version__)'
+
+
+The [Changes](https://github.com/jmcnamara/XlsxWriter/blob/master/Changes) file lists what has changed in the latest versions.
+
+
+### Read the documentation
+
+Read or search the [XlsxWriter documentation](https://xlsxwriter.readthedocs.io/) to see if the issue you are encountering is already explained.
+
+### Look at the example programs
+
+There are [many example programs](https://github.com/jmcnamara/XlsxWriter/tree/master/examples) in the distribution. Try to identify an example program that corresponds to your query and adapt it to use as a bug report.
+
+
+### Pointers for submitting a bug report
+
+1. Describe the problem as clearly and as concisely as possible.
+2. Include a sample program. This is probably the most important step. It is generally easier to describe a problem in code than in written prose.
+3. The sample program should be as small as possible to demonstrate the problem. Don't copy and paste large non-relevant sections of your program.
+
+
+### Sample Bug Report
+
+A sample bug report is shown below. This format helps to analyze and respond to the bug report more quickly.
+
+> **Issue with SOMETHING**
+>
+> I am using XlsxWriter to do SOMETHING but it appears to do SOMETHING ELSE.
+>
+> I am using Python version X.Y.Z and XlsxWriter x.y.z.
+>
+> Here is some code that demonstrates the problem::
+>
+> import xlsxwriter
+>
+> workbook = xlsxwriter.Workbook('hello.xlsx')
+> worksheet = workbook.add_worksheet()
+>
+> worksheet.write('A1', 'Hello world')
+>
+> workbook.close()
+
+
+See also how [How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve) from StackOverflow.
+
+
+### Use the XlsxWriter GitHub issue tracker
+
+Submit the bug report using the [XlsxWriter issue tracker](https://github.com/jmcnamara/XlsxWriter/issues).
+
+
+## Pull Requests and Contributing to XlsxWriter
+
+All patches and pull requests are welcome but must start with an issue tracker.
+
+
+### Getting Started
+
+1. Pull requests and new feature proposals must start with an [issue tracker](https://github.com/jmcnamara/XlsxWriter/issues). This serves as the focal point for the design discussion.
+2. Describe what you plan to do. If there are API changes or additions add some pseudo-code to demonstrate them.
+3. Fork the repository.
+4. Run all the tests to make sure the current code work on your system using `make test`.
+5. Create a feature branch for your new feature.
+6. Enable Travis CI on your fork, see below.
+
+
+### Enabling Travis CI via your GitHub account
+
+Travis CI is a free Continuous Integration service that will test any code you push to GitHub with various versions of Python 2 and 3, and PyPy.
+
+See the [Travis CI Getting Started](http://about.travis-ci.org/docs/user/getting-started/) instructions.
+
+Note there is already a `.travis.yml` file in the XlsxWriter repo so that doesn't need to be created.
+
+
+### Writing Tests
+
+This is the most important step. XlsxWriter has over 1000 tests and a 2:1 test to code ratio. Patches and pull requests for anything other than minor fixes or typos will not be merged without tests.
+
+Use the existing tests in `XlsxWriter/xlsxwriter/test/` as examples.
+
+Ideally, new features should be accompanied by tests that compare XlsxWriter output against actual Excel 2007 files. See the `XlsxWriter/xlsxwriter/test/comparison` test files for examples. If you don't have access to Excel 2007 I can help you create input files for test cases.
+
+Tests should use the standard [unittest](http://docs.python.org/2/library/unittest.html) Python module.
+
+
+### Code Style
+
+Follow the general style of the surrounding code and format it to the [PEP8](http://www.python.org/dev/peps/pep-0008/) coding standards.
+
+Tests should conform to `PEP8` but can ignore `E501` for long lines to allow the inclusion of Excel XML in tests.
+
+There is a make target that will verify the source and test files:
+
+ make testpep8
+
+
+### Running tests
+
+As a minimum, tests should be run using Python 2.7 and Python 3.5.
+
+
+ make test
+ # or
+ py.test
+
+I use [pythonbrew](https://github.com/utahta/pythonbrew) and [Tox](https://tox.readthedocs.io/en/latest/) to test with a variety of Python versions. See the Makefile for example test targets. A `tox.ini` file is already configured.
+
+When you push your changes they will also be tested using [Travis CI](https://travis-ci.org/jmcnamara/XlsxWriter/) as explained above.
+
+
+### Documentation
+
+If your feature requires it then write some [RST](http://docutils.sourceforge.net/rst.html) documentation in [Sphinx](http://sphinx-doc.org) format or add to the existing documentation.
+
+The docs, in `dev/docs/source` can be built in Html format using:
+
+ make docs
+
+
+### Example programs
+
+If applicable add an example program to the `examples` directory.
+
+
+### Copyright and License
+
+Copyright remains with the original author. Do not include additional copyright claims or Licensing requirements. GitHub and the `git` repository will record your contribution an it will be acknowledged in the Changes file.
+
+
+### Submitting the Pull Request
+
+If your change involves several incremental `git` commits then `rebase` or `squash` them onto another branch so that the Pull Request is a single commit or a small number of logical commits.
+
+Push your changes to GitHub and submit the Pull Request with a hash link to the to the Issue tracker that was opened above.
diff --git a/Changes b/Changes
new file mode 100644
index 0000000..a1dadf5
--- /dev/null
+++ b/Changes
@@ -0,0 +1,987 @@
+
+Release 0.9.6 - Dec 26 2016
+---------------------------
+
+* Fix for table with data but without a header.
+ Issue `#405 <https://github.com/jmcnamara/XlsxWriter/issues/405>`_.
+
+* Add a warning when the number of series in a chart exceeds Excel's limit
+ of 255.
+ Issue `#399 <https://github.com/jmcnamara/XlsxWriter/issues/399>`_.
+
+
+Release 0.9.5 - Dec 24 2016
+---------------------------
+
+* Fix for missing `remove_timezone` option in Chart class.
+ PR from Thomas Arnhold
+ `#404 <https://github.com/jmcnamara/XlsxWriter/issues/404>`_.
+
+
+Release 0.9.4 - Dec 2 2016
+--------------------------
+
+* Added user definable removal of timezones in datetimes. See the
+ :func:`Workbook` constructor option ``remove_timezone`` and :ref:`Timezone
+ Handling in XlsxWriter <timezone_handling>`.
+ Issue `#257 <https://github.com/jmcnamara/XlsxWriter/issues/257>`_.
+
+* Fix duplicate header warning in :func:`add_table` when there is only one
+ user defined header.
+ Issue `#380 <https://github.com/jmcnamara/XlsxWriter/issues/380>`_.
+
+* Fix for `center_across` property in :func:`add_format`.
+ Issue `#381 <https://github.com/jmcnamara/XlsxWriter/issues/381>`_.
+
+
+Release 0.9.3 - July 8 2016
+---------------------------
+
+* Added check to :func:`add_table` to prevent duplicate header names which
+ leads to a corrupt Excel file.
+ Issue `#362 <https://github.com/jmcnamara/XlsxWriter/issues/362>`_.
+
+
+Release 0.9.2 - June 13 2016
+----------------------------
+
+* Added workbook :func:`set_size` method to set the workbook window size.
+
+
+Release 0.9.1 - June 8 2016
+---------------------------
+
+* Added font support to chart :func:`set_table`.
+
+* Documented used of font rotation in chart :ref:`data labels
+ <chart_series_option_data_labels>`.
+ Issue `#337 <https://github.com/jmcnamara/XlsxWriter/issues/337>`_.
+
+
+Release 0.9.0 - June 7 2016
+---------------------------
+
+* Added :ref:`trendline properties <chart_series_option_trendline>`:
+ ``intercept``, ``display_equation`` and ``display_r_squared``.
+ Feature request `#357 <https://github.com/jmcnamara/XlsxWriter/issues/357>`_.
+
+
+Release 0.8.9 - June 1 2016
+---------------------------
+
+* Fix for :func:`insert_image` issue when handling images with zero dpi.
+ Issue `#356 <https://github.com/jmcnamara/XlsxWriter/issues/356>`_.
+
+
+Release 0.8.8 - May 31 2016
+---------------------------
+
+* Added workbook :func:`set_custom_property` method to set custom document
+ properties.
+ Feature request `#355 <https://github.com/jmcnamara/XlsxWriter/issues/355>`_.
+
+
+Release 0.8.7 - May 13 2016
+---------------------------
+
+* Fix for issue when inserting read-only images on Windows.
+ Issue `#352 <https://github.com/jmcnamara/XlsxWriter/issues/352>`_.
+
+* Added :func:`get_worksheet_by_name()` method to allow the retrieval of a
+ worksheet from a workbook via its name.
+
+* Fixed issue where internal file creation and modification dates were in the
+ local timezone instead of UTC.
+
+Release 0.8.6 - April 27 2016
+-----------------------------
+
+* Fix for ``external:`` urls where the target/anchor contains spaces.
+ Issue `#350 <https://github.com/jmcnamara/XlsxWriter/issues/350>`_.
+
+
+Release 0.8.5 - April 17 2016
+-----------------------------
+
+* Added additional documentation on :ref:`ewx_pandas` and
+ :ref:`pandas_examples`.
+
+* Added fix for :func:`set_center_across` format method.
+
+
+Release 0.8.4 - January 16 2016
+-------------------------------
+
+* Fix for :func:`write_url` exception when the URL contains two ``#``
+ location/anchors. Note, URLs like this aren't strictly valid and cannot be
+ entered manually in Excel.
+ Issue `#330 <https://github.com/jmcnamara/XlsxWriter/issues/330>`_.
+
+
+Release 0.8.3 - January 14 2016
+-------------------------------
+
+* Added options to configure chart axis tick placement. See :func:`set_x_axis()`.
+
+
+Release 0.8.2 - January 13 2016
+-------------------------------
+
+* Added transparency option to solid fill colors in chart areas
+ (:ref:`chart_formatting_fill`).
+ Feature request `#298 <https://github.com/jmcnamara/XlsxWriter/issues/298>`_.
+
+
+Release 0.8.1 - January 12 2016
+-------------------------------
+
+* Added option to set chart tick interval.
+ Feature request `#251 <https://github.com/jmcnamara/XlsxWriter/issues/251>`_.
+
+
+Release 0.8.0 - January 10 2016
+-------------------------------
+
+* Added additional documentation on :ref:`working_with_formulas`.
+
+
+Release 0.7.9 - January 9 2016
+------------------------------
+
+* Added chart pattern fills, see :ref:`chart_formatting_pattern` and
+ :ref:`ex_chart_pattern`.
+ Feature request `#268 <https://github.com/jmcnamara/XlsxWriter/issues/268>`_.
+
+
+Release 0.7.8 - January 6 2016
+------------------------------
+
+* Add checks for valid and non-duplicate worksheet table names.
+ Issue `#319 <https://github.com/jmcnamara/XlsxWriter/issues/319>`_.
+
+
+Release 0.7.7 - October 19 2015
+-------------------------------
+
+* Added support for table header formatting and a fix for wrapped lines in the
+ header.
+ Feature request `#287 <https://github.com/jmcnamara/XlsxWriter/issues/287>`_.
+
+
+Release 0.7.6 - October 7 2015
+------------------------------
+
+* Fix for images with negative offsets.
+ Issue `#273 <https://github.com/jmcnamara/XlsxWriter/issues/273>`_.
+
+
+Release 0.7.5 - October 4 2015
+------------------------------
+
+* Allow hyperlinks longer than 255 characters when the link and anchor are
+ each less than or equal to 255 characters.
+
+* Added ``hyperlink_base`` document property.
+ Feature request `#306 <https://github.com/jmcnamara/XlsxWriter/issues/306>`_.
+
+
+Release 0.7.4 - September 29 2015
+---------------------------------
+
+* Added option to allow data validation input messages with the 'any' validate
+ parameter.
+
+* Fixed url encoding of links to external files and directories.
+ Issue `#278 <https://github.com/jmcnamara/XlsxWriter/issues/278>`_.
+
+
+Release 0.7.3 - May 7 2015
+--------------------------
+
+* Added documentation on :ref:`ewx_pandas` and :ref:`pandas_examples`.
+
+* Added support for ``with`` context manager.
+ Pull request `#239 <https://github.com/jmcnamara/XlsxWriter/pull/239>`_.
+
+
+Release 0.7.2 - March 29 2015
+-----------------------------
+
+* Added support for textboxes in worksheets. See :func:`insert_textbox()` and
+ :ref:`working_with_textboxes` for more details.
+ Feature request `#107 <https://github.com/jmcnamara/XlsxWriter/issues/107>`_.
+
+
+Release 0.7.1 - March 23 2015
+-----------------------------
+
+* Added gradient fills to chart objects such as the plot area of columns. See
+ :ref:`chart_formatting_gradient` and :ref:`ex_chart_gradient`.
+ Feature request `#228 <https://github.com/jmcnamara/XlsxWriter/issues/228>`_.
+
+
+Release 0.7.0 - March 21 2015
+-----------------------------
+
+* Added support for display units in chart axes. See :func:`set_x_axis()`.
+ Feature request `#185 <https://github.com/jmcnamara/XlsxWriter/issues/185>`_.
+
+* Added ``nan_inf_to_errors`` :func:`Workbook` constructor option to allow
+ mapping of Python `nan/inf` value to Excel error formulas in ``write()`` and
+ ``write_number()``.
+ Feature request `#150 <https://github.com/jmcnamara/XlsxWriter/issues/150>`_.
+
+
+Release 0.6.9 - March 19 2015
+-----------------------------
+
+* Added support for clustered category charts. See :ref:`ex_chart_clustered`
+ for details.
+ Feature request `#180 <https://github.com/jmcnamara/XlsxWriter/issues/180>`_.
+
+* Refactored the :ref:`format` and formatting documentation.
+
+Release 0.6.8 - March 17 2015
+-----------------------------
+
+* Added option to combine two different chart types. See the
+ :ref:`chart_combined_charts` section and :ref:`ex_chart_combined` and
+ :ref:`ex_chart_pareto` for more details.
+ Feature request `#72 <https://github.com/jmcnamara/XlsxWriter/issues/72>`_.
+
+
+Release 0.6.7 - March 1 2015
+----------------------------
+
+* Added option to add function value in worksheet :func:`add_table`.
+ Feature request `#216 <https://github.com/jmcnamara/XlsxWriter/issues/216>`_.
+
+* Fix for A1 row/col numbers below lower bound.
+ Issue `#212 <https://github.com/jmcnamara/XlsxWriter/issues/212>`_.
+
+
+Release 0.6.6 - January 16 2015
+-------------------------------
+
+* Fix for incorrect shebang line in `vba_extract.py` packaged in wheel.
+ Issue `#211 <https://github.com/jmcnamara/XlsxWriter/issues/211>`_.
+
+* Added docs and example for diagonal cell border.
+ See :ref:`ex_diagonal_border`.
+
+
+Release 0.6.5 - December 31 2014
+--------------------------------
+
+* Added worksheet quoting for chart names in lists.
+ Issue `#205 <https://github.com/jmcnamara/XlsxWriter/issues/205>`_.
+
+* Added docs on how to find and set VBA codenames.
+ Issue `#202 <https://github.com/jmcnamara/XlsxWriter/issues/202>`_.
+
+* Fix Python3 issue with unused charts.
+ Issue `#200 <https://github.com/jmcnamara/XlsxWriter/issues/200>`_.
+
+* Enabled warning for missing category is scatter chart.
+ Issue `#197 <https://github.com/jmcnamara/XlsxWriter/issues/197>`_.
+
+* Fix for upper chart style limit. Increased the chart style limit from
+ 42 to the correct 48.
+ Issue `#192 <https://github.com/jmcnamara/XlsxWriter/issues/192>`_.
+
+* Raise warning if a chart is inserted more than once.
+ Issue `#184 <https://github.com/jmcnamara/XlsxWriter/issues/184>`_.
+
+
+Release 0.6.4 - November 15 2014
+--------------------------------
+
+* Fix for issue where fonts applied to data labels raised exception.
+ Issue `#179 <https://github.com/jmcnamara/XlsxWriter/issues/179>`_.
+
+* Added option to allow explicit text axis types for charts, similar to date
+ axes.
+ Feature request `#178 <https://github.com/jmcnamara/XlsxWriter/issues/178>`_.
+
+* Fix for issue where the bar/column chart gap and overlap weren't
+ applied to the secondary axis.
+ Issue `#177 <https://github.com/jmcnamara/XlsxWriter/issues/177>`_.
+
+
+Release 0.6.3 - November 6 2014
+-------------------------------
+
+* Added support for adding VBA macros to workbooks. See :ref:`macros`.
+ Feature request `#126 <https://github.com/jmcnamara/XlsxWriter/issues/126>`_.
+
+
+Release 0.6.2 - November 1 2014
+-------------------------------
+
+* Added chart axis line and fill properties.
+ Feature request `#88 <https://github.com/jmcnamara/XlsxWriter/issues/88>`_.
+
+
+Release 0.6.1 - October 29 2014
+-------------------------------
+
+* Added chart specific handling of data label positions since not all positions
+ are available for all chart types.
+ Issue `#170 <https://github.com/jmcnamara/XlsxWriter/issues/170>`_.
+
+* Added number formatting
+ (issue `#130 <https://github.com/jmcnamara/XlsxWriter/issues/130>`_),
+ font handling, separator and legend key for data labels.
+ See :ref:`chart_series_option_data_labels`
+
+* Fix for non-quoted worksheet names containing spaces and non-alphanumeric
+ characters.
+ Issue `#167 <https://github.com/jmcnamara/XlsxWriter/issues/167>`_.
+
+
+Release 0.6.0 - October 15 2014
+-------------------------------
+
+* Added option to add images to headers and footers. See
+ :ref:`ex_headers_footers`.
+ Feature request `#133 <https://github.com/jmcnamara/XlsxWriter/issues/133>`_.
+
+* Fixed issue where non 96dpi images weren't scaled properly in Excel.
+ Issue `#164 <https://github.com/jmcnamara/XlsxWriter/issues/164>`_.
+
+* Added option to not scale header/footer with page. See :func:`set_header`.
+ Feature request `#134 <https://github.com/jmcnamara/XlsxWriter/issues/134>`_.
+
+
+Release 0.5.9 - October 11 2014
+-------------------------------
+
+* Removed ``egg_base`` requirement from ``setup.cfg`` which was preventing
+ installation on Windows.
+ Issue `#162 <https://github.com/jmcnamara/XlsxWriter/issues/162>`_.
+
+* Fix for issue where X axis title formula was overwritten by the Y axis
+ title.
+ Issue `#161 <https://github.com/jmcnamara/XlsxWriter/issues/161>`_.
+
+
+Release 0.5.8 - September 28 2014
+---------------------------------
+
+* Added support for Doughnut charts.
+ Feature request `#157 <https://github.com/jmcnamara/XlsxWriter/issues/157>`_.
+
+* Added support for wheel packages.
+ Feature request `#156 <https://github.com/jmcnamara/XlsxWriter/issues/156>`_.
+
+* Made the exception handling in ``write()`` clearer for unsupported types so
+ that it raises a more accurate TypeError instead of a ValueError.
+ Issue `#153 <https://github.com/jmcnamara/XlsxWriter/issues/153>`_.
+
+
+Release 0.5.7 - August 13 2014
+------------------------------
+
+* Added support for :func:`insert_image` images from byte streams to allow
+ images from URLs and other sources.
+ Feature request `#118 <https://github.com/jmcnamara/XlsxWriter/issues/118>`_.
+
+* Added :func:`write_datetime` support for datetime.timedelta.
+ Feature request `#128 <https://github.com/jmcnamara/XlsxWriter/issues/128>`_.
+
+
+Release 0.5.6 - July 22 2014
+----------------------------
+
+* Fix for spurious exception message when :func:`close()` isn't used.
+ Issue `#131 <https://github.com/jmcnamara/XlsxWriter/issues/131>`_.
+
+* Fix for formula string values that look like numbers.
+ Issue `#122 <https://github.com/jmcnamara/XlsxWriter/issues/122>`_.
+
+* Clarify :func:`print_area()` documentation for complete row/column ranges.
+ Issue `#139 <https://github.com/jmcnamara/XlsxWriter/issues/139>`_.
+
+* Fix for unicode strings in data validation lists.
+ Issue `#135 <https://github.com/jmcnamara/XlsxWriter/issues/135>`_.
+
+
+Release 0.5.5 - May 6 2014
+--------------------------
+
+* Fix for incorrect chart offsets in :func:`insert_chart()` and
+ :func:`set_size()`.
+
+Release 0.5.4 - May 4 2014
+--------------------------
+
+* Added image positioning option to :func:`insert_image` to control how
+ images are moved in relation to surrounding cells.
+ Feature request `#117 <https://github.com/jmcnamara/XlsxWriter/issues/117>`_.
+
+* Fix for chart ``error_bar`` exceptions.
+ Issue `#115 <https://github.com/jmcnamara/XlsxWriter/issues/115>`_.
+
+* Added clearer reporting of nested exceptions in ``write()`` methods.
+ Issue `#108 <https://github.com/jmcnamara/XlsxWriter/issues/108>`_.
+
+* Added support for ``inside_base`` data label position in charts.
+
+Release 0.5.3 - February 20 2014
+--------------------------------
+
+* Added checks and warnings for data validation limits.
+ Issue `#89 <https://github.com/jmcnamara/XlsxWriter/issues/89>`_.
+
+* Added option to add hyperlinks to images. Thanks to Paul Tax.
+
+* Added Python 3 Http server example. Thanks to Krystian Rosinski.
+
+* Added :func:`set_calc_mode` method to control automatic calculation of
+ formulas when worksheet is opened. Thanks to Chris Tompkinson.
+
+* Added :func:`use_zip64` method to allow ZIP64 extensions when writing
+ very large files.
+
+* Fix to handle '0' and other number like strings as number formats.
+ Issue `#103 <https://github.com/jmcnamara/XlsxWriter/issues/103>`_.
+
+* Fix for missing images in ``in_memory`` mode.
+ Issue `#102 <https://github.com/jmcnamara/XlsxWriter/issues/102>`_.
+
+
+Release 0.5.2 - December 31 2013
+--------------------------------
+
+* Added date axis handling to charts. See :ref:`ex_chart_date_axis`.
+ Feature request `#73 <https://github.com/jmcnamara/XlsxWriter/issues/73>`_.
+
+* Added support for non-contiguous chart ranges.
+ Feature request `#44 <https://github.com/jmcnamara/XlsxWriter/issues/44>`_.
+
+* Fix for low byte and control characters in strings.
+ Issue `#86 <https://github.com/jmcnamara/XlsxWriter/issues/86>`_.
+
+* Fix for chart titles with exclamation mark.
+ Issue `#83 <https://github.com/jmcnamara/XlsxWriter/issues/83>`_.
+
+* Fix to remove duplicate :func:`set_column` entries.
+ Issue `#82 <https://github.com/jmcnamara/XlsxWriter/issues/82>`_.
+
+
+Release 0.5.1 - December 2 2013
+-------------------------------
+
+* Added interval unit option for category axes.
+ Feature request `#69 <https://github.com/jmcnamara/XlsxWriter/issues/69>`_.
+
+* Fix for axis name font rotation.
+
+* Fix for several minor issues with Pie chart legends.
+
+
+Release 0.5.0 - November 17 2013
+--------------------------------
+
+* Added :ref:`Chartsheets <Chartsheet>` to allow single worksheet charts.
+ Feature request `#10 <https://github.com/jmcnamara/XlsxWriter/issues/10>`_.
+
+
+Release 0.4.9 - November 17 2013
+--------------------------------
+
+* Added :ref:`chart object positioning and sizing <chart_layout>` to allow
+ positioning of plotarea, legend, title and axis names.
+ Feature request `#66 <https://github.com/jmcnamara/XlsxWriter/issues/66>`_.
+
+* Added :func:`set_title()` ``none`` option to turn off automatic titles.
+
+* Improved :func:`define_name()` name validation.
+
+* Fix to prevent modification of user parameters in
+ :func:`conditional_format()`.
+
+
+Release 0.4.8 - November 13 2013
+--------------------------------
+
+* Added ``in_memory`` :func:`Workbook` constructor option to allow XlsxWriter
+ to work on Google App Engine.
+ Feature request `#28 <https://github.com/jmcnamara/XlsxWriter/issues/28>`_.
+
+
+Release 0.4.7 - November 9 2013
+-------------------------------
+
+* Added fix for markers on non-marker scatter charts.
+ Issue `#62 <https://github.com/jmcnamara/XlsxWriter/issues/62>`_.
+
+* Added custom error bar option. Thanks to input from Alex Birmingham.
+
+* Changed Html docs to Bootstrap theme.
+
+* Added :ref:`ex_merge_rich`.
+
+
+Release 0.4.6 - October 23 2013
+-------------------------------
+
+* Added font formatting to chart legends.
+
+
+Release 0.4.5 - October 21 2013
+-------------------------------
+
+* Added ``position_axis`` chart axis option.
+
+* Added optional list handling for chart names.
+
+
+Release 0.4.4 - October 16 2013
+-------------------------------
+
+* Documented use of :ref:`cell utility <cell_utility>` functions.
+
+* Fix for tables added in non-sequential order. Closes
+ `#51 <https://github.com/jmcnamara/XlsxWriter/issues/51>`_ reported by
+ calfzhou.
+
+
+Release 0.4.3 - September 12 2013
+---------------------------------
+
+* Fix for comments overlying columns with non-default width.
+ Issue `#45 <https://github.com/jmcnamara/XlsxWriter/issues/45>`_.
+
+
+Release 0.4.2 - August 30 2013
+------------------------------
+
+* Added a default blue underline hyperlink format for :func:`write_url()`.
+
+* Added :func:`Workbook` constructor options ``strings_to_formulas`` and
+ ``strings_to_urls`` to override default conversion of strings in write().
+
+
+Release 0.4.1 - August 28 2013
+------------------------------
+
+* Fix for charts and images that cross rows and columns that are hidden or
+ formatted but which don't have size changes. Issue
+ `#42 <https://github.com/jmcnamara/XlsxWriter/issues/42>`_ reported by
+ Kristian Stobbe.
+
+
+Release 0.4.0 - August 26 2013
+------------------------------
+
+* Added more generic support for JPEG files. Issue
+ `#40 <https://github.com/jmcnamara/XlsxWriter/issues/40>`_ reported by Simon
+ Breuss.
+
+* Fix for harmless Python 3 installation warning. Issue
+ `#41 <https://github.com/jmcnamara/XlsxWriter/issues/41>`_ reported by James
+ Reeves.
+
+
+Release 0.3.9 - August 24 2013
+------------------------------
+
+* Added fix for minor issue with :func:`insert_image` for images that extend
+ over several cells.
+
+* Added fix to ensure formula calculation on load regardless of Excel version.
+
+
+Release 0.3.8 - August 23 2013
+------------------------------
+
+* Added handling for Decimal(), Fraction() and other float types to the
+ :func:`write()` function.
+
+* Added Python 2.5 and Jython support. Thanks to Jonas Diemer for the patch.
+
+
+Release 0.3.7 - August 16 2013
+------------------------------
+
+* Added :func:`write_boolean()` function to write Excel boolean values. Feature request
+ `#37 <https://github.com/jmcnamara/XlsxWriter/issues/37>`_. Also added
+ explicit handling of Python bool values to the :func:`write()` function.
+
+* Changed :func:`Workbook` constructor option
+ ``strings_to_numbers`` default option to False so that there is no implicit
+ conversion of numbers in strings to numbers. The previous behavior can be
+ obtained by setting the constructor option to True.
+ **Note** This is a backward incompatibility.
+
+
+Release 0.3.6 - July 26 2013
+-----------------------------
+
+* Simplified import based on a suggestion from John Yeung. Feature request
+ `#26 <https://github.com/jmcnamara/XlsxWriter/issues/26>`_.
+
+* Fix for NAN/INF converted to invalid numbers in write(). Issue
+ `#30 <https://github.com/jmcnamara/XlsxWriter/issues/30>`_.
+
+* Added :func:`Workbook` constructor option ``strings_to_numbers`` to
+ override default conversion of number strings to numbers in write().
+
+* Added :func:`Workbook` constructor option ``default_date_format`` to
+ allow a default date format string to be set. Feature request
+ `#5 <https://github.com/jmcnamara/XlsxWriter/issues/5>`_.
+
+
+Release 0.3.5 - June 28 2013
+-----------------------------
+
+* Reverted back to using codecs for file encoding (versions <= 0.3.1) to avoid
+ numerous UTF-8 issues in Python2/3.
+
+
+Release 0.3.4 - June 27 2013
+-----------------------------
+
+* Added Chart line smoothing option. Thanks to Dieter Vandenbussche.
+
+* Added Http Server example (:ref:`ex_http_server`). Thanks to
+ Alexander Afanasiev.
+
+* Fixed inaccurate column width calculation. Closes
+ `#27 <https://github.com/jmcnamara/XlsxWriter/issues/27>`_. Thanks to
+ John Yeung.
+
+* Added chart axis font rotation.
+
+
+Release 0.3.3 - June 10 2013
+-----------------------------
+
+* Minor packaging fixes
+ `#14 <https://github.com/jmcnamara/XlsxWriter/issues/14>`_ and
+ `#19 <https://github.com/jmcnamara/XlsxWriter/issues/19>`_.
+
+* Fixed explicit UTF-8 file encoding for Python 3.
+ PR from Alexandr Shadchin,
+ `#15 <https://github.com/jmcnamara/XlsxWriter/issues/15>`_.
+
+* Fixed invalid string formatting resulted in misleading stack trace.
+ PR from Andrei Korostelev,
+ `#21 <https://github.com/jmcnamara/XlsxWriter/issues/21>`_.
+
+
+Release 0.3.2 - May 1 2013
+-----------------------------
+
+* Speed optimizations. The module is now 10-15% faster on average.
+
+
+Release 0.3.1 - April 27 2013
+-----------------------------
+
+* Added chart support. See the :ref:`chart_class`, :ref:`working_with_charts`
+ and :ref:`chart_examples`.
+
+
+Release 0.3.0 - April 7 2013
+-----------------------------
+
+* Added worksheet sparklines. See :ref:`sparklines`, :ref:`ex_sparklines1` and
+ :ref:`ex_sparklines2`
+
+
+Release 0.2.9 - April 7 2013
+-----------------------------
+
+* Added worksheet tables. See :ref:`tables` and :ref:`ex_tables`.
+
+* Tested with the new Python stable releases 2.7.4 and 3.3.1. All tests now
+ pass in the following versions:
+
+ * Python 2.6
+ * Python 2.7.2
+ * Python 2.7.3
+ * Python 2.7.4
+ * Python 3.1
+ * Python 3.2
+ * Python 3.3.0
+ * Python 3.3.1
+
+* There are now over 700 unit tests including more than 170 tests that
+ compare against the output of Excel.
+
+
+Release 0.2.8 - April 4 2013
+-----------------------------
+
+* Added worksheet outlines and grouping. See :ref:`outlines`.
+
+
+Release 0.2.7 - April 3 2013
+-----------------------------
+
+* Added :func:`set_default_row` method. See :ref:`ex_hide_row_col`.
+
+* Added hide_row_col.py, hide_sheet.py and text_indent.py examples.
+
+
+Release 0.2.6 - April 1 2013
+-----------------------------
+
+* Added :func:`freeze_panes` and :func:`split_panes` methods.
+ See :ref:`ex_panes` .
+
+* Added :func:`set_selection` method to select worksheet cell or range of
+ cells.
+
+
+Release 0.2.5 - April 1 2013
+-----------------------------
+
+* Added additional :func:`Workbook` parameters ``'tmpdir'`` and
+ ``'date_1904'``.
+
+
+Release 0.2.4 - March 31 2013
+-----------------------------
+
+* Added :func:`Workbook` ``'constant_memory'`` constructor property to
+ minimize memory usage when writing large files. See :ref:`memory_perf`
+ for more details.
+
+* Fixed bug with handling of UTF-8 strings in worksheet names (and probably
+ some other places as well). Reported by Josh English.
+
+* Fixed bug where temporary directory used to create xlsx files wasn't
+ cleaned up after program close.
+
+
+Release 0.2.3 - March 27 2013
+-----------------------------
+
+* Fixed bug that was killing performance for medium sized files. The module
+ is now 10x faster than previous versions. Reported by John Yeung.
+
+
+Release 0.2.2 - March 27 2013
+-----------------------------
+
+* Added worksheet data validation options. See the :func:`data_validation`
+ method, :ref:`working_with_data_validation` and :ref:`ex_data_valid`.
+
+* There are now over 600 unit tests including more than 130 tests that
+ compare against the output of Excel.
+
+
+Release 0.2.1 - March 25 2013
+-----------------------------
+
+* Added support for datetime.datetime, datetime.date and datetime.time
+ to the :func:`write_datetime` method. GitHub issue
+ `#3 <https://github.com/jmcnamara/XlsxWriter/issues/3>`_.
+ Thanks to Eduardo (eazb) and Josh English for the prompt.
+
+
+Release 0.2.0 - March 24 2013
+-----------------------------
+
+* Added conditional formatting. See the :func:`conditional_format` method,
+ :ref:`working_with_conditional_formats` and :ref:`ex_cond_format`.
+
+
+Release 0.1.9 - March 19 2013
+-----------------------------
+
+* Added Python 2.6 support. All tests now pass in the following versions:
+
+ * Python 2.6
+ * Python 2.7.2
+ * Python 2.7.3
+ * Python 3.1
+ * Python 3.2
+ * Python 3.3.0
+
+
+Release 0.1.8 - March 18 2013
+-----------------------------
+
+* Fixed Python 3 support.
+
+
+Release 0.1.7 - March 18 2013
+-----------------------------
+
+* Added the option to write cell comments to a worksheet. See
+ :func:`write_comment` and :ref:`cell_comments`.
+
+
+Release 0.1.6 - March 17 2013
+-----------------------------
+
+* Added :func:`insert_image` worksheet method to support inserting PNG and
+ JPEG images into a worksheet. See also the example program
+ :ref:`ex_insert_image`.
+
+* There are now over 500 unit tests including more than 100 tests that
+ compare against the output of Excel.
+
+
+Release 0.1.5 - March 10 2013
+-----------------------------
+
+* Added the :func:`write_rich_string` worksheet method to allow writing of
+ text with multiple formats to a cell. Also added example program:
+ :ref:`ex_rich_strings`.
+
+* Added the :func:`hide` worksheet method to hide worksheets.
+
+* Added the :func:`set_first_sheet()` worksheet method.
+
+
+Release 0.1.4 - March 8 2013
+----------------------------
+
+* Added the :func:`protect` worksheet method to allow protection of cells
+ from editing. Also added example program: :ref:`ex_protection`.
+
+
+Release 0.1.3 - March 7 2013
+----------------------------
+
+* Added worksheet methods:
+
+ * :func:`set_zoom` for setting worksheet zoom levels.
+ * :func:`right_to_left` for middle eastern versions of Excel.
+ * :func:`hide_zero` for hiding zero values in cells.
+ * :func:`set_tab_color` for setting the worksheet tab color.
+
+
+Release 0.1.2 - March 6 2013
+----------------------------
+
+* Added autofilters. See :ref:`working_with_autofilters` for more details.
+
+* Added the :func:`write_row` and :func:`write_column` worksheet methods.
+
+
+Release 0.1.1 - March 3 2013
+----------------------------
+
+* Added the :func:`write_url` worksheet method for writing hyperlinks to
+ a worksheet.
+
+
+Release 0.1.0 - February 28 2013
+--------------------------------
+
+* Added the :func:`set_properties` workbook method for setting document
+ properties.
+
+* Added several new examples programs with documentation. The examples now
+ include:
+
+ * array_formula.py
+ * cell_indentation.py
+ * datetimes.py
+ * defined_name.py
+ * demo.py
+ * doc_properties.py
+ * headers_footers.py
+ * hello_world.py
+ * merge1.py
+ * tutorial1.py
+ * tutorial2.py
+ * tutorial3.py
+ * unicode_polish_utf8.py
+ * unicode_shift_jis.py
+
+Release 0.0.9 - February 27 2013
+--------------------------------
+
+* Added the :func:`define_name` method to create defined names and ranges
+ in a workbook or worksheet.
+
+* Added the :func:`worksheets` method as an accessor for the worksheets in a
+ workbook.
+
+
+Release 0.0.8 - February 26 2013
+--------------------------------
+
+* Added the :func:`merge_range` method to merge worksheet cells.
+
+
+Release 0.0.7 - February 25 2013
+--------------------------------
+
+* Added final page setup methods to complete the page setup section.
+
+ * :func:`print_area`
+ * :func:`fit_to_pages`
+ * :func:`set_start_page`
+ * :func:`set_print_scale`
+ * :func:`set_h_pagebreaks`
+ * :func:`set_v_pagebreaks`
+
+
+Release 0.0.6 - February 22 2013
+--------------------------------
+
+* Added page setup method.
+
+ * :func:`print_row_col_headers`
+
+
+Release 0.0.5 - February 21 2013
+--------------------------------
+
+* Added page setup methods.
+
+ * :func:`repeat_rows`
+ * :func:`repeat_columns`
+
+
+Release 0.0.4 - February 20 2013
+--------------------------------
+
+* Added Python 3 support with help from John Evans. Tested with:
+
+ * Python-2.7.2
+ * Python-2.7.3
+ * Python-3.2
+ * Python-3.3.0
+
+* Added page setup methods.
+
+ * :func:`center_horizontally`
+ * :func:`center_vertically`
+ * :func:`set_header`
+ * :func:`set_footer`
+ * :func:`hide_gridlines`
+
+
+Release 0.0.3 - February 19 2013
+--------------------------------
+
+* Added page setup method.
+
+ * :func:`set_margins`
+
+
+Release 0.0.2 - February 18 2013
+--------------------------------
+
+* Added page setup methods.
+
+ * :func:`set_landscape`
+ * :func:`set_portrait`
+ * :func:`set_page_view`
+ * :func:`set_paper`
+ * :func:`print_across`
+
+
+Release 0.0.1 - February 17 2013
+--------------------------------
+
+* First public release.
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..db4d10f
--- /dev/null
+++ b/ISSUE_TEMPLATE.md
@@ -0,0 +1,63 @@
+# Opening XlsxWriter Issues
+
+## Asking questions about using XlsxWriter
+
+General questions on how to do something with the XlsxWriter module should be
+asked on [StackOverflow](http://stackoverflow.com/questions/tagged/xlsxwriter).
+Add the ``xlsxwriter`` tag to the question. This has a better chance of
+getting several answers and also helps others who might have similar questions
+in the future.
+
+
+See below for information on adding a Bug Report or Feature Request.
+
+
+## Reporting Bugs
+
+Modify the following to suit.
+
+
+Title: Issue with SOMETHING
+
+Hi,
+
+I am using XlsxWriter to do SOMETHING but it appears to do SOMETHING ELSE.
+
+I am using Python version X.Y.Z and XlsxWriter x.y.z and Excel version X.
+
+Here is some code that demonstrates the problem:
+
+```python
+
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('hello.xlsx')
+worksheet = workbook.add_worksheet()
+
+worksheet.write('A1', 'Hello world')
+
+workbook.close()
+
+```
+
+Notes:
+
+1. Ensure that the example code can be run to generate a file that
+ demonstrates the issue.
+2. Only include code that relates to the issue.
+3. Remove non-relevant text from this template.
+4. If you are seeing an issue in LibreOffice, OpenOffice or another third
+ party application, also test the output with a version of Excel.
+5. You can get the required version numbers as follows:
+
+ python --version
+ python -c 'import xlsxwriter; print(xlsxwriter.__version__)'
+
+
+## Feature requests
+
+Add `Feature request:` to the title.
+
+In the comment section describe the feature that you would like to be added.
+
+If you are currently using a workaround you can show that workaround.
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..d8b4b37
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,26 @@
+Copyright (c) 2013, John McNamara <jmcnamara at cpan.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of the FreeBSD Project.
\ No newline at end of file
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..76e5f64
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,9 @@
+include README.rst
+include Changes
+include LICENSE.txt
+include xlsxwriter/*.py
+include examples/*.py
+include examples/*.txt
+include examples/*.bin
+include docs/readme.html
+recursive-include docs/_static *
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6e2b9ca
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,78 @@
+#
+# Simple Makefile for the XlsxWriter project.
+#
+
+.PHONY: docs
+
+docs doc:
+ @make -C dev/docs html
+
+pdf:
+ @make -C dev/docs latexpdf
+
+clean:
+ @make -C dev/docs clean
+
+alldocs: clean docs pdf
+ @cp -r dev/docs/build/html docs
+ @cp -r dev/docs/build/latex/XlsxWriter.pdf docs
+
+pdf_release: pdf
+ @cp -r dev/docs/build/latex/XlsxWriter.pdf docs
+
+install:
+ @python setup.py install
+ @rm -rf build
+
+test:
+ @python -m unittest discover
+
+# Test with stable Python 2/3 releases.
+testpythons:
+ @echo "Testing with Python 2.7.4:"
+ @~/.pythonbrew/pythons/Python-2.7.4/bin/python -m unittest discover
+ @echo "Testing with Python 3.4.1:"
+ @~/.pythonbrew/pythons/Python-3.4.1/bin/python -m unittest discover
+
+# Test with all stable Python 2/3 releases.
+testpythonsall:
+ @echo "Testing with Python 2.5.6:"
+ @~/.pythonbrew/pythons/Python-2.5.6/bin/py.test -q
+ @echo "Testing with Python 2.6.8:"
+ @~/.pythonbrew/pythons/Python-2.6.8/bin/py.test -q
+ @echo "Testing with Python 2.7.4:"
+ @~/.pythonbrew/pythons/Python-2.7.4/bin/py.test -q
+ @echo "Testing with Python 3.1.5:"
+ @~/.pythonbrew/pythons/Python-3.1.5/bin/py.test -q
+ @echo "Testing with Python 3.2.5:"
+ @~/.pythonbrew/pythons/Python-3.2.5/bin/py.test -q
+ @echo "Testing with Python 3.3.2:"
+ @~/.pythonbrew/pythons/Python-3.3.2/bin/py.test -q
+ @echo "Testing with Python 3.4.1:"
+ @~/.pythonbrew/pythons/Python-3.4.1/bin/py.test -q
+ @echo "Testing with Python 3.5.0:"
+ @~/.pythonbrew/pythons/Python-3.5.0/bin/py.test -q
+
+testpep8:
+ @ls -1 xlsxwriter/*.py | egrep -v "theme|compat|__init__" | xargs flake8
+ @pep8 --ignore=E501 xlsxwriter/theme.py
+ @pep8 --ignore=E501 xlsxwriter/compat_collections.py
+ @find xlsxwriter/test -name \*.py | xargs pep8 --ignore=E501
+
+spellcheck:
+ @for f in dev/docs/source/*.rst; do aspell --lang=en_US --check $$f; done
+ @for f in *.md; do aspell --lang=en_US --check $$f; done
+ @for f in xlsxwriter/*.py; do aspell --lang=en_US --check $$f; done
+ @for f in examples/*.py; do aspell --lang=en_US --check $$f; done
+ @aspell --lang=en_US --check Changes
+
+releasecheck:
+ @dev/release/release_check.sh
+
+release: releasecheck
+ @git push origin master
+ @git push --tags
+ @python setup.py sdist bdist_wheel upload --repository https://pypi.python.org/pypi
+ @curl -X POST http://readthedocs.org/build/6277
+ @rm -rf build
+ @rm -rf XlsxWriter.egg-info/
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..892922f
--- /dev/null
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,85 @@
+
+## Pull Requests and Contributing to XlsxWriter
+
+All patches and pull requests are welcome but must start with an issue tracker.
+
+
+### Getting Started
+
+1. Pull requests and new feature proposals must start with an [issue tracker](https://github.com/jmcnamara/XlsxWriter/issues). This serves as the focal point for the design discussion.
+2. Describe what you plan to do. If there are API changes or additions add some pseudo-code to demonstrate them.
+3. Fork the repository.
+4. Run all the tests to make sure the current code work on your system using `make test`.
+5. Create a feature branch for your new feature.
+6. Enable Travis CI on your fork, see below.
+
+
+### Enabling Travis CI via your GitHub account
+
+Travis CI is a free Continuous Integration service that will test any code you push to GitHub with various versions of Python 2 and 3, and PyPy.
+
+See the [Travis CI Getting Started](http://about.travis-ci.org/docs/user/getting-started/) instructions.
+
+Note there is already a `.travis.yml` file in the XlsxWriter repo so that doesn't need to be created.
+
+
+### Writing Tests
+
+This is the most important step. XlsxWriter has over 1000 tests and a 2:1 test to code ratio. Patches and pull requests for anything other than minor fixes or typos will not be merged without tests.
+
+Use the existing tests in `XlsxWriter/xlsxwriter/test/` as examples.
+
+Ideally, new features should be accompanied by tests that compare XlsxWriter output against actual Excel 2007 files. See the `XlsxWriter/xlsxwriter/test/comparison` test files for examples. If you don't have access to Excel 2007 I can help you create input files for test cases.
+
+Tests should use the standard [unittest](http://docs.python.org/2/library/unittest.html) Python module.
+
+
+### Code Style
+
+Follow the general style of the surrounding code and format it to the [PEP8](http://www.python.org/dev/peps/pep-0008/) coding standards.
+
+Tests should conform to `PEP8` but can ignore `E501` for long lines to allow the inclusion of Excel XML in tests.
+
+There is a make target that will verify the source and test files:
+
+ make testpep8
+
+
+### Running tests
+
+As a minimum, tests should be run using Python 2.7 and Python 3.5.
+
+
+ make test
+ # or
+ py.test
+
+I use [pythonbrew](https://github.com/utahta/pythonbrew) and [Tox](https://tox.readthedocs.io/en/latest/) to test with a variety of Python versions. See the Makefile for example test targets. A `tox.ini` file is already configured.
+
+When you push your changes they will also be tested using [Travis CI](https://travis-ci.org/jmcnamara/XlsxWriter/) as explained above.
+
+
+### Documentation
+
+If your feature requires it then write some [RST](http://docutils.sourceforge.net/rst.html) documentation in [Sphinx](http://sphinx-doc.org) format or add to the existing documentation.
+
+The docs, in `dev/docs/source` can be built in Html format using:
+
+ make docs
+
+
+### Example programs
+
+If applicable add an example program to the `examples` directory.
+
+
+### Copyright and License
+
+Copyright remains with the original author. Do not include additional copyright claims or Licensing requirements. GitHub and the `git` repository will record your contribution an it will be acknowledged in the Changes file.
+
+
+### Submitting the Pull Request
+
+If your change involves several incremental `git` commits then `rebase` or `squash` them onto another branch so that the Pull Request is a single commit or a small number of logical commits.
+
+Push your changes to GitHub and submit the Pull Request with a hash link to the to the Issue tracker that was opened above.
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..e99d3f9
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,66 @@
+XlsxWriter
+==========
+
+**XlsxWriter** is a Python module for writing files in the Excel 2007+ XLSX
+file format.
+
+XlsxWriter can be used to write text, numbers, formulas and hyperlinks to
+multiple worksheets and it supports features such as formatting and many more,
+including:
+
+* 100% compatible Excel XLSX files.
+* Full formatting.
+* Merged cells.
+* Defined names.
+* Charts.
+* Autofilters.
+* Data validation and drop down lists.
+* Conditional formatting.
+* Worksheet PNG/JPEG images.
+* Rich multi-format strings.
+* Cell comments.
+* Integration with Pandas.
+* Textboxes.
+* Memory optimization mode for writing large files.
+
+It supports Python 2.5, 2.6, 2.7, 3.1, 3.2, 3.3, 3.4, 3.5, Jython and PyPy and
+uses standard libraries only.
+
+Here is a simple example:
+
+.. code-block:: python
+
+ import xlsxwriter
+
+
+ # Create an new Excel file and add a worksheet.
+ workbook = xlsxwriter.Workbook('demo.xlsx')
+ worksheet = workbook.add_worksheet()
+
+ # Widen the first column to make the text clearer.
+ worksheet.set_column('A:A', 20)
+
+ # Add a bold format to use to highlight cells.
+ bold = workbook.add_format({'bold': True})
+
+ # Write some simple text.
+ worksheet.write('A1', 'Hello')
+
+ # Text with formatting.
+ worksheet.write('A2', 'World', bold)
+
+ # Write some numbers, with row/column notation.
+ worksheet.write(2, 0, 123)
+ worksheet.write(3, 0, 123.456)
+
+ # Insert an image.
+ worksheet.insert_image('B5', 'logo.png')
+
+ workbook.close()
+
+.. image:: https://raw.github.com/jmcnamara/XlsxWriter/master/dev/docs/source/_images/demo.png
+
+See the full documentation at: https://xlsxwriter.readthedocs.io
+
+Release notes: https://xlsxwriter.readthedocs.io/changes.html
+
diff --git a/conftest.py b/conftest.py
new file mode 100644
index 0000000..42de580
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1,36 @@
+# Config script for py.test.
+import sys
+
+
+collect_ignore = ['setup.py']
+
+# Tests to ignore/skip in Python 2.5/Jython.
+py25_ignore = [
+
+ # 'f' not supported in'%Y-%m-%dT%H:%M:%S.%f'
+ 'xlsxwriter/test/worksheet/test_date_time_01.py',
+ 'xlsxwriter/test/worksheet/test_date_time_02.py',
+ 'xlsxwriter/test/worksheet/test_date_time_03.py',
+
+ # No fractions or decimal.
+ 'xlsxwriter/test/comparison/test_types03.py',
+
+ # No unicode_literals.
+ 'xlsxwriter/test/comparison/test_chart_axis25.py',
+ 'xlsxwriter/test/comparison/test_utf8_01.py',
+ 'xlsxwriter/test/comparison/test_utf8_03.py',
+ 'xlsxwriter/test/comparison/test_utf8_04.py',
+ 'xlsxwriter/test/comparison/test_utf8_05.py',
+ 'xlsxwriter/test/comparison/test_utf8_06.py',
+ 'xlsxwriter/test/comparison/test_utf8_07.py',
+ 'xlsxwriter/test/comparison/test_utf8_08.py',
+ 'xlsxwriter/test/comparison/test_utf8_09.py',
+ 'xlsxwriter/test/comparison/test_utf8_10.py',
+ 'xlsxwriter/test/comparison/test_utf8_11.py',
+ 'xlsxwriter/test/comparison/test_properties05.py',
+ 'xlsxwriter/test/comparison/test_defined_name04.py',
+ 'xlsxwriter/test/comparison/test_data_validation07.py',
+ ]
+
+if sys.version_info < (2, 6, 0):
+ collect_ignore.extend(py25_ignore)
diff --git a/dev/Readme.txt b/dev/Readme.txt
new file mode 100644
index 0000000..f296c56
--- /dev/null
+++ b/dev/Readme.txt
@@ -0,0 +1,6 @@
+
+This directory contains files that are useful to the library developer.
+
+It contains the source for the documentation, some performance testing
+scripts and some release utilities.
+
diff --git a/dev/docs/Makefile b/dev/docs/Makefile
new file mode 100644
index 0000000..b0b8d7f
--- /dev/null
+++ b/dev/docs/Makefile
@@ -0,0 +1,152 @@
+# 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
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> 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 " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @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/XlsxWriter.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/XlsxWriter.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/XlsxWriter"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/XlsxWriter"
+ @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."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @perl ../release/modify_latex.pl $(BUILDDIR)/latex/XlsxWriter.tex
+ @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."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+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/dev/docs/make.bat b/dev/docs/make.bat
new file mode 100644
index 0000000..cc255b8
--- /dev/null
+++ b/dev/docs/make.bat
@@ -0,0 +1,190 @@
+ at ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
+set I18NSPHINXOPTS=%SPHINXOPTS% source
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> 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. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ 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
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ 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
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\XlsxWriter.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\XlsxWriter.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ 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
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+:end
diff --git a/dev/docs/source/_images/array_formula.png b/dev/docs/source/_images/array_formula.png
new file mode 100644
index 0000000..d1793a7
Binary files /dev/null and b/dev/docs/source/_images/array_formula.png differ
diff --git a/dev/docs/source/_images/autofilter1.png b/dev/docs/source/_images/autofilter1.png
new file mode 100644
index 0000000..617f098
Binary files /dev/null and b/dev/docs/source/_images/autofilter1.png differ
diff --git a/dev/docs/source/_images/autofilter2.png b/dev/docs/source/_images/autofilter2.png
new file mode 100644
index 0000000..32ba75e
Binary files /dev/null and b/dev/docs/source/_images/autofilter2.png differ
diff --git a/dev/docs/source/_images/autofilter3.png b/dev/docs/source/_images/autofilter3.png
new file mode 100644
index 0000000..61c93c8
Binary files /dev/null and b/dev/docs/source/_images/autofilter3.png differ
diff --git a/dev/docs/source/_images/chart_area1.png b/dev/docs/source/_images/chart_area1.png
new file mode 100644
index 0000000..a84fbff
Binary files /dev/null and b/dev/docs/source/_images/chart_area1.png differ
diff --git a/dev/docs/source/_images/chart_area2.png b/dev/docs/source/_images/chart_area2.png
new file mode 100644
index 0000000..806f728
Binary files /dev/null and b/dev/docs/source/_images/chart_area2.png differ
diff --git a/dev/docs/source/_images/chart_area3.png b/dev/docs/source/_images/chart_area3.png
new file mode 100644
index 0000000..c14b2ba
Binary files /dev/null and b/dev/docs/source/_images/chart_area3.png differ
diff --git a/dev/docs/source/_images/chart_bar1.png b/dev/docs/source/_images/chart_bar1.png
new file mode 100644
index 0000000..d83886d
Binary files /dev/null and b/dev/docs/source/_images/chart_bar1.png differ
diff --git a/dev/docs/source/_images/chart_bar2.png b/dev/docs/source/_images/chart_bar2.png
new file mode 100644
index 0000000..6fa0bd1
Binary files /dev/null and b/dev/docs/source/_images/chart_bar2.png differ
diff --git a/dev/docs/source/_images/chart_bar3.png b/dev/docs/source/_images/chart_bar3.png
new file mode 100644
index 0000000..1420de0
Binary files /dev/null and b/dev/docs/source/_images/chart_bar3.png differ
diff --git a/dev/docs/source/_images/chart_chartarea.png b/dev/docs/source/_images/chart_chartarea.png
new file mode 100644
index 0000000..b99672c
Binary files /dev/null and b/dev/docs/source/_images/chart_chartarea.png differ
diff --git a/dev/docs/source/_images/chart_clustered.png b/dev/docs/source/_images/chart_clustered.png
new file mode 100644
index 0000000..d1f40e2
Binary files /dev/null and b/dev/docs/source/_images/chart_clustered.png differ
diff --git a/dev/docs/source/_images/chart_column1.png b/dev/docs/source/_images/chart_column1.png
new file mode 100644
index 0000000..c892b04
Binary files /dev/null and b/dev/docs/source/_images/chart_column1.png differ
diff --git a/dev/docs/source/_images/chart_column2.png b/dev/docs/source/_images/chart_column2.png
new file mode 100644
index 0000000..5bcc6a8
Binary files /dev/null and b/dev/docs/source/_images/chart_column2.png differ
diff --git a/dev/docs/source/_images/chart_column3.png b/dev/docs/source/_images/chart_column3.png
new file mode 100644
index 0000000..6e7373d
Binary files /dev/null and b/dev/docs/source/_images/chart_column3.png differ
diff --git a/dev/docs/source/_images/chart_combined1.png b/dev/docs/source/_images/chart_combined1.png
new file mode 100644
index 0000000..90ec166
Binary files /dev/null and b/dev/docs/source/_images/chart_combined1.png differ
diff --git a/dev/docs/source/_images/chart_combined2.png b/dev/docs/source/_images/chart_combined2.png
new file mode 100644
index 0000000..355e6b0
Binary files /dev/null and b/dev/docs/source/_images/chart_combined2.png differ
diff --git a/dev/docs/source/_images/chart_data_labels1.png b/dev/docs/source/_images/chart_data_labels1.png
new file mode 100644
index 0000000..db3483d
Binary files /dev/null and b/dev/docs/source/_images/chart_data_labels1.png differ
diff --git a/dev/docs/source/_images/chart_data_table1.png b/dev/docs/source/_images/chart_data_table1.png
new file mode 100644
index 0000000..a534fb6
Binary files /dev/null and b/dev/docs/source/_images/chart_data_table1.png differ
diff --git a/dev/docs/source/_images/chart_data_table2.png b/dev/docs/source/_images/chart_data_table2.png
new file mode 100644
index 0000000..9bd9fcf
Binary files /dev/null and b/dev/docs/source/_images/chart_data_table2.png differ
diff --git a/dev/docs/source/_images/chart_data_tools1.png b/dev/docs/source/_images/chart_data_tools1.png
new file mode 100644
index 0000000..4282cec
Binary files /dev/null and b/dev/docs/source/_images/chart_data_tools1.png differ
diff --git a/dev/docs/source/_images/chart_data_tools2.png b/dev/docs/source/_images/chart_data_tools2.png
new file mode 100644
index 0000000..f81efec
Binary files /dev/null and b/dev/docs/source/_images/chart_data_tools2.png differ
diff --git a/dev/docs/source/_images/chart_data_tools3.png b/dev/docs/source/_images/chart_data_tools3.png
new file mode 100644
index 0000000..a1ba837
Binary files /dev/null and b/dev/docs/source/_images/chart_data_tools3.png differ
diff --git a/dev/docs/source/_images/chart_data_tools4.png b/dev/docs/source/_images/chart_data_tools4.png
new file mode 100644
index 0000000..023502b
Binary files /dev/null and b/dev/docs/source/_images/chart_data_tools4.png differ
diff --git a/dev/docs/source/_images/chart_data_tools5.png b/dev/docs/source/_images/chart_data_tools5.png
new file mode 100644
index 0000000..8228535
Binary files /dev/null and b/dev/docs/source/_images/chart_data_tools5.png differ
diff --git a/dev/docs/source/_images/chart_data_tools6.png b/dev/docs/source/_images/chart_data_tools6.png
new file mode 100644
index 0000000..cbc1683
Binary files /dev/null and b/dev/docs/source/_images/chart_data_tools6.png differ
diff --git a/dev/docs/source/_images/chart_date_axis.png b/dev/docs/source/_images/chart_date_axis.png
new file mode 100644
index 0000000..1f6ab66
Binary files /dev/null and b/dev/docs/source/_images/chart_date_axis.png differ
diff --git a/dev/docs/source/_images/chart_display_units.png b/dev/docs/source/_images/chart_display_units.png
new file mode 100644
index 0000000..d6146cc
Binary files /dev/null and b/dev/docs/source/_images/chart_display_units.png differ
diff --git a/dev/docs/source/_images/chart_doughnut1.png b/dev/docs/source/_images/chart_doughnut1.png
new file mode 100644
index 0000000..74c5011
Binary files /dev/null and b/dev/docs/source/_images/chart_doughnut1.png differ
diff --git a/dev/docs/source/_images/chart_doughnut2.png b/dev/docs/source/_images/chart_doughnut2.png
new file mode 100644
index 0000000..5f82bdd
Binary files /dev/null and b/dev/docs/source/_images/chart_doughnut2.png differ
diff --git a/dev/docs/source/_images/chart_drop_lines.png b/dev/docs/source/_images/chart_drop_lines.png
new file mode 100644
index 0000000..f77f51b
Binary files /dev/null and b/dev/docs/source/_images/chart_drop_lines.png differ
diff --git a/dev/docs/source/_images/chart_error_bars1.png b/dev/docs/source/_images/chart_error_bars1.png
new file mode 100644
index 0000000..84e3f07
Binary files /dev/null and b/dev/docs/source/_images/chart_error_bars1.png differ
diff --git a/dev/docs/source/_images/chart_error_bars2.png b/dev/docs/source/_images/chart_error_bars2.png
new file mode 100644
index 0000000..f1d9c36
Binary files /dev/null and b/dev/docs/source/_images/chart_error_bars2.png differ
diff --git a/dev/docs/source/_images/chart_fill1.png b/dev/docs/source/_images/chart_fill1.png
new file mode 100644
index 0000000..9007c43
Binary files /dev/null and b/dev/docs/source/_images/chart_fill1.png differ
diff --git a/dev/docs/source/_images/chart_fill2.png b/dev/docs/source/_images/chart_fill2.png
new file mode 100644
index 0000000..357c5f3
Binary files /dev/null and b/dev/docs/source/_images/chart_fill2.png differ
diff --git a/dev/docs/source/_images/chart_font1.png b/dev/docs/source/_images/chart_font1.png
new file mode 100644
index 0000000..e17124b
Binary files /dev/null and b/dev/docs/source/_images/chart_font1.png differ
diff --git a/dev/docs/source/_images/chart_formatting1.png b/dev/docs/source/_images/chart_formatting1.png
new file mode 100644
index 0000000..b60057d
Binary files /dev/null and b/dev/docs/source/_images/chart_formatting1.png differ
diff --git a/dev/docs/source/_images/chart_formatting2.png b/dev/docs/source/_images/chart_formatting2.png
new file mode 100644
index 0000000..21f1902
Binary files /dev/null and b/dev/docs/source/_images/chart_formatting2.png differ
diff --git a/dev/docs/source/_images/chart_formatting3.png b/dev/docs/source/_images/chart_formatting3.png
new file mode 100644
index 0000000..31fe3f9
Binary files /dev/null and b/dev/docs/source/_images/chart_formatting3.png differ
diff --git a/dev/docs/source/_images/chart_formatting4.png b/dev/docs/source/_images/chart_formatting4.png
new file mode 100644
index 0000000..c45d678
Binary files /dev/null and b/dev/docs/source/_images/chart_formatting4.png differ
diff --git a/dev/docs/source/_images/chart_formatting5.png b/dev/docs/source/_images/chart_formatting5.png
new file mode 100644
index 0000000..000645b
Binary files /dev/null and b/dev/docs/source/_images/chart_formatting5.png differ
diff --git a/dev/docs/source/_images/chart_gradient.png b/dev/docs/source/_images/chart_gradient.png
new file mode 100644
index 0000000..68fc7e4
Binary files /dev/null and b/dev/docs/source/_images/chart_gradient.png differ
diff --git a/dev/docs/source/_images/chart_gridlines.png b/dev/docs/source/_images/chart_gridlines.png
new file mode 100644
index 0000000..d5a590a
Binary files /dev/null and b/dev/docs/source/_images/chart_gridlines.png differ
diff --git a/dev/docs/source/_images/chart_high_low_lines.png b/dev/docs/source/_images/chart_high_low_lines.png
new file mode 100644
index 0000000..e835fe7
Binary files /dev/null and b/dev/docs/source/_images/chart_high_low_lines.png differ
diff --git a/dev/docs/source/_images/chart_layout.png b/dev/docs/source/_images/chart_layout.png
new file mode 100644
index 0000000..8c3ca85
Binary files /dev/null and b/dev/docs/source/_images/chart_layout.png differ
diff --git a/dev/docs/source/_images/chart_legend_bottom.png b/dev/docs/source/_images/chart_legend_bottom.png
new file mode 100644
index 0000000..9d70545
Binary files /dev/null and b/dev/docs/source/_images/chart_legend_bottom.png differ
diff --git a/dev/docs/source/_images/chart_legend_delete.png b/dev/docs/source/_images/chart_legend_delete.png
new file mode 100644
index 0000000..8cf2913
Binary files /dev/null and b/dev/docs/source/_images/chart_legend_delete.png differ
diff --git a/dev/docs/source/_images/chart_legend_none.png b/dev/docs/source/_images/chart_legend_none.png
new file mode 100644
index 0000000..ac08f01
Binary files /dev/null and b/dev/docs/source/_images/chart_legend_none.png differ
diff --git a/dev/docs/source/_images/chart_line1.png b/dev/docs/source/_images/chart_line1.png
new file mode 100644
index 0000000..acdf52b
Binary files /dev/null and b/dev/docs/source/_images/chart_line1.png differ
diff --git a/dev/docs/source/_images/chart_marker1.png b/dev/docs/source/_images/chart_marker1.png
new file mode 100644
index 0000000..f1e44df
Binary files /dev/null and b/dev/docs/source/_images/chart_marker1.png differ
diff --git a/dev/docs/source/_images/chart_marker2.png b/dev/docs/source/_images/chart_marker2.png
new file mode 100644
index 0000000..a52dc84
Binary files /dev/null and b/dev/docs/source/_images/chart_marker2.png differ
diff --git a/dev/docs/source/_images/chart_max_min.png b/dev/docs/source/_images/chart_max_min.png
new file mode 100644
index 0000000..418da87
Binary files /dev/null and b/dev/docs/source/_images/chart_max_min.png differ
diff --git a/dev/docs/source/_images/chart_pareto.png b/dev/docs/source/_images/chart_pareto.png
new file mode 100644
index 0000000..8ce7943
Binary files /dev/null and b/dev/docs/source/_images/chart_pareto.png differ
diff --git a/dev/docs/source/_images/chart_pattern.png b/dev/docs/source/_images/chart_pattern.png
new file mode 100644
index 0000000..1859814
Binary files /dev/null and b/dev/docs/source/_images/chart_pattern.png differ
diff --git a/dev/docs/source/_images/chart_pie1.png b/dev/docs/source/_images/chart_pie1.png
new file mode 100644
index 0000000..0de45c3
Binary files /dev/null and b/dev/docs/source/_images/chart_pie1.png differ
diff --git a/dev/docs/source/_images/chart_pie2.png b/dev/docs/source/_images/chart_pie2.png
new file mode 100644
index 0000000..bfd7cb3
Binary files /dev/null and b/dev/docs/source/_images/chart_pie2.png differ
diff --git a/dev/docs/source/_images/chart_plotarea.png b/dev/docs/source/_images/chart_plotarea.png
new file mode 100644
index 0000000..129bd6d
Binary files /dev/null and b/dev/docs/source/_images/chart_plotarea.png differ
diff --git a/dev/docs/source/_images/chart_points1.png b/dev/docs/source/_images/chart_points1.png
new file mode 100644
index 0000000..e49f4f8
Binary files /dev/null and b/dev/docs/source/_images/chart_points1.png differ
diff --git a/dev/docs/source/_images/chart_radar1.png b/dev/docs/source/_images/chart_radar1.png
new file mode 100644
index 0000000..b5ab0a2
Binary files /dev/null and b/dev/docs/source/_images/chart_radar1.png differ
diff --git a/dev/docs/source/_images/chart_radar2.png b/dev/docs/source/_images/chart_radar2.png
new file mode 100644
index 0000000..91bccf0
Binary files /dev/null and b/dev/docs/source/_images/chart_radar2.png differ
diff --git a/dev/docs/source/_images/chart_radar3.png b/dev/docs/source/_images/chart_radar3.png
new file mode 100644
index 0000000..0b5d4c6
Binary files /dev/null and b/dev/docs/source/_images/chart_radar3.png differ
diff --git a/dev/docs/source/_images/chart_reverse.png b/dev/docs/source/_images/chart_reverse.png
new file mode 100644
index 0000000..0fe265f
Binary files /dev/null and b/dev/docs/source/_images/chart_reverse.png differ
diff --git a/dev/docs/source/_images/chart_scatter1.png b/dev/docs/source/_images/chart_scatter1.png
new file mode 100644
index 0000000..aa52378
Binary files /dev/null and b/dev/docs/source/_images/chart_scatter1.png differ
diff --git a/dev/docs/source/_images/chart_scatter2.png b/dev/docs/source/_images/chart_scatter2.png
new file mode 100644
index 0000000..226a703
Binary files /dev/null and b/dev/docs/source/_images/chart_scatter2.png differ
diff --git a/dev/docs/source/_images/chart_scatter3.png b/dev/docs/source/_images/chart_scatter3.png
new file mode 100644
index 0000000..4e61d0e
Binary files /dev/null and b/dev/docs/source/_images/chart_scatter3.png differ
diff --git a/dev/docs/source/_images/chart_scatter4.png b/dev/docs/source/_images/chart_scatter4.png
new file mode 100644
index 0000000..5d9b53d
Binary files /dev/null and b/dev/docs/source/_images/chart_scatter4.png differ
diff --git a/dev/docs/source/_images/chart_scatter5.png b/dev/docs/source/_images/chart_scatter5.png
new file mode 100644
index 0000000..aaf00cd
Binary files /dev/null and b/dev/docs/source/_images/chart_scatter5.png differ
diff --git a/dev/docs/source/_images/chart_secondary_axis1.png b/dev/docs/source/_images/chart_secondary_axis1.png
new file mode 100644
index 0000000..cde6ddc
Binary files /dev/null and b/dev/docs/source/_images/chart_secondary_axis1.png differ
diff --git a/dev/docs/source/_images/chart_secondary_axis2.png b/dev/docs/source/_images/chart_secondary_axis2.png
new file mode 100644
index 0000000..6c2976e
Binary files /dev/null and b/dev/docs/source/_images/chart_secondary_axis2.png differ
diff --git a/dev/docs/source/_images/chart_simple.png b/dev/docs/source/_images/chart_simple.png
new file mode 100644
index 0000000..a81d23e
Binary files /dev/null and b/dev/docs/source/_images/chart_simple.png differ
diff --git a/dev/docs/source/_images/chart_stock1.png b/dev/docs/source/_images/chart_stock1.png
new file mode 100644
index 0000000..c4b88e4
Binary files /dev/null and b/dev/docs/source/_images/chart_stock1.png differ
diff --git a/dev/docs/source/_images/chart_style.png b/dev/docs/source/_images/chart_style.png
new file mode 100644
index 0000000..8089d82
Binary files /dev/null and b/dev/docs/source/_images/chart_style.png differ
diff --git a/dev/docs/source/_images/chart_styles.png b/dev/docs/source/_images/chart_styles.png
new file mode 100644
index 0000000..68afc01
Binary files /dev/null and b/dev/docs/source/_images/chart_styles.png differ
diff --git a/dev/docs/source/_images/chart_table.png b/dev/docs/source/_images/chart_table.png
new file mode 100644
index 0000000..87f4371
Binary files /dev/null and b/dev/docs/source/_images/chart_table.png differ
diff --git a/dev/docs/source/_images/chart_title.png b/dev/docs/source/_images/chart_title.png
new file mode 100644
index 0000000..9b63841
Binary files /dev/null and b/dev/docs/source/_images/chart_title.png differ
diff --git a/dev/docs/source/_images/chart_trendline1.png b/dev/docs/source/_images/chart_trendline1.png
new file mode 100644
index 0000000..d6073c2
Binary files /dev/null and b/dev/docs/source/_images/chart_trendline1.png differ
diff --git a/dev/docs/source/_images/chart_trendline2.png b/dev/docs/source/_images/chart_trendline2.png
new file mode 100644
index 0000000..c2baf8a
Binary files /dev/null and b/dev/docs/source/_images/chart_trendline2.png differ
diff --git a/dev/docs/source/_images/chart_trendline3.png b/dev/docs/source/_images/chart_trendline3.png
new file mode 100644
index 0000000..8e40f7c
Binary files /dev/null and b/dev/docs/source/_images/chart_trendline3.png differ
diff --git a/dev/docs/source/_images/chart_up_down_bars.png b/dev/docs/source/_images/chart_up_down_bars.png
new file mode 100644
index 0000000..75374be
Binary files /dev/null and b/dev/docs/source/_images/chart_up_down_bars.png differ
diff --git a/dev/docs/source/_images/chart_working.png b/dev/docs/source/_images/chart_working.png
new file mode 100644
index 0000000..497db7e
Binary files /dev/null and b/dev/docs/source/_images/chart_working.png differ
diff --git a/dev/docs/source/_images/chart_x_axis.png b/dev/docs/source/_images/chart_x_axis.png
new file mode 100644
index 0000000..d0c6d03
Binary files /dev/null and b/dev/docs/source/_images/chart_x_axis.png differ
diff --git a/dev/docs/source/_images/chartsheet.png b/dev/docs/source/_images/chartsheet.png
new file mode 100644
index 0000000..def6cfe
Binary files /dev/null and b/dev/docs/source/_images/chartsheet.png differ
diff --git a/dev/docs/source/_images/comments1.png b/dev/docs/source/_images/comments1.png
new file mode 100644
index 0000000..3af636b
Binary files /dev/null and b/dev/docs/source/_images/comments1.png differ
diff --git a/dev/docs/source/_images/comments2.png b/dev/docs/source/_images/comments2.png
new file mode 100644
index 0000000..9fc18b0
Binary files /dev/null and b/dev/docs/source/_images/comments2.png differ
diff --git a/dev/docs/source/_images/conditional_format1.png b/dev/docs/source/_images/conditional_format1.png
new file mode 100644
index 0000000..6d56f73
Binary files /dev/null and b/dev/docs/source/_images/conditional_format1.png differ
diff --git a/dev/docs/source/_images/conditional_format2.png b/dev/docs/source/_images/conditional_format2.png
new file mode 100644
index 0000000..2abd666
Binary files /dev/null and b/dev/docs/source/_images/conditional_format2.png differ
diff --git a/dev/docs/source/_images/conditional_format3.png b/dev/docs/source/_images/conditional_format3.png
new file mode 100644
index 0000000..2b7624c
Binary files /dev/null and b/dev/docs/source/_images/conditional_format3.png differ
diff --git a/dev/docs/source/_images/conditional_format4.png b/dev/docs/source/_images/conditional_format4.png
new file mode 100644
index 0000000..addee53
Binary files /dev/null and b/dev/docs/source/_images/conditional_format4.png differ
diff --git a/dev/docs/source/_images/custom_properties.png b/dev/docs/source/_images/custom_properties.png
new file mode 100644
index 0000000..3ab5553
Binary files /dev/null and b/dev/docs/source/_images/custom_properties.png differ
diff --git a/dev/docs/source/_images/data_validate1.png b/dev/docs/source/_images/data_validate1.png
new file mode 100644
index 0000000..c83c231
Binary files /dev/null and b/dev/docs/source/_images/data_validate1.png differ
diff --git a/dev/docs/source/_images/data_validate2.png b/dev/docs/source/_images/data_validate2.png
new file mode 100644
index 0000000..75d7df4
Binary files /dev/null and b/dev/docs/source/_images/data_validate2.png differ
diff --git a/dev/docs/source/_images/data_validate3.png b/dev/docs/source/_images/data_validate3.png
new file mode 100644
index 0000000..30b15ed
Binary files /dev/null and b/dev/docs/source/_images/data_validate3.png differ
diff --git a/dev/docs/source/_images/defined_name.png b/dev/docs/source/_images/defined_name.png
new file mode 100644
index 0000000..793507e
Binary files /dev/null and b/dev/docs/source/_images/defined_name.png differ
diff --git a/dev/docs/source/_images/demo.png b/dev/docs/source/_images/demo.png
new file mode 100644
index 0000000..b546f77
Binary files /dev/null and b/dev/docs/source/_images/demo.png differ
diff --git a/dev/docs/source/_images/diagonal_border.png b/dev/docs/source/_images/diagonal_border.png
new file mode 100644
index 0000000..cd276fd
Binary files /dev/null and b/dev/docs/source/_images/diagonal_border.png differ
diff --git a/dev/docs/source/_images/doc_properties.png b/dev/docs/source/_images/doc_properties.png
new file mode 100644
index 0000000..40cf7b4
Binary files /dev/null and b/dev/docs/source/_images/doc_properties.png differ
diff --git a/dev/docs/source/_images/format_font_align.png b/dev/docs/source/_images/format_font_align.png
new file mode 100644
index 0000000..a5f88b2
Binary files /dev/null and b/dev/docs/source/_images/format_font_align.png differ
diff --git a/dev/docs/source/_images/format_font_bold.png b/dev/docs/source/_images/format_font_bold.png
new file mode 100644
index 0000000..4343805
Binary files /dev/null and b/dev/docs/source/_images/format_font_bold.png differ
diff --git a/dev/docs/source/_images/format_font_color.png b/dev/docs/source/_images/format_font_color.png
new file mode 100644
index 0000000..e668945
Binary files /dev/null and b/dev/docs/source/_images/format_font_color.png differ
diff --git a/dev/docs/source/_images/format_font_italic.png b/dev/docs/source/_images/format_font_italic.png
new file mode 100644
index 0000000..ce1cb4a
Binary files /dev/null and b/dev/docs/source/_images/format_font_italic.png differ
diff --git a/dev/docs/source/_images/format_font_name.png b/dev/docs/source/_images/format_font_name.png
new file mode 100644
index 0000000..6f2999d
Binary files /dev/null and b/dev/docs/source/_images/format_font_name.png differ
diff --git a/dev/docs/source/_images/format_font_script.png b/dev/docs/source/_images/format_font_script.png
new file mode 100644
index 0000000..87f8c70
Binary files /dev/null and b/dev/docs/source/_images/format_font_script.png differ
diff --git a/dev/docs/source/_images/format_font_size.png b/dev/docs/source/_images/format_font_size.png
new file mode 100644
index 0000000..c485059
Binary files /dev/null and b/dev/docs/source/_images/format_font_size.png differ
diff --git a/dev/docs/source/_images/format_font_strikeout.png b/dev/docs/source/_images/format_font_strikeout.png
new file mode 100644
index 0000000..36328b4
Binary files /dev/null and b/dev/docs/source/_images/format_font_strikeout.png differ
diff --git a/dev/docs/source/_images/format_font_text_rotated.png b/dev/docs/source/_images/format_font_text_rotated.png
new file mode 100644
index 0000000..b5f042f
Binary files /dev/null and b/dev/docs/source/_images/format_font_text_rotated.png differ
diff --git a/dev/docs/source/_images/format_font_text_wrap.png b/dev/docs/source/_images/format_font_text_wrap.png
new file mode 100644
index 0000000..ad5892f
Binary files /dev/null and b/dev/docs/source/_images/format_font_text_wrap.png differ
diff --git a/dev/docs/source/_images/format_font_underlined.png b/dev/docs/source/_images/format_font_underlined.png
new file mode 100644
index 0000000..cf4810d
Binary files /dev/null and b/dev/docs/source/_images/format_font_underlined.png differ
diff --git a/dev/docs/source/_images/formats_intro.png b/dev/docs/source/_images/formats_intro.png
new file mode 100644
index 0000000..916c538
Binary files /dev/null and b/dev/docs/source/_images/formats_intro.png differ
diff --git a/dev/docs/source/_images/formats_num_str.png b/dev/docs/source/_images/formats_num_str.png
new file mode 100644
index 0000000..eae8669
Binary files /dev/null and b/dev/docs/source/_images/formats_num_str.png differ
diff --git a/dev/docs/source/_images/formats_set_bg_color.png b/dev/docs/source/_images/formats_set_bg_color.png
new file mode 100644
index 0000000..fd8e14a
Binary files /dev/null and b/dev/docs/source/_images/formats_set_bg_color.png differ
diff --git a/dev/docs/source/_images/header_image.png b/dev/docs/source/_images/header_image.png
new file mode 100644
index 0000000..eb038f9
Binary files /dev/null and b/dev/docs/source/_images/header_image.png differ
diff --git a/dev/docs/source/_images/headers_footers.png b/dev/docs/source/_images/headers_footers.png
new file mode 100644
index 0000000..d691c09
Binary files /dev/null and b/dev/docs/source/_images/headers_footers.png differ
diff --git a/dev/docs/source/_images/hello01.png b/dev/docs/source/_images/hello01.png
new file mode 100644
index 0000000..93c3ed1
Binary files /dev/null and b/dev/docs/source/_images/hello01.png differ
diff --git a/dev/docs/source/_images/hide_row_col.png b/dev/docs/source/_images/hide_row_col.png
new file mode 100644
index 0000000..15a2ab6
Binary files /dev/null and b/dev/docs/source/_images/hide_row_col.png differ
diff --git a/dev/docs/source/_images/hide_sheet.png b/dev/docs/source/_images/hide_sheet.png
new file mode 100644
index 0000000..074fa39
Binary files /dev/null and b/dev/docs/source/_images/hide_sheet.png differ
diff --git a/dev/docs/source/_images/hyperlink.png b/dev/docs/source/_images/hyperlink.png
new file mode 100644
index 0000000..ff10c2f
Binary files /dev/null and b/dev/docs/source/_images/hyperlink.png differ
diff --git a/dev/docs/source/_images/images.png b/dev/docs/source/_images/images.png
new file mode 100644
index 0000000..37a13ce
Binary files /dev/null and b/dev/docs/source/_images/images.png differ
diff --git a/dev/docs/source/_images/images_bytesio.png b/dev/docs/source/_images/images_bytesio.png
new file mode 100644
index 0000000..f7fc527
Binary files /dev/null and b/dev/docs/source/_images/images_bytesio.png differ
diff --git a/dev/docs/source/_images/insert_image.png b/dev/docs/source/_images/insert_image.png
new file mode 100644
index 0000000..a80c8cb
Binary files /dev/null and b/dev/docs/source/_images/insert_image.png differ
diff --git a/dev/docs/source/_images/logo.png b/dev/docs/source/_images/logo.png
new file mode 100644
index 0000000..8428d76
Binary files /dev/null and b/dev/docs/source/_images/logo.png differ
diff --git a/dev/docs/source/_images/macros.png b/dev/docs/source/_images/macros.png
new file mode 100644
index 0000000..926a669
Binary files /dev/null and b/dev/docs/source/_images/macros.png differ
diff --git a/dev/docs/source/_images/merge1.png b/dev/docs/source/_images/merge1.png
new file mode 100644
index 0000000..a4dadc6
Binary files /dev/null and b/dev/docs/source/_images/merge1.png differ
diff --git a/dev/docs/source/_images/merge_range.png b/dev/docs/source/_images/merge_range.png
new file mode 100644
index 0000000..374097c
Binary files /dev/null and b/dev/docs/source/_images/merge_range.png differ
diff --git a/dev/docs/source/_images/merge_rich.png b/dev/docs/source/_images/merge_rich.png
new file mode 100644
index 0000000..52b42aa
Binary files /dev/null and b/dev/docs/source/_images/merge_rich.png differ
diff --git a/dev/docs/source/_images/outline1.png b/dev/docs/source/_images/outline1.png
new file mode 100644
index 0000000..2a9b6b6
Binary files /dev/null and b/dev/docs/source/_images/outline1.png differ
diff --git a/dev/docs/source/_images/outline2.png b/dev/docs/source/_images/outline2.png
new file mode 100644
index 0000000..d0b45bf
Binary files /dev/null and b/dev/docs/source/_images/outline2.png differ
diff --git a/dev/docs/source/_images/outline3.png b/dev/docs/source/_images/outline3.png
new file mode 100644
index 0000000..3c1cad5
Binary files /dev/null and b/dev/docs/source/_images/outline3.png differ
diff --git a/dev/docs/source/_images/outline4.png b/dev/docs/source/_images/outline4.png
new file mode 100644
index 0000000..7ef99d8
Binary files /dev/null and b/dev/docs/source/_images/outline4.png differ
diff --git a/dev/docs/source/_images/outline5.png b/dev/docs/source/_images/outline5.png
new file mode 100644
index 0000000..14dc3bd
Binary files /dev/null and b/dev/docs/source/_images/outline5.png differ
diff --git a/dev/docs/source/_images/outline6.png b/dev/docs/source/_images/outline6.png
new file mode 100644
index 0000000..3ec43c4
Binary files /dev/null and b/dev/docs/source/_images/outline6.png differ
diff --git a/dev/docs/source/_images/pandas_chart.png b/dev/docs/source/_images/pandas_chart.png
new file mode 100644
index 0000000..9ce3a9e
Binary files /dev/null and b/dev/docs/source/_images/pandas_chart.png differ
diff --git a/dev/docs/source/_images/pandas_chart_columns.png b/dev/docs/source/_images/pandas_chart_columns.png
new file mode 100644
index 0000000..137a96f
Binary files /dev/null and b/dev/docs/source/_images/pandas_chart_columns.png differ
diff --git a/dev/docs/source/_images/pandas_chart_line.png b/dev/docs/source/_images/pandas_chart_line.png
new file mode 100644
index 0000000..33932b3
Binary files /dev/null and b/dev/docs/source/_images/pandas_chart_line.png differ
diff --git a/dev/docs/source/_images/pandas_chart_stock.png b/dev/docs/source/_images/pandas_chart_stock.png
new file mode 100644
index 0000000..d0d0dc6
Binary files /dev/null and b/dev/docs/source/_images/pandas_chart_stock.png differ
diff --git a/dev/docs/source/_images/pandas_column_formats.png b/dev/docs/source/_images/pandas_column_formats.png
new file mode 100644
index 0000000..e62489e
Binary files /dev/null and b/dev/docs/source/_images/pandas_column_formats.png differ
diff --git a/dev/docs/source/_images/pandas_conditional.png b/dev/docs/source/_images/pandas_conditional.png
new file mode 100644
index 0000000..d9e0a0d
Binary files /dev/null and b/dev/docs/source/_images/pandas_conditional.png differ
diff --git a/dev/docs/source/_images/pandas_datetime.png b/dev/docs/source/_images/pandas_datetime.png
new file mode 100644
index 0000000..36aa766
Binary files /dev/null and b/dev/docs/source/_images/pandas_datetime.png differ
diff --git a/dev/docs/source/_images/pandas_multiple.png b/dev/docs/source/_images/pandas_multiple.png
new file mode 100644
index 0000000..7193dcc
Binary files /dev/null and b/dev/docs/source/_images/pandas_multiple.png differ
diff --git a/dev/docs/source/_images/pandas_positioning.png b/dev/docs/source/_images/pandas_positioning.png
new file mode 100644
index 0000000..207aa4c
Binary files /dev/null and b/dev/docs/source/_images/pandas_positioning.png differ
diff --git a/dev/docs/source/_images/pandas_simple.png b/dev/docs/source/_images/pandas_simple.png
new file mode 100644
index 0000000..78c0921
Binary files /dev/null and b/dev/docs/source/_images/pandas_simple.png differ
diff --git a/dev/docs/source/_images/panes.png b/dev/docs/source/_images/panes.png
new file mode 100644
index 0000000..be23834
Binary files /dev/null and b/dev/docs/source/_images/panes.png differ
diff --git a/dev/docs/source/_images/rich_strings.png b/dev/docs/source/_images/rich_strings.png
new file mode 100644
index 0000000..82ef033
Binary files /dev/null and b/dev/docs/source/_images/rich_strings.png differ
diff --git a/dev/docs/source/_images/rich_strings_small.png b/dev/docs/source/_images/rich_strings_small.png
new file mode 100644
index 0000000..10babf9
Binary files /dev/null and b/dev/docs/source/_images/rich_strings_small.png differ
diff --git a/dev/docs/source/_images/sparklines1.png b/dev/docs/source/_images/sparklines1.png
new file mode 100644
index 0000000..e1d4337
Binary files /dev/null and b/dev/docs/source/_images/sparklines1.png differ
diff --git a/dev/docs/source/_images/sparklines2.png b/dev/docs/source/_images/sparklines2.png
new file mode 100644
index 0000000..9458b19
Binary files /dev/null and b/dev/docs/source/_images/sparklines2.png differ
diff --git a/dev/docs/source/_images/tab_colors.png b/dev/docs/source/_images/tab_colors.png
new file mode 100644
index 0000000..79e93e0
Binary files /dev/null and b/dev/docs/source/_images/tab_colors.png differ
diff --git a/dev/docs/source/_images/tables1.png b/dev/docs/source/_images/tables1.png
new file mode 100644
index 0000000..d433001
Binary files /dev/null and b/dev/docs/source/_images/tables1.png differ
diff --git a/dev/docs/source/_images/tables10.png b/dev/docs/source/_images/tables10.png
new file mode 100644
index 0000000..352a6f1
Binary files /dev/null and b/dev/docs/source/_images/tables10.png differ
diff --git a/dev/docs/source/_images/tables11.png b/dev/docs/source/_images/tables11.png
new file mode 100644
index 0000000..31131b2
Binary files /dev/null and b/dev/docs/source/_images/tables11.png differ
diff --git a/dev/docs/source/_images/tables12.png b/dev/docs/source/_images/tables12.png
new file mode 100644
index 0000000..1686f3f
Binary files /dev/null and b/dev/docs/source/_images/tables12.png differ
diff --git a/dev/docs/source/_images/tables2.png b/dev/docs/source/_images/tables2.png
new file mode 100644
index 0000000..16b7d7f
Binary files /dev/null and b/dev/docs/source/_images/tables2.png differ
diff --git a/dev/docs/source/_images/tables3.png b/dev/docs/source/_images/tables3.png
new file mode 100644
index 0000000..88fa1b0
Binary files /dev/null and b/dev/docs/source/_images/tables3.png differ
diff --git a/dev/docs/source/_images/tables4.png b/dev/docs/source/_images/tables4.png
new file mode 100644
index 0000000..657a398
Binary files /dev/null and b/dev/docs/source/_images/tables4.png differ
diff --git a/dev/docs/source/_images/tables5.png b/dev/docs/source/_images/tables5.png
new file mode 100644
index 0000000..57435c7
Binary files /dev/null and b/dev/docs/source/_images/tables5.png differ
diff --git a/dev/docs/source/_images/tables6.png b/dev/docs/source/_images/tables6.png
new file mode 100644
index 0000000..79a7c56
Binary files /dev/null and b/dev/docs/source/_images/tables6.png differ
diff --git a/dev/docs/source/_images/tables7.png b/dev/docs/source/_images/tables7.png
new file mode 100644
index 0000000..ed5b369
Binary files /dev/null and b/dev/docs/source/_images/tables7.png differ
diff --git a/dev/docs/source/_images/tables8.png b/dev/docs/source/_images/tables8.png
new file mode 100644
index 0000000..e0c14e4
Binary files /dev/null and b/dev/docs/source/_images/tables8.png differ
diff --git a/dev/docs/source/_images/tables9.png b/dev/docs/source/_images/tables9.png
new file mode 100644
index 0000000..6b83b36
Binary files /dev/null and b/dev/docs/source/_images/tables9.png differ
diff --git a/dev/docs/source/_images/text_indent.png b/dev/docs/source/_images/text_indent.png
new file mode 100644
index 0000000..335dc9a
Binary files /dev/null and b/dev/docs/source/_images/text_indent.png differ
diff --git a/dev/docs/source/_images/textbox01.png b/dev/docs/source/_images/textbox01.png
new file mode 100644
index 0000000..cf7112a
Binary files /dev/null and b/dev/docs/source/_images/textbox01.png differ
diff --git a/dev/docs/source/_images/textbox02.png b/dev/docs/source/_images/textbox02.png
new file mode 100644
index 0000000..d1dd74e
Binary files /dev/null and b/dev/docs/source/_images/textbox02.png differ
diff --git a/dev/docs/source/_images/textbox03.png b/dev/docs/source/_images/textbox03.png
new file mode 100644
index 0000000..bb7635f
Binary files /dev/null and b/dev/docs/source/_images/textbox03.png differ
diff --git a/dev/docs/source/_images/textbox11.png b/dev/docs/source/_images/textbox11.png
new file mode 100644
index 0000000..8fd19ee
Binary files /dev/null and b/dev/docs/source/_images/textbox11.png differ
diff --git a/dev/docs/source/_images/textbox12.png b/dev/docs/source/_images/textbox12.png
new file mode 100644
index 0000000..2896d60
Binary files /dev/null and b/dev/docs/source/_images/textbox12.png differ
diff --git a/dev/docs/source/_images/textbox13.png b/dev/docs/source/_images/textbox13.png
new file mode 100644
index 0000000..75e5a7c
Binary files /dev/null and b/dev/docs/source/_images/textbox13.png differ
diff --git a/dev/docs/source/_images/textbox14.png b/dev/docs/source/_images/textbox14.png
new file mode 100644
index 0000000..a60127a
Binary files /dev/null and b/dev/docs/source/_images/textbox14.png differ
diff --git a/dev/docs/source/_images/textbox15.png b/dev/docs/source/_images/textbox15.png
new file mode 100644
index 0000000..1d9a54e
Binary files /dev/null and b/dev/docs/source/_images/textbox15.png differ
diff --git a/dev/docs/source/_images/textbox16.png b/dev/docs/source/_images/textbox16.png
new file mode 100644
index 0000000..5964dd0
Binary files /dev/null and b/dev/docs/source/_images/textbox16.png differ
diff --git a/dev/docs/source/_images/textbox17.png b/dev/docs/source/_images/textbox17.png
new file mode 100644
index 0000000..0b567f1
Binary files /dev/null and b/dev/docs/source/_images/textbox17.png differ
diff --git a/dev/docs/source/_images/textbox21.png b/dev/docs/source/_images/textbox21.png
new file mode 100644
index 0000000..ab566f1
Binary files /dev/null and b/dev/docs/source/_images/textbox21.png differ
diff --git a/dev/docs/source/_images/textbox22.png b/dev/docs/source/_images/textbox22.png
new file mode 100644
index 0000000..0ebfdec
Binary files /dev/null and b/dev/docs/source/_images/textbox22.png differ
diff --git a/dev/docs/source/_images/textbox23.png b/dev/docs/source/_images/textbox23.png
new file mode 100644
index 0000000..9013866
Binary files /dev/null and b/dev/docs/source/_images/textbox23.png differ
diff --git a/dev/docs/source/_images/textbox24.png b/dev/docs/source/_images/textbox24.png
new file mode 100644
index 0000000..232d563
Binary files /dev/null and b/dev/docs/source/_images/textbox24.png differ
diff --git a/dev/docs/source/_images/textbox25.png b/dev/docs/source/_images/textbox25.png
new file mode 100644
index 0000000..ab5641f
Binary files /dev/null and b/dev/docs/source/_images/textbox25.png differ
diff --git a/dev/docs/source/_images/textbox26.png b/dev/docs/source/_images/textbox26.png
new file mode 100644
index 0000000..b442b93
Binary files /dev/null and b/dev/docs/source/_images/textbox26.png differ
diff --git a/dev/docs/source/_images/textbox31.png b/dev/docs/source/_images/textbox31.png
new file mode 100644
index 0000000..ca74faf
Binary files /dev/null and b/dev/docs/source/_images/textbox31.png differ
diff --git a/dev/docs/source/_images/textbox32.png b/dev/docs/source/_images/textbox32.png
new file mode 100644
index 0000000..ba7c200
Binary files /dev/null and b/dev/docs/source/_images/textbox32.png differ
diff --git a/dev/docs/source/_images/textbox33.png b/dev/docs/source/_images/textbox33.png
new file mode 100644
index 0000000..4c7db8a
Binary files /dev/null and b/dev/docs/source/_images/textbox33.png differ
diff --git a/dev/docs/source/_images/textbox34.png b/dev/docs/source/_images/textbox34.png
new file mode 100644
index 0000000..441d104
Binary files /dev/null and b/dev/docs/source/_images/textbox34.png differ
diff --git a/dev/docs/source/_images/textbox35.png b/dev/docs/source/_images/textbox35.png
new file mode 100644
index 0000000..805ee5d
Binary files /dev/null and b/dev/docs/source/_images/textbox35.png differ
diff --git a/dev/docs/source/_images/textbox41.png b/dev/docs/source/_images/textbox41.png
new file mode 100644
index 0000000..4c26b22
Binary files /dev/null and b/dev/docs/source/_images/textbox41.png differ
diff --git a/dev/docs/source/_images/textbox42.png b/dev/docs/source/_images/textbox42.png
new file mode 100644
index 0000000..3a90d0a
Binary files /dev/null and b/dev/docs/source/_images/textbox42.png differ
diff --git a/dev/docs/source/_images/tutorial01.png b/dev/docs/source/_images/tutorial01.png
new file mode 100644
index 0000000..bff5b6b
Binary files /dev/null and b/dev/docs/source/_images/tutorial01.png differ
diff --git a/dev/docs/source/_images/tutorial02.png b/dev/docs/source/_images/tutorial02.png
new file mode 100644
index 0000000..c12380f
Binary files /dev/null and b/dev/docs/source/_images/tutorial02.png differ
diff --git a/dev/docs/source/_images/tutorial03.png b/dev/docs/source/_images/tutorial03.png
new file mode 100644
index 0000000..031367a
Binary files /dev/null and b/dev/docs/source/_images/tutorial03.png differ
diff --git a/dev/docs/source/_images/unicode_polish_utf8.png b/dev/docs/source/_images/unicode_polish_utf8.png
new file mode 100644
index 0000000..85ec282
Binary files /dev/null and b/dev/docs/source/_images/unicode_polish_utf8.png differ
diff --git a/dev/docs/source/_images/unicode_python2.png b/dev/docs/source/_images/unicode_python2.png
new file mode 100644
index 0000000..77131f0
Binary files /dev/null and b/dev/docs/source/_images/unicode_python2.png differ
diff --git a/dev/docs/source/_images/unicode_python3.png b/dev/docs/source/_images/unicode_python3.png
new file mode 100644
index 0000000..8303e8f
Binary files /dev/null and b/dev/docs/source/_images/unicode_python3.png differ
diff --git a/dev/docs/source/_images/unicode_shift_jis.png b/dev/docs/source/_images/unicode_shift_jis.png
new file mode 100644
index 0000000..e478069
Binary files /dev/null and b/dev/docs/source/_images/unicode_shift_jis.png differ
diff --git a/dev/docs/source/_images/workbook01.png b/dev/docs/source/_images/workbook01.png
new file mode 100644
index 0000000..c0a2be8
Binary files /dev/null and b/dev/docs/source/_images/workbook01.png differ
diff --git a/dev/docs/source/_images/workbook02.png b/dev/docs/source/_images/workbook02.png
new file mode 100644
index 0000000..2c87fa7
Binary files /dev/null and b/dev/docs/source/_images/workbook02.png differ
diff --git a/dev/docs/source/_images/working_with_dates_and_times01.png b/dev/docs/source/_images/working_with_dates_and_times01.png
new file mode 100644
index 0000000..6cddd91
Binary files /dev/null and b/dev/docs/source/_images/working_with_dates_and_times01.png differ
diff --git a/dev/docs/source/_images/working_with_dates_and_times02.png b/dev/docs/source/_images/working_with_dates_and_times02.png
new file mode 100644
index 0000000..dee6e27
Binary files /dev/null and b/dev/docs/source/_images/working_with_dates_and_times02.png differ
diff --git a/dev/docs/source/_images/working_with_formulas1.png b/dev/docs/source/_images/working_with_formulas1.png
new file mode 100644
index 0000000..2e4b291
Binary files /dev/null and b/dev/docs/source/_images/working_with_formulas1.png differ
diff --git a/dev/docs/source/_images/working_with_formulas2.png b/dev/docs/source/_images/working_with_formulas2.png
new file mode 100644
index 0000000..7299deb
Binary files /dev/null and b/dev/docs/source/_images/working_with_formulas2.png differ
diff --git a/dev/docs/source/_images/worksheet00.png b/dev/docs/source/_images/worksheet00.png
new file mode 100644
index 0000000..e97c0ae
Binary files /dev/null and b/dev/docs/source/_images/worksheet00.png differ
diff --git a/dev/docs/source/_images/worksheet01.png b/dev/docs/source/_images/worksheet01.png
new file mode 100644
index 0000000..a8e36b6
Binary files /dev/null and b/dev/docs/source/_images/worksheet01.png differ
diff --git a/dev/docs/source/_images/worksheet02.png b/dev/docs/source/_images/worksheet02.png
new file mode 100644
index 0000000..a00ea71
Binary files /dev/null and b/dev/docs/source/_images/worksheet02.png differ
diff --git a/dev/docs/source/_images/worksheet_activate.png b/dev/docs/source/_images/worksheet_activate.png
new file mode 100644
index 0000000..ffddc5b
Binary files /dev/null and b/dev/docs/source/_images/worksheet_activate.png differ
diff --git a/dev/docs/source/_images/worksheet_protection.png b/dev/docs/source/_images/worksheet_protection.png
new file mode 100644
index 0000000..6812ad5
Binary files /dev/null and b/dev/docs/source/_images/worksheet_protection.png differ
diff --git a/dev/docs/source/_static/basic.css b/dev/docs/source/_static/basic.css
new file mode 100644
index 0000000..fe770ba
--- /dev/null
+++ b/dev/docs/source/_static/basic.css
@@ -0,0 +1,543 @@
+/*
+ * basic.css
+ * ~~~~~~~~~
+ *
+ * Sphinx stylesheet -- basic theme.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+/* -- main layout ----------------------------------------------------------- */
+
+div.clearer {
+ clear: both;
+}
+
+/* -- relbar ---------------------------------------------------------------- */
+
+div.related {
+ width: 100%;
+ font-size: 90%;
+}
+
+div.related h3 {
+ display: none;
+}
+
+div.related ul {
+ margin: 0;
+ padding: 0 0 0 10px;
+ list-style: none;
+}
+
+div.related li {
+ display: inline;
+}
+
+div.related li.right {
+ float: right;
+ margin-right: 5px;
+}
+
+/* -- sidebar --------------------------------------------------------------- */
+
+div.sphinxsidebarwrapper {
+ padding: 10px 5px 0 10px;
+}
+
+div.sphinxsidebar {
+ float: left;
+ width: 230px;
+ margin-left: -100%;
+ font-size: 90%;
+}
+
+div.sphinxsidebar ul {
+ list-style: none;
+}
+
+div.sphinxsidebar ul ul,
+div.sphinxsidebar ul.want-points {
+ margin-left: 20px;
+ list-style: square;
+}
+
+div.sphinxsidebar ul ul {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+div.sphinxsidebar form {
+ margin-top: 10px;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #98dbcc;
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+div.sphinxsidebar #searchbox input[type="text"] {
+ width: 170px;
+}
+
+div.sphinxsidebar #searchbox input[type="submit"] {
+ width: 30px;
+}
+
+img {
+ border: 0;
+}
+
+/* -- search page ----------------------------------------------------------- */
+
+ul.search {
+ margin: 10px 0 0 20px;
+ padding: 0;
+}
+
+ul.search li {
+ padding: 5px 0 5px 20px;
+ background-image: url(file.png);
+ background-repeat: no-repeat;
+ background-position: 0 7px;
+}
+
+ul.search li a {
+ font-weight: bold;
+}
+
+ul.search li div.context {
+ color: #888;
+ margin: 2px 0 0 30px;
+ text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+ font-weight: bold;
+}
+
+/* -- index page ------------------------------------------------------------ */
+
+table.contentstable {
+ width: 90%;
+}
+
+table.contentstable p.biglink {
+ line-height: 150%;
+}
+
+a.biglink {
+ font-size: 1.3em;
+}
+
+span.linkdescr {
+ font-style: italic;
+ padding-top: 5px;
+ font-size: 90%;
+}
+
+/* -- general index --------------------------------------------------------- */
+
+table.indextable {
+ width: 100%;
+}
+
+table.indextable td {
+ text-align: left;
+ vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+ height: 10px;
+}
+
+table.indextable tr.cap {
+ margin-top: 10px;
+ background-color: #f2f2f2;
+}
+
+img.toggler {
+ margin-right: 3px;
+ margin-top: 3px;
+ cursor: pointer;
+}
+
+div.modindex-jumpbox {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ margin: 1em 0 1em 0;
+ padding: 0.4em;
+}
+
+div.genindex-jumpbox {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ margin: 1em 0 1em 0;
+ padding: 0.4em;
+}
+
+/* -- general body styles --------------------------------------------------- */
+
+a.headerlink {
+ visibility: hidden;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink {
+ visibility: visible;
+}
+
+div.body p.caption {
+ text-align: inherit;
+}
+
+div.body td {
+ text-align: left;
+}
+
+.field-list ul {
+ padding-left: 1em;
+}
+
+.first {
+ margin-top: 0 !important;
+}
+
+p.rubric {
+ margin-top: 30px;
+ font-weight: bold;
+}
+
+img.align-left, .figure.align-left, object.align-left {
+ clear: left;
+ float: left;
+ margin-right: 1em;
+}
+
+img.align-right, .figure.align-right, object.align-right {
+ clear: right;
+ float: right;
+ margin-left: 1em;
+}
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left;
+}
+
+.align-center {
+ text-align: center;
+}
+
+.align-right {
+ text-align: right;
+}
+
+/* -- sidebars -------------------------------------------------------------- */
+
+div.sidebar {
+ margin: 0 0 0.5em 1em;
+ border: 1px solid #ddb;
+ padding: 7px 7px 0 7px;
+ background-color: #ffe;
+ width: 40%;
+ float: right;
+}
+
+p.sidebar-title {
+ font-weight: bold;
+}
+
+/* -- topics ---------------------------------------------------------------- */
+
+div.topic {
+ border: 1px solid #ccc;
+ padding: 7px 7px 0 7px;
+ margin: 10px 0 10px 0;
+}
+
+p.topic-title {
+ font-size: 1.1em;
+ font-weight: bold;
+ margin-top: 10px;
+ width: 825px;
+}
+
+/* -- admonitions ----------------------------------------------------------- */
+
+div.admonition {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding: 7px;
+ /* width: 842px; */
+}
+
+div.admonition dt {
+ font-weight: bold;
+}
+
+div.admonition dl {
+ margin-bottom: 0;
+}
+
+p.admonition-title {
+ margin: 0px 10px 5px 0px;
+ font-weight: bold;
+}
+
+div.body p.centered {
+ text-align: center;
+ margin-top: 25px;
+}
+
+/* -- tables ---------------------------------------------------------------- */
+
+table.docutils {
+ margin-left: 30px;
+ border: 0;
+ border-collapse: collapse;
+}
+
+table.docutils td, table.docutils th {
+ padding: 1px 8px 1px 5px;
+ border-top: 1px solid #aaa;
+ border-left: 1px solid #aaa;
+ border-right: 1px solid #aaa;
+ border-bottom: 1px solid #aaa;
+}
+
+table.field-list td, table.field-list th {
+ border: 0 !important;
+}
+
+table.footnote td, table.footnote th {
+ border: 0 !important;
+}
+
+th {
+ text-align: left;
+ padding-right: 5px;
+}
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px;
+}
+
+table.citation td {
+ border-bottom: none;
+}
+
+/* -- other body styles ----------------------------------------------------- */
+
+ol.arabic {
+ list-style: decimal;
+}
+
+ol.loweralpha {
+ list-style: lower-alpha;
+}
+
+ol.upperalpha {
+ list-style: upper-alpha;
+}
+
+ol.lowerroman {
+ list-style: lower-roman;
+}
+
+ol.upperroman {
+ list-style: upper-roman;
+}
+
+dl {
+ margin-bottom: 30px;
+}
+
+dd p {
+ margin-top: 0px;
+}
+
+dd ul, dd table {
+ margin-bottom: 10px;
+}
+
+dd {
+ margin-top: 3px;
+ margin-bottom: 10px;
+ margin-left: 30px;
+}
+
+dt:target, .highlighted {
+ background-color: #fbe54e;
+}
+
+dl.glossary dt {
+ font-weight: bold;
+ font-size: 1.1em;
+}
+
+.field-list ul {
+ margin: 0;
+ padding-left: 1em;
+}
+
+.field-list p {
+ margin: 0;
+}
+
+.refcount {
+ color: #060;
+}
+
+.optional {
+ font-size: 1.3em;
+}
+
+.versionmodified {
+ font-style: italic;
+}
+
+.system-message {
+ background-color: #fda;
+ padding: 5px;
+ border: 3px solid red;
+}
+
+.footnote:target {
+ background-color: #ffa;
+}
+
+.line-block {
+ display: block;
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+
+.line-block .line-block {
+ margin-top: 0;
+ margin-bottom: 0;
+ margin-left: 1.5em;
+}
+
+.guilabel, .menuselection {
+ font-family: sans-serif;
+}
+
+.accelerator {
+ text-decoration: underline;
+}
+
+.classifier {
+ font-style: oblique;
+}
+
+abbr, acronym {
+ border-bottom: dotted 1px;
+ cursor: help;
+}
+
+/* -- code displays --------------------------------------------------------- */
+
+pre {
+ overflow: auto;
+ overflow-y: hidden; /* fixes display issues on Chrome browsers */
+}
+
+td.linenos pre {
+ padding: 5px 0px;
+ border: 0;
+ background-color: transparent;
+ color: #aaa;
+}
+
+table.highlighttable {
+ margin-left: 0.5em;
+}
+
+table.highlighttable td {
+ padding: 0 0.5em 0 0.5em;
+}
+
+tt.descname {
+ background-color: transparent;
+ font-weight: bold;
+ font-size: 1.2em;
+}
+
+tt.descclassname {
+ background-color: transparent;
+}
+
+tt.xref, a tt {
+ background-color: transparent;
+ /* font-weight: bold; */
+}
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+ background-color: transparent;
+}
+
+.viewcode-link {
+ float: right;
+}
+
+.viewcode-back {
+ float: right;
+ font-family: sans-serif;
+}
+
+div.viewcode-block:target {
+ margin: -1px -10px;
+ padding: 0 10px;
+}
+
+/* -- math display ---------------------------------------------------------- */
+
+img.math {
+ vertical-align: middle;
+}
+
+div.body div.math p {
+ text-align: center;
+}
+
+span.eqno {
+ float: right;
+}
+
+/* -- printout stylesheet --------------------------------------------------- */
+
+ at media print {
+ div.document,
+ div.documentwrapper,
+ div.bodywrapper {
+ margin: 0 !important;
+ width: 100%;
+ }
+
+ div.sphinxsidebar,
+ div.related,
+ div.footer,
+ #top-link {
+ display: none;
+ }
+}
\ No newline at end of file
diff --git a/dev/docs/source/_static/default.css b/dev/docs/source/_static/default.css
new file mode 100644
index 0000000..409ef80
--- /dev/null
+++ b/dev/docs/source/_static/default.css
@@ -0,0 +1,258 @@
+/*
+ * default.css_t
+ * ~~~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- default theme.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+ at import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+ font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif;
+ font-size: 100%;
+ background-color: #FFFFFF;
+ color: #000;
+ margin: 0;
+ padding: 0;
+ min-width: 800px;
+}
+
+
+div.document {
+ background-color: #F2F2F2;
+}
+
+div.documentwrapper {
+ float: left;
+ width: 100%;
+}
+
+div.bodywrapper {
+ margin: 0 0 0 280px;
+}
+
+div.body {
+ background-color: #ffffff;
+ color: #000000;
+ padding: 0 20px 30px 20px;
+}
+
+div.footer {
+ color: #9CB640;
+ width: 100%;
+ padding: 9px 0 9px 0;
+ text-align: center;
+ font-size: 75%;
+}
+
+div.footer a {
+ color: #9CB640;
+ text-decoration: underline;
+}
+
+div.related {
+ background-color: #9CB640;
+ line-height: 80px;
+ color: #ffffff;
+}
+
+div.related a {
+ color: #ffffff;
+}
+
+div.sphinxsidebar {
+}
+
+div.sphinxsidebar h3 {
+ font-family: 'Trebuchet MS', sans-serif;
+ color: #9CB640;
+ font-size: 1.4em;
+ font-weight: normal;
+ margin: 0;
+ padding: 0;
+}
+
+div.sphinxsidebar h3 a {
+ color: #ffffff;
+}
+
+div.sphinxsidebar h4 {
+ font-family: 'Trebuchet MS', sans-serif;
+ color: #9CB640;
+ font-size: 1.3em;
+ font-weight: normal;
+ margin: 5px 0 0 0;
+ padding: 0;
+}
+
+div.sphinxsidebar p {
+ color: #ffffff;
+}
+
+div.sphinxsidebar p.topless {
+ margin: 5px 10px 10px 10px;
+}
+
+div.sphinxsidebar ul {
+ margin: 10px;
+ padding: 0;
+ color: #ffffff;
+}
+
+div.sphinxsidebar a {
+ color: #9CB640;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #9CB640;
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+
+
+/* -- hyperlink styles ------------------------------------------------------ */
+
+a {
+ color: #9CB640;
+ text-decoration: none;
+}
+
+a:visited {
+ color: #9CB640;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+
+
+/* -- body styles ----------------------------------------------------------- */
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+ font-family: 'Trebuchet MS', sans-serif;
+ background-color: #f2f2f2;
+ font-weight: normal;
+ color: #9CB640;
+ border-bottom: 1px solid #ccc;
+ margin: 20px -20px 10px -20px;
+ padding: 3px 0 3px 10px;
+}
+
+div.body h1 { margin-top: 0; font-size: 200%; padding: 20px;}
+div.body h2 { font-size: 160%; }
+div.body h3 { font-size: 140%; }
+div.body h4 { font-size: 120%; }
+div.body h5 { font-size: 110%; }
+div.body h6 { font-size: 100%; }
+
+a.headerlink {
+ color: #c60f0f;
+ font-size: 0.8em;
+ padding: 0 4px 0 4px;
+ text-decoration: none;
+}
+
+a.headerlink:hover {
+ background-color: #c60f0f;
+ color: white;
+}
+
+div.body p, div.body dd, div.body li {
+ text-align: justify;
+ line-height: 130%;
+}
+
+div.admonition p.admonition-title + p {
+ display: inline;
+}
+
+div.admonition p {
+ margin-bottom: 5px;
+}
+
+div.admonition pre {
+ margin-bottom: 5px;
+}
+
+div.admonition ul, div.admonition ol {
+ margin-bottom: 5px;
+}
+
+div.note {
+ background-color: #eee;
+ border: 1px solid #ccc;
+}
+
+div.seealso {
+ background-color: #ffc;
+ border: 1px solid #ff6;
+}
+
+div.topic {
+ background-color: #eee;
+}
+
+div.warning {
+ background-color: #ffe4e4;
+ border: 1px solid #f66;
+}
+
+p.admonition-title {
+ display: inline;
+}
+
+p.admonition-title:after {
+ content: ":";
+}
+
+pre {
+ margin-left: 30px;
+ width : 640px;
+ padding: 15px;
+ color: #333333;
+ line-height: 120%;
+ border: 1px solid #ac9;
+}
+
+cite, code, tt {
+ font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
+ font-size: 0.95em;
+ letter-spacing: 0.01em;
+}
+
+
+th {
+ background-color: #f2f2f2;
+}
+
+.warning tt {
+ background: #efc2c2;
+}
+
+.note tt {
+ background: #d6d6d6;
+}
+
+.viewcode-back {
+ font-family: sans-serif;
+}
+
+div.viewcode-block:target {
+ background-color: #f4debf;
+ border-top: 1px solid #ac9;
+ border-bottom: 1px solid #ac9;
+}
\ No newline at end of file
diff --git a/dev/docs/source/_static/pygments.css b/dev/docs/source/_static/pygments.css
new file mode 100644
index 0000000..34b2ef6
--- /dev/null
+++ b/dev/docs/source/_static/pygments.css
@@ -0,0 +1,61 @@
+.highlight .hll { border-left: 2px solid #ff0000; }
+.highlight .c { color: #65B154 } /* Comment */
+.highlight .err { border: 1px solid #FF0000 } /* Error */
+.highlight .k { color: #007020; font-weight: bold } /* Keyword */
+.highlight .o { color: #666666 } /* Operator */
+.highlight .cm { color: #65B154 } /* Comment.Multiline */
+.highlight .cp { color: #007020 } /* Comment.Preproc */
+.highlight .c1 { color: #65B154 } /* Comment.Single */
+.highlight .cs { color: #65B154; background-color: #fff0f0 } /* Comment.Special */
+.highlight .gd { color: #A00000 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #FF0000 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #333333 } /* Generic.Output */
+.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #0044DD } /* Generic.Traceback */
+.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
+.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
+.highlight .kp { color: #007020 } /* Keyword.Pseudo */
+.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #902000 } /* Keyword.Type */
+.highlight .m { color: #208050 } /* Literal.Number */
+.highlight .s { color: #4070a0 } /* Literal.String */
+.highlight .na { color: #4070a0 } /* Name.Attribute */
+.highlight .nb { color: #007020 } /* Name.Builtin */
+.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
+.highlight .no { color: #60add5 } /* Name.Constant */
+.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
+.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
+.highlight .ne { color: #007020 } /* Name.Exception */
+.highlight .nf { color: #06287e } /* Name.Function */
+.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
+.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
+.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #bb60d5 } /* Name.Variable */
+.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mf { color: #208050 } /* Literal.Number.Float */
+.highlight .mh { color: #208050 } /* Literal.Number.Hex */
+.highlight .mi { color: #208050 } /* Literal.Number.Integer */
+.highlight .mo { color: #208050 } /* Literal.Number.Oct */
+.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
+.highlight .sc { color: #4070a0 } /* Literal.String.Char */
+.highlight .sd { color: #4070a0 } /* Literal.String.Doc */
+.highlight .s2 { color: #4070a0 } /* Literal.String.Double */
+.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
+.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
+.highlight .si { color: #70a0d0 } /* Literal.String.Interpol */
+.highlight .sx { color: #c65d09 } /* Literal.String.Other */
+.highlight .sr { color: #235388 } /* Literal.String.Regex */
+.highlight .s1 { color: #4070a0 } /* Literal.String.Single */
+.highlight .ss { color: #517918 } /* Literal.String.Symbol */
+.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
+.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
+.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
+.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */
\ No newline at end of file
diff --git a/dev/docs/source/_themes/bootstrap/globaltoc.html b/dev/docs/source/_themes/bootstrap/globaltoc.html
new file mode 100644
index 0000000..b27d3ac
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/globaltoc.html
@@ -0,0 +1,7 @@
+<li class="dropdown globaltoc-container">
+ <a href="{{ pathto(master_doc) }}"
+ class="dropdown-toggle"
+ data-toggle="dropdown">{{ theme_navbar_site_name }} <b class="caret"></b></a>
+ <ul class="dropdown-menu globaltoc"
+ >{{ toctree(maxdepth=theme_globaltoc_depth|toint, collapse=False, includehidden=theme_globaltoc_includehidden|tobool) }}</ul>
+</li>
diff --git a/dev/docs/source/_themes/bootstrap/layout.html b/dev/docs/source/_themes/bootstrap/layout.html
new file mode 100644
index 0000000..4875dd5
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/layout.html
@@ -0,0 +1,144 @@
+{% if theme_bootstrap_version == "3" %}
+ {% set bootstrap_version, bootstrap_additional_css, navbar_version = "3.0.0", "theme", "" %}
+ {% set bs_span_prefix = "col-md-" %}
+ {% if theme_bootswatch_theme %}
+ {% set css_files = css_files + [
+ '//netdna.bootstrapcdn.com/bootswatch/' + bootstrap_version + '/' + theme_bootswatch_theme + '/bootstrap.min.css',
+ '_static/bootstrap-sphinx.css'
+ ]
+ %}
+ {% else %}
+ {% set css_files = [
+ '_static/bootstrap-' + bootstrap_version + '/css/bootstrap.min.css'
+ ] + css_files
+ %}
+ {% set css_files = css_files + [
+ '_static/bootstrap-' + bootstrap_version + '/css/bootstrap-' + bootstrap_additional_css + '.min.css',
+ '_static/bootstrap-sphinx.css'
+ ]
+ %}
+ {% endif %}
+{% else %}
+ {% set bootstrap_version, bootstrap_additional_css, navbar_version = "2.3.2", "responsive", "-2" %}
+ {% set bs_span_prefix = "span" %}
+ {% set css_files = [
+ '_static/bootstrap-' + bootstrap_version + '/css/bootstrap.min.css'
+ ] + css_files
+ %}
+ {% if theme_bootswatch_theme %}
+ {% set css_files = css_files + [
+ '//netdna.bootstrapcdn.com/bootswatch/' + bootstrap_version + '/' + theme_bootswatch_theme + '/bootstrap.min.css'
+ ]
+ %}
+ {% endif %}
+ {% set css_files = css_files + [
+ '_static/bootstrap-' + bootstrap_version + '/css/bootstrap-' + bootstrap_additional_css + '.min.css',
+ '_static/bootstrap-sphinx.css'
+ ]
+ %}
+{% endif %}
+
+{% extends "basic/layout.html" %}
+
+ {% set script_files = script_files + [
+ '_static/js/jquery-1.9.1.min.js',
+ '_static/js/jquery-fix.js',
+ '_static/bootstrap-' + bootstrap_version + '/js/bootstrap.min.js',
+ '_static/bootstrap-sphinx.js'
+ ]
+%}
+{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and sidebars %}
+
+{%- set bs_content_width = render_sidebar and "9" or "12"%}
+
+
+{%- block doctype -%}
+<!DOCTYPE html>
+{%- endblock %}
+
+{# Sidebar: Rework into our Bootstrap nav section. #}
+{% macro navBar() %}
+{% include "navbar" + navbar_version + ".html" %}
+{% endmacro %}
+
+{% if theme_bootstrap_version == "3" %}
+ {%- macro bsidebar() %}
+ {%- if render_sidebar %}
+ <div class="{{ bs_span_prefix }}3">
+ <div id="sidebar" class="bs-sidenav" role="complementary">
+ {%- for sidebartemplate in sidebars %}
+ {%- include sidebartemplate %}
+ {%- endfor %}
+ </div>
+ </div>
+ {%- endif %}
+ {%- endmacro %}
+{% else %}
+ {%- macro bsidebar() %}
+ {%- if render_sidebar %}
+ <div class="{{ bs_span_prefix }}3">
+ <div id="sidebar" class="bs-sidenav well" data-spy="affix">
+ {%- for sidebartemplate in sidebars %}
+ {%- include sidebartemplate %}
+ {%- endfor %}
+ </div>
+ </div>
+ {%- endif %}
+ {%- endmacro %}
+{% endif %}
+
+
+{%- block extrahead %}
+<meta charset='utf-8'>
+<meta http-equiv='X-UA-Compatible' content='IE=edge,chrome=1'>
+<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1'>
+<meta name="apple-mobile-web-app-capable" content="yes">
+{% endblock %}
+
+{# Silence the sidebar's, relbar's #}
+{% block header %}{% endblock %}
+{% block relbar1 %}{% endblock %}
+{% block relbar2 %}{% endblock %}
+{% block sidebarsourcelink %}{% endblock %}
+
+{%- block content %}
+{{ navBar() }}
+<div class="container">
+ <div class="row">
+ {%- block sidebar1 %}{{ bsidebar() }}{% endblock %}
+ <div class="{{ bs_span_prefix }}{{ bs_content_width }}">
+ {% block body %}{% endblock %}
+ </div>
+ {% block sidebar2 %} {# possible location for sidebar #} {% endblock %}
+ </div>
+</div>
+{%- endblock %}
+
+{%- block footer %}
+<footer class="footer">
+ <div class="container">
+ <p class="pull-right">
+ <a href="#">Back to top</a>
+ {% if theme_source_link_position == "footer" %}
+ <br/>
+ {% include "sourcelink.html" %}
+ {% endif %}
+ </p>
+ <p>
+ {%- if show_copyright %}
+ {%- if hasdoc('copyright') %}
+ {% trans path=pathto('copyright'), copyright=copyright|e %}© <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %}<br/>
+ {%- else %}
+ {% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %}<br/>
+ {%- endif %}
+ {%- endif %}
+ {%- if last_updated %}
+ {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}<br/>
+ {%- endif %}
+ {%- if show_sphinx %}
+ {% trans sphinx_version=sphinx_version|e %}Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> {{ sphinx_version }}.{% endtrans %}<br/>
+ {%- endif %}
+ </p>
+ </div>
+</footer>
+{%- endblock %}
diff --git a/dev/docs/source/_themes/bootstrap/localtoc.html b/dev/docs/source/_themes/bootstrap/localtoc.html
new file mode 100644
index 0000000..6e6c03b
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/localtoc.html
@@ -0,0 +1 @@
+{{ toc }}
diff --git a/dev/docs/source/_themes/bootstrap/navbar-2.html b/dev/docs/source/_themes/bootstrap/navbar-2.html
new file mode 100644
index 0000000..ce450b0
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/navbar-2.html
@@ -0,0 +1,45 @@
+ <div id="navbar" class="{{ theme_navbar_class }} {% if theme_navbar_fixed_top == 'true' -%} navbar-fixed-top{%- endif -%}">
+ <div class="navbar-inner">
+ <div class="container">
+ <!-- .btn-navbar is used as the toggle for collapsed navbar content -->
+ <button class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+
+ <a class="brand" href="{{ pathto(master_doc) }}">{% if theme_navbar_title -%}{{ theme_navbar_title|e }}{%- else -%}{{ project|e }}{%- endif -%}</a>
+
+ <div class="nav-collapse">
+ <ul class="nav">
+ <li class="divider-vertical"></li>
+ {% if theme_navbar_links %}
+ {%- for link in theme_navbar_links %}
+ <li><a href="{{ pathto(*link[1:]) }}">{{ link[0] }}</a></li>
+ {%- endfor %}
+ {% endif %}
+ {% block navbartoc %}
+ {% include "globaltoc.html" %}
+ {% if theme_navbar_pagenav %}
+ {% include "navbartoc.html" %}
+ {% endif %}
+ {% endblock %}
+ {% if theme_navbar_sidebarrel %}
+ {% block sidebarrel %}
+ {% include "relations.html" %}
+ {% endblock %}
+ {% endif %}
+ {% block navbarextra %}
+ {% endblock %}
+ {% if theme_source_link_position == "nav" %}
+ <li>{% include "sourcelink.html" %}</li>
+ {% endif %}
+ </ul>
+
+ {% block navbarsearch %}
+ {% include "navbarsearchbox.html" %}
+ {% endblock %}
+ </div>
+ </div>
+ </div>
+ </div>
diff --git a/dev/docs/source/_themes/bootstrap/navbar.html b/dev/docs/source/_themes/bootstrap/navbar.html
new file mode 100644
index 0000000..85c0c0c
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/navbar.html
@@ -0,0 +1,44 @@
+ <div id="navbar" class="{{ theme_navbar_class }} navbar-default {% if theme_navbar_fixed_top == 'true' -%} navbar-fixed-top{%- endif -%}">
+ <div class="container">
+ <div class="navbar-header">
+ <!-- .btn-navbar is used as the toggle for collapsed navbar content -->
+ <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".nav-collapse">
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a class="navbar-brand" href="{{ pathto(master_doc) }}">{% if theme_navbar_title -%}{{ theme_navbar_title|e }}{%- else -%}{{ project|e }}{%- endif -%}</a>
+ </div>
+
+ <div class="collapse navbar-collapse nav-collapse">
+ <ul class="nav navbar-nav">
+ <li class="divider-vertical"></li>
+ {% if theme_navbar_links %}
+ {%- for link in theme_navbar_links %}
+ <li><a href="{{ pathto(*link[1:]) }}">{{ link[0] }}</a></li>
+ {%- endfor %}
+ {% endif %}
+ {% block navbartoc %}
+ {% include "globaltoc.html" %}
+ {% if theme_navbar_pagenav %}
+ {% include "navbartoc.html" %}
+ {% endif %}
+ {% endblock %}
+ {% if theme_navbar_sidebarrel %}
+ {% block sidebarrel %}
+ {% include "relations.html" %}
+ {% endblock %}
+ {% endif %}
+ {% block navbarextra %}
+ {% endblock %}
+ {% if theme_source_link_position == "nav" %}
+ <li>{% include "sourcelink.html" %}</li>
+ {% endif %}
+ </ul>
+
+ {% block navbarsearch %}
+ {% include "navbarsearchbox.html" %}
+ {% endblock %}
+ </div>
+ </div>
+ </div>
diff --git a/dev/docs/source/_themes/bootstrap/navbarsearchbox.html b/dev/docs/source/_themes/bootstrap/navbarsearchbox.html
new file mode 100644
index 0000000..82e9338
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/navbarsearchbox.html
@@ -0,0 +1,9 @@
+{%- if pagename != "search" %}
+<form class="navbar-form navbar-right" action="{{ pathto('search') }}" method="get">
+ <div class="form-group">
+ <input type="text" name="q" class="form-control" placeholder="Search" />
+ </div>
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+</form>
+{%- endif %}
diff --git a/dev/docs/source/_themes/bootstrap/navbartoc.html b/dev/docs/source/_themes/bootstrap/navbartoc.html
new file mode 100644
index 0000000..2041c5c
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/navbartoc.html
@@ -0,0 +1,4 @@
+<li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ _('Page') }} <b class="caret"></b></a>
+ <ul class="dropdown-menu localtoc">{{ toc }}</ul>
+</li>
diff --git a/dev/docs/source/_themes/bootstrap/relations.html b/dev/docs/source/_themes/bootstrap/relations.html
new file mode 100644
index 0000000..cd947d1
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/relations.html
@@ -0,0 +1,8 @@
+{%- if prev %}
+ <li><a href="{{ prev.link|e }}"
+ title="{{ _('Previous Chapter: ') + prev.title|striptags }}">{{ "«"|safe }} {{ prev.title|striptags|truncate(length=16, killwords=True) }}</a></li>
+{%- endif %}
+{%- if next %}
+ <li><a href="{{ next.link|e }}"
+ title="{{ _('Next Chapter: ') + next.title|striptags }}">{{ next.title|striptags|truncate(length=16, killwords=True) }} {{ "»"|safe }}</a></li>
+{%- endif %}
diff --git a/dev/docs/source/_themes/bootstrap/search.html b/dev/docs/source/_themes/bootstrap/search.html
new file mode 100644
index 0000000..ffe1106
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/search.html
@@ -0,0 +1,58 @@
+{#
+ basic/search.html
+ ~~~~~~~~~~~~~~~~~
+
+ Template for the search page.
+
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+#}
+{%- extends "layout.html" %}
+{% set title = _('Search') %}
+{% set script_files = script_files + ['_static/searchtools.js'] %}
+{% block extrahead %}
+ <script type="text/javascript">
+ jQuery(function() { Search.loadIndex("{{ pathto('searchindex.js', 1) }}"); });
+ </script>
+ {{ super() }}
+{% endblock %}
+{% block body %}
+ <h1 id="search-documentation">{{ _('Search') }}</h1>
+ <div id="fallback" class="admonition warning">
+ <script type="text/javascript">$('#fallback').hide();</script>
+ <p>
+ {% trans %}Please activate JavaScript to enable the search
+ functionality.{% endtrans %}
+ </p>
+ </div>
+ <p>
+ {% trans %}From here you can search these documents. Enter your search
+ words into the box below and click "search". Note that the search
+ function will automatically search for all of the words. Pages
+ containing fewer words won't appear in the result list.{% endtrans %}
+ </p>
+
+ <form class="form-search" action="" method="get">
+ <input type="text" name="q" class="input-medium search-query">
+ <button type="submit" class="btn">{{ _('search') }}</button>
+ <span id="search-progress" style="padding-left: 10px"></span>
+ </form>
+
+ {% if search_performed %}
+ <h2>{{ _('Search Results') }}</h2>
+ {% if not search_results %}
+ <p>{{ _('Your search did not match any results.') }}</p>
+ {% endif %}
+ {% endif %}
+ <div id="search-results">
+ {% if search_results %}
+ <ul>
+ {% for href, caption, context in search_results %}
+ <li><a href="{{ pathto(item.href) }}">{{ caption }}</a>
+ <div class="context">{{ context|e }}</div>
+ </li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ </div>
+{% endblock %}
diff --git a/dev/docs/source/_themes/bootstrap/searchbox.html b/dev/docs/source/_themes/bootstrap/searchbox.html
new file mode 100644
index 0000000..53755f3
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/searchbox.html
@@ -0,0 +1,9 @@
+{%- if pagename != "search" %}
+<form action="{{ pathto('search') }}" method="get">
+ <div class="form-group">
+ <input type="text" name="q" class="form-control" placeholder="Search" />
+ </div>
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+</form>
+{%- endif %}
diff --git a/dev/docs/source/_themes/bootstrap/searchresults.html b/dev/docs/source/_themes/bootstrap/searchresults.html
new file mode 100644
index 0000000..cfb0aa4
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/searchresults.html
@@ -0,0 +1,38 @@
+{#
+ basic/searchresults.html
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Template for the body of the search results page.
+
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+#}
+<h1 id="search-documentation">Search</h1>
+<p>
+ From here you can search these documents. Enter your search
+ words into the box below and click "search".
+</p>
+
+<form class="form-search" action="" method="get">
+ <input type="text" name="q" class="input-medium search-query">
+ <button type="submit" class="btn">{{ _('search') }}</button>
+ <span id="search-progress" style="padding-left: 10px"></span>
+</form>
+
+{%- if search_performed %}
+ <h2>Search Results</h2>
+ {%- if not search_results %}
+ <p>Your search did not match any results.</p>
+ {%- endif %}
+{%- endif %}
+<div id="search-results">
+ {%- if search_results %}
+ <ul class="search">
+ {% for href, caption, context in search_results %}
+ <li><a href="{{ docroot }}{{ href }}/?highlight={{ q }}">{{ caption }}</a>
+ <div class="context">{{ context|e }}</div>
+ </li>
+ {% endfor %}
+ </ul>
+ {%- endif %}
+</div>
diff --git a/dev/docs/source/_themes/bootstrap/sourcelink.html b/dev/docs/source/_themes/bootstrap/sourcelink.html
new file mode 100644
index 0000000..3cf7335
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/sourcelink.html
@@ -0,0 +1,6 @@
+{%- if show_source and has_source and sourcename %}
+<div id="sourcelink">
+ <a href="{{ pathto('_sources/' + sourcename, true)|e }}"
+ rel="nofollow">{{ _('Source') }}</a>
+</div>
+{%- endif %}
diff --git a/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/css/bootstrap-theme.css b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/css/bootstrap-theme.css
new file mode 100644
index 0000000..ad11735
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/css/bootstrap-theme.css
@@ -0,0 +1,384 @@
+.btn-default,
+.btn-primary,
+.btn-success,
+.btn-info,
+.btn-warning,
+.btn-danger {
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.btn-default:active,
+.btn-primary:active,
+.btn-success:active,
+.btn-info:active,
+.btn-warning:active,
+.btn-danger:active,
+.btn-default.active,
+.btn-primary.active,
+.btn-success.active,
+.btn-info.active,
+.btn-warning.active,
+.btn-danger.active {
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
+
+.btn:active,
+.btn.active {
+ background-image: none;
+}
+
+.btn-default {
+ text-shadow: 0 1px 0 #fff;
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#e6e6e6));
+ background-image: -webkit-linear-gradient(top, #ffffff, 0%, #e6e6e6, 100%);
+ background-image: -moz-linear-gradient(top, #ffffff 0%, #e6e6e6 100%);
+ background-image: linear-gradient(to bottom, #ffffff 0%, #e6e6e6 100%);
+ background-repeat: repeat-x;
+ border-color: #e0e0e0;
+ border-color: #ccc;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
+}
+
+.btn-default:active,
+.btn-default.active {
+ background-color: #e6e6e6;
+ border-color: #e0e0e0;
+}
+
+.btn-primary {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9));
+ background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%);
+ background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%);
+ background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
+ background-repeat: repeat-x;
+ border-color: #2d6ca2;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
+}
+
+.btn-primary:active,
+.btn-primary.active {
+ background-color: #3071a9;
+ border-color: #2d6ca2;
+}
+
+.btn-success {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44));
+ background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%);
+ background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%);
+ background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
+ background-repeat: repeat-x;
+ border-color: #419641;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
+}
+
+.btn-success:active,
+.btn-success.active {
+ background-color: #449d44;
+ border-color: #419641;
+}
+
+.btn-warning {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f));
+ background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%);
+ background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
+ background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
+ background-repeat: repeat-x;
+ border-color: #eb9316;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
+}
+
+.btn-warning:active,
+.btn-warning.active {
+ background-color: #ec971f;
+ border-color: #eb9316;
+}
+
+.btn-danger {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c));
+ background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%);
+ background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%);
+ background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
+ background-repeat: repeat-x;
+ border-color: #c12e2a;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
+}
+
+.btn-danger:active,
+.btn-danger.active {
+ background-color: #c9302c;
+ border-color: #c12e2a;
+}
+
+.btn-info {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5));
+ background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%);
+ background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
+ background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
+ background-repeat: repeat-x;
+ border-color: #2aabd2;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
+}
+
+.btn-info:active,
+.btn-info.active {
+ background-color: #31b0d5;
+ border-color: #2aabd2;
+}
+
+.thumbnail,
+.img-thumbnail {
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
+}
+
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus,
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+ background-color: #357ebd;
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd));
+ background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%);
+ background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%);
+ background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
+}
+
+.navbar {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#f8f8f8));
+ background-image: -webkit-linear-gradient(top, #ffffff, 0%, #f8f8f8, 100%);
+ background-image: -moz-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
+ background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);
+ background-repeat: repeat-x;
+ border-radius: 4px;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
+}
+
+.navbar .navbar-nav > .active > a {
+ background-color: #f8f8f8;
+}
+
+.navbar-brand,
+.navbar-nav > li > a {
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
+}
+
+.navbar-inverse {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#3c3c3c), to(#222222));
+ background-image: -webkit-linear-gradient(top, #3c3c3c, 0%, #222222, 100%);
+ background-image: -moz-linear-gradient(top, #3c3c3c 0%, #222222 100%);
+ background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
+}
+
+.navbar-inverse .navbar-nav > .active > a {
+ background-color: #222222;
+}
+
+.navbar-inverse .navbar-brand,
+.navbar-inverse .navbar-nav > li > a {
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+
+.navbar-static-top,
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+ border-radius: 0;
+}
+
+.alert {
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.alert-success {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#c8e5bc));
+ background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #c8e5bc, 100%);
+ background-image: -moz-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
+ background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
+ background-repeat: repeat-x;
+ border-color: #b2dba1;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
+}
+
+.alert-info {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#b9def0));
+ background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #b9def0, 100%);
+ background-image: -moz-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
+ background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
+ background-repeat: repeat-x;
+ border-color: #9acfea;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
+}
+
+.alert-warning {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#f8efc0));
+ background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #f8efc0, 100%);
+ background-image: -moz-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
+ background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
+ background-repeat: repeat-x;
+ border-color: #f5e79e;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
+}
+
+.alert-danger {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#e7c3c3));
+ background-image: -webkit-linear-gradient(top, #f2dede, 0%, #e7c3c3, 100%);
+ background-image: -moz-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
+ background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
+ background-repeat: repeat-x;
+ border-color: #dca7a7;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
+}
+
+.progress {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ebebeb), to(#f5f5f5));
+ background-image: -webkit-linear-gradient(top, #ebebeb, 0%, #f5f5f5, 100%);
+ background-image: -moz-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
+ background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
+}
+
+.progress-bar {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9));
+ background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%);
+ background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%);
+ background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
+}
+
+.progress-bar-success {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44));
+ background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%);
+ background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%);
+ background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
+}
+
+.progress-bar-info {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5));
+ background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%);
+ background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
+ background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
+}
+
+.progress-bar-warning {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f));
+ background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%);
+ background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
+ background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
+}
+
+.progress-bar-danger {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c));
+ background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%);
+ background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%);
+ background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
+}
+
+.list-group {
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
+}
+
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
+ text-shadow: 0 -1px 0 #3071a9;
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3278b3));
+ background-image: -webkit-linear-gradient(top, #428bca, 0%, #3278b3, 100%);
+ background-image: -moz-linear-gradient(top, #428bca 0%, #3278b3 100%);
+ background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%);
+ background-repeat: repeat-x;
+ border-color: #3278b3;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);
+}
+
+.panel {
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.panel-default > .panel-heading {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f5f5f5), to(#e8e8e8));
+ background-image: -webkit-linear-gradient(top, #f5f5f5, 0%, #e8e8e8, 100%);
+ background-image: -moz-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+ background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
+}
+
+.panel-primary > .panel-heading {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd));
+ background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%);
+ background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%);
+ background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
+}
+
+.panel-success > .panel-heading {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#d0e9c6));
+ background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #d0e9c6, 100%);
+ background-image: -moz-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
+ background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
+}
+
+.panel-info > .panel-heading {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#c4e3f3));
+ background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #c4e3f3, 100%);
+ background-image: -moz-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
+ background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
+}
+
+.panel-warning > .panel-heading {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#faf2cc));
+ background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #faf2cc, 100%);
+ background-image: -moz-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
+ background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
+}
+
+.panel-danger > .panel-heading {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#ebcccc));
+ background-image: -webkit-linear-gradient(top, #f2dede, 0%, #ebcccc, 100%);
+ background-image: -moz-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
+ background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
+}
+
+.well {
+ background-image: -webkit-gradient(linear, left 0%, left 100%, from(#e8e8e8), to(#f5f5f5));
+ background-image: -webkit-linear-gradient(top, #e8e8e8, 0%, #f5f5f5, 100%);
+ background-image: -moz-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
+ background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
+ background-repeat: repeat-x;
+ border-color: #dcdcdc;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
+ -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
+}
\ No newline at end of file
diff --git a/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/css/bootstrap-theme.min.css b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/css/bootstrap-theme.min.css
new file mode 100644
index 0000000..06ac5c8
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/css/bootstrap-theme.min.css
@@ -0,0 +1,356 @@
+.btn-default, .btn-primary, .btn-success, .btn-info, .btn-warning,
+.btn-danger {
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075)
+}
+
+.btn-default:active, .btn-primary:active, .btn-success:active,
+.btn-info:active, .btn-warning:active, .btn-danger:active, .btn-default.active,
+.btn-primary.active, .btn-success.active, .btn-info.active, .btn-warning.active,
+.btn-danger.active {
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125)
+}
+
+.btn:active, .btn.active {
+ background-image: none
+}
+
+.btn-default {
+ text-shadow: 0 1px 0 #fff;
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#fff), to(#e6e6e6));
+ background-image: -webkit-linear-gradient(top, #fff, 0%, #e6e6e6, 100%);
+ background-image: -moz-linear-gradient(top, #fff 0, #e6e6e6 100%);
+ background-image: linear-gradient(to bottom, #fff 0, #e6e6e6 100%);
+ background-repeat: repeat-x;
+ border-color: #e0e0e0;
+ border-color: #ccc;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0)
+}
+
+.btn-default:active, .btn-default.active {
+ background-color: #e6e6e6;
+ border-color: #e0e0e0
+}
+
+.btn-primary {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#428bca), to(#3071a9));
+ background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%);
+ background-image: -moz-linear-gradient(top, #428bca 0, #3071a9 100%);
+ background-image: linear-gradient(to bottom, #428bca 0, #3071a9 100%);
+ background-repeat: repeat-x;
+ border-color: #2d6ca2;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0)
+}
+
+.btn-primary:active, .btn-primary.active {
+ background-color: #3071a9;
+ border-color: #2d6ca2
+}
+
+.btn-success {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#5cb85c), to(#449d44));
+ background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%);
+ background-image: -moz-linear-gradient(top, #5cb85c 0, #449d44 100%);
+ background-image: linear-gradient(to bottom, #5cb85c 0, #449d44 100%);
+ background-repeat: repeat-x;
+ border-color: #419641;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)
+}
+
+.btn-success:active, .btn-success.active {
+ background-color: #449d44;
+ border-color: #419641
+}
+
+.btn-warning {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#f0ad4e), to(#ec971f));
+ background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%);
+ background-image: -moz-linear-gradient(top, #f0ad4e 0, #ec971f 100%);
+ background-image: linear-gradient(to bottom, #f0ad4e 0, #ec971f 100%);
+ background-repeat: repeat-x;
+ border-color: #eb9316;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)
+}
+
+.btn-warning:active, .btn-warning.active {
+ background-color: #ec971f;
+ border-color: #eb9316
+}
+
+.btn-danger {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#d9534f), to(#c9302c));
+ background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%);
+ background-image: -moz-linear-gradient(top, #d9534f 0, #c9302c 100%);
+ background-image: linear-gradient(to bottom, #d9534f 0, #c9302c 100%);
+ background-repeat: repeat-x;
+ border-color: #c12e2a;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)
+}
+
+.btn-danger:active, .btn-danger.active {
+ background-color: #c9302c;
+ border-color: #c12e2a
+}
+
+.btn-info {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#5bc0de), to(#31b0d5));
+ background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%);
+ background-image: -moz-linear-gradient(top, #5bc0de 0, #31b0d5 100%);
+ background-image: linear-gradient(to bottom, #5bc0de 0, #31b0d5 100%);
+ background-repeat: repeat-x;
+ border-color: #2aabd2;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0)
+}
+
+.btn-info:active, .btn-info.active {
+ background-color: #31b0d5;
+ border-color: #2aabd2
+}
+
+.thumbnail, .img-thumbnail {
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075)
+}
+
+.dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus, .dropdown-menu>.active>a,
+.dropdown-menu>.active>a:hover, .dropdown-menu>.active>a:focus {
+ background-color: #357ebd;
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#428bca), to(#357ebd));
+ background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%);
+ background-image: -moz-linear-gradient(top, #428bca 0, #357ebd 100%);
+ background-image: linear-gradient(to bottom, #428bca 0, #357ebd 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)
+}
+
+.navbar {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#fff), to(#f8f8f8));
+ background-image: -webkit-linear-gradient(top, #fff, 0%, #f8f8f8, 100%);
+ background-image: -moz-linear-gradient(top, #fff 0, #f8f8f8 100%);
+ background-image: linear-gradient(to bottom, #fff 0, #f8f8f8 100%);
+ background-repeat: repeat-x;
+ border-radius: 4px;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075)
+}
+
+.navbar .navbar-nav>.active>a {
+ background-color: #f8f8f8
+}
+
+.navbar-brand, .navbar-nav>li>a {
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25)
+}
+
+.navbar-inverse {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#3c3c3c), to(#222));
+ background-image: -webkit-linear-gradient(top, #3c3c3c, 0%, #222, 100%);
+ background-image: -moz-linear-gradient(top, #3c3c3c 0, #222 100%);
+ background-image: linear-gradient(to bottom, #3c3c3c 0, #222 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0)
+}
+
+.navbar-inverse .navbar-nav>.active>a {
+ background-color: #222
+}
+
+.navbar-inverse .navbar-brand, .navbar-inverse .navbar-nav>li>a {
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25)
+}
+
+.navbar-static-top, .navbar-fixed-top, .navbar-fixed-bottom {
+ border-radius: 0
+}
+
+.alert {
+ /* text-shadow: 0 1px 0 rgba(255,255,255,0.2); */
+ /* -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05); */
+ /* box-shadow: inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05); */
+}
+
+.alert-success {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#dff0d8), to(#c8e5bc));
+ background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #c8e5bc, 100%);
+ background-image: -moz-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);
+ background-image: linear-gradient(to bottom, #dff0d8 0, #c8e5bc 100%);
+ background-repeat: repeat-x;
+ border-color: #b2dba1;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0)
+}
+
+.alert-info {
+ /* background-image: -webkit-gradient(linear,left 0,left 100%,from(#d9edf7),to(#b9def0)); */
+ /* background-image: -webkit-linear-gradient(top,#d9edf7,0%,#b9def0,100%); */
+ /* background-image: -moz-linear-gradient(top,#d9edf7 0,#b9def0 100%); */
+ /* background-image: linear-gradient(to bottom,#d9edf7 0,#b9def0 100%); */
+ /* background-repeat: repeat-x; */
+ border-color: #cccccc;
+ /* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffb9def0',GradientType=0); */
+}
+
+.alert-warning {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#fcf8e3), to(#f8efc0));
+ background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #f8efc0, 100%);
+ background-image: -moz-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);
+ background-image: linear-gradient(to bottom, #fcf8e3 0, #f8efc0 100%);
+ background-repeat: repeat-x;
+ border-color: #f5e79e;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0)
+}
+
+.alert-danger {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#f2dede), to(#e7c3c3));
+ background-image: -webkit-linear-gradient(top, #f2dede, 0%, #e7c3c3, 100%);
+ background-image: -moz-linear-gradient(top, #f2dede 0, #e7c3c3 100%);
+ background-image: linear-gradient(to bottom, #f2dede 0, #e7c3c3 100%);
+ background-repeat: repeat-x;
+ border-color: #dca7a7;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0)
+}
+
+.progress {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#ebebeb), to(#f5f5f5));
+ background-image: -webkit-linear-gradient(top, #ebebeb, 0%, #f5f5f5, 100%);
+ background-image: -moz-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);
+ background-image: linear-gradient(to bottom, #ebebeb 0, #f5f5f5 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0)
+}
+
+.progress-bar {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#428bca), to(#3071a9));
+ background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%);
+ background-image: -moz-linear-gradient(top, #428bca 0, #3071a9 100%);
+ background-image: linear-gradient(to bottom, #428bca 0, #3071a9 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0)
+}
+
+.progress-bar-success {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#5cb85c), to(#449d44));
+ background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%);
+ background-image: -moz-linear-gradient(top, #5cb85c 0, #449d44 100%);
+ background-image: linear-gradient(to bottom, #5cb85c 0, #449d44 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)
+}
+
+.progress-bar-info {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#5bc0de), to(#31b0d5));
+ background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%);
+ background-image: -moz-linear-gradient(top, #5bc0de 0, #31b0d5 100%);
+ background-image: linear-gradient(to bottom, #5bc0de 0, #31b0d5 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0)
+}
+
+.progress-bar-warning {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#f0ad4e), to(#ec971f));
+ background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%);
+ background-image: -moz-linear-gradient(top, #f0ad4e 0, #ec971f 100%);
+ background-image: linear-gradient(to bottom, #f0ad4e 0, #ec971f 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)
+}
+
+.progress-bar-danger {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#d9534f), to(#c9302c));
+ background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%);
+ background-image: -moz-linear-gradient(top, #d9534f 0, #c9302c 100%);
+ background-image: linear-gradient(to bottom, #d9534f 0, #c9302c 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)
+}
+
+.list-group {
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075)
+}
+
+.list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus {
+ text-shadow: 0 -1px 0 #3071a9;
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#428bca), to(#3278b3));
+ background-image: -webkit-linear-gradient(top, #428bca, 0%, #3278b3, 100%);
+ background-image: -moz-linear-gradient(top, #428bca 0, #3278b3 100%);
+ background-image: linear-gradient(to bottom, #428bca 0, #3278b3 100%);
+ background-repeat: repeat-x;
+ border-color: #3278b3;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0)
+}
+
+.panel {
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05)
+}
+
+.panel-default>.panel-heading {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#f5f5f5), to(#e8e8e8));
+ background-image: -webkit-linear-gradient(top, #f5f5f5, 0%, #e8e8e8, 100%);
+ background-image: -moz-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);
+ background-image: linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)
+}
+
+.panel-primary>.panel-heading {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#428bca), to(#357ebd));
+ background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%);
+ background-image: -moz-linear-gradient(top, #428bca 0, #357ebd 100%);
+ background-image: linear-gradient(to bottom, #428bca 0, #357ebd 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)
+}
+
+.panel-success>.panel-heading {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#dff0d8), to(#d0e9c6));
+ background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #d0e9c6, 100%);
+ background-image: -moz-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);
+ background-image: linear-gradient(to bottom, #dff0d8 0, #d0e9c6 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0)
+}
+
+.panel-info>.panel-heading {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#d9edf7), to(#c4e3f3));
+ background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #c4e3f3, 100%);
+ background-image: -moz-linear-gradient(top, #d9edf7 0, #c4e3f3 100%);
+ background-image: linear-gradient(to bottom, #d9edf7 0, #c4e3f3 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0)
+}
+
+.panel-warning>.panel-heading {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#fcf8e3), to(#faf2cc));
+ background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #faf2cc, 100%);
+ background-image: -moz-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);
+ background-image: linear-gradient(to bottom, #fcf8e3 0, #faf2cc 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0)
+}
+
+.panel-danger>.panel-heading {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#f2dede), to(#ebcccc));
+ background-image: -webkit-linear-gradient(top, #f2dede, 0%, #ebcccc, 100%);
+ background-image: -moz-linear-gradient(top, #f2dede 0, #ebcccc 100%);
+ background-image: linear-gradient(to bottom, #f2dede 0, #ebcccc 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0)
+}
+
+.well {
+ background-image: -webkit-gradient(linear, left 0, left 100%, from(#e8e8e8), to(#f5f5f5));
+ background-image: -webkit-linear-gradient(top, #e8e8e8, 0%, #f5f5f5, 100%);
+ background-image: -moz-linear-gradient(top, #e8e8e8 0, #f5f5f5 100%);
+ background-image: linear-gradient(to bottom, #e8e8e8 0, #f5f5f5 100%);
+ background-repeat: repeat-x;
+ border-color: #dcdcdc;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
+ -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1)
+}
+
diff --git a/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/css/bootstrap.css b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/css/bootstrap.css
new file mode 100755
index 0000000..0471064
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/css/bootstrap.css
@@ -0,0 +1,5909 @@
+/*!
+ * Bootstrap v3.0.0
+ *
+ * Copyright 2013 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+/*! normalize.css v2.1.0 | MIT License | git.io/normalize */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+nav,
+section,
+summary {
+ display: block;
+}
+audio,
+canvas,
+video {
+ display: inline-block;
+}
+audio:not([controls]) {
+ display: none;
+ height: 0;
+}
+[hidden] {
+ display: none;
+}
+html {
+ font-family: sans-serif;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+body {
+ margin: 0;
+}
+a:focus {
+ outline: thin dotted;
+}
+a:active,
+a:hover {
+ outline: 0;
+}
+h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
+}
+abbr[title] {
+ border-bottom: 1px dotted;
+}
+b,
+strong {
+ font-weight: bold;
+}
+dfn {
+ font-style: italic;
+}
+hr {
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ height: 0;
+}
+mark {
+ background: #ff0;
+ color: #000;
+}
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, serif;
+ font-size: 1em;
+}
+pre {
+ white-space: pre-wrap;
+}
+q {
+ quotes: "\201C" "\201D" "\2018" "\2019";
+}
+small {
+ font-size: 80%;
+}
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+sup {
+ top: -0.5em;
+}
+sub {
+ bottom: -0.25em;
+}
+img {
+ border: 0;
+}
+svg:not(:root) {
+ overflow: hidden;
+}
+figure {
+ margin: 0;
+}
+fieldset {
+ border: 1px solid #c0c0c0;
+ margin: 0 2px;
+ padding: 0.35em 0.625em 0.75em;
+}
+legend {
+ border: 0;
+ padding: 0;
+}
+button,
+input,
+select,
+textarea {
+ font-family: inherit;
+ font-size: 100%;
+ margin: 0;
+}
+button,
+input {
+ line-height: normal;
+}
+button,
+select {
+ text-transform: none;
+}
+button,
+html input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+ -webkit-appearance: button;
+ cursor: pointer;
+}
+button[disabled],
+html input[disabled] {
+ cursor: default;
+}
+input[type="checkbox"],
+input[type="radio"] {
+ box-sizing: border-box;
+ padding: 0;
+}
+input[type="search"] {
+ -webkit-appearance: textfield;
+ -moz-box-sizing: content-box;
+ -webkit-box-sizing: content-box;
+ box-sizing: content-box;
+}
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+textarea {
+ overflow: auto;
+ vertical-align: top;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+*,
+*:before,
+*:after {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+html {
+ font-size: 62.5%;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+body {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ line-height: 1.428571429;
+ color: #333333;
+ background-color: #ffffff;
+}
+input,
+button,
+select,
+textarea {
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+button,
+input,
+select[multiple],
+textarea {
+ background-image: none;
+}
+a {
+ color: #428bca;
+ text-decoration: none;
+}
+a:hover,
+a:focus {
+ color: #2a6496;
+ text-decoration: underline;
+}
+a:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+img {
+ vertical-align: middle;
+}
+.img-responsive {
+ display: block;
+ max-width: 100%;
+ height: auto;
+}
+.img-rounded {
+ border-radius: 6px;
+}
+.img-thumbnail {
+ padding: 4px;
+ line-height: 1.428571429;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-radius: 4px;
+ -webkit-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ display: inline-block;
+ max-width: 100%;
+ height: auto;
+}
+.img-circle {
+ border-radius: 50%;
+}
+hr {
+ margin-top: 20px;
+ margin-bottom: 20px;
+ border: 0;
+ border-top: 1px solid #eeeeee;
+}
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0 0 0 0);
+ border: 0;
+}
+ at media print {
+ * {
+ text-shadow: none !important;
+ color: #000 !important;
+ background: transparent !important;
+ box-shadow: none !important;
+ }
+ a,
+ a:visited {
+ text-decoration: underline;
+ }
+ a[href]:after {
+ content: " (" attr(href) ")";
+ }
+ abbr[title]:after {
+ content: " (" attr(title) ")";
+ }
+ .ir a:after,
+ a[href^="javascript:"]:after,
+ a[href^="#"]:after {
+ content: "";
+ }
+ pre,
+ blockquote {
+ border: 1px solid #999;
+ page-break-inside: avoid;
+ }
+ thead {
+ display: table-header-group;
+ }
+ tr,
+ img {
+ page-break-inside: avoid;
+ }
+ img {
+ max-width: 100% !important;
+ }
+ @page {
+ margin: 2cm .5cm;
+ }
+ p,
+ h2,
+ h3 {
+ orphans: 3;
+ widows: 3;
+ }
+ h2,
+ h3 {
+ page-break-after: avoid;
+ }
+ .navbar {
+ display: none;
+ }
+ .table td,
+ .table th {
+ background-color: #fff !important;
+ }
+ .btn > .caret,
+ .dropup > .btn > .caret {
+ border-top-color: #000 !important;
+ }
+ .label {
+ border: 1px solid #000;
+ }
+ .table {
+ border-collapse: collapse !important;
+ }
+ .table-bordered th,
+ .table-bordered td {
+ border: 1px solid #ddd !important;
+ }
+}
+p {
+ margin: 0 0 10px;
+}
+.lead {
+ margin-bottom: 20px;
+ font-size: 16.099999999999998px;
+ font-weight: 200;
+ line-height: 1.4;
+}
+ at media (min-width: 768px) {
+ .lead {
+ font-size: 21px;
+ }
+}
+small {
+ font-size: 85%;
+}
+cite {
+ font-style: normal;
+}
+.text-muted {
+ color: #999999;
+}
+.text-primary {
+ color: #428bca;
+}
+.text-warning {
+ color: #c09853;
+}
+.text-danger {
+ color: #b94a48;
+}
+.text-success {
+ color: #468847;
+}
+.text-info {
+ color: #3a87ad;
+}
+.text-left {
+ text-align: left;
+}
+.text-right {
+ text-align: right;
+}
+.text-center {
+ text-align: center;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+.h1,
+.h2,
+.h3,
+.h4,
+.h5,
+.h6 {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 500;
+ line-height: 1.1;
+}
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small,
+.h1 small,
+.h2 small,
+.h3 small,
+.h4 small,
+.h5 small,
+.h6 small {
+ font-weight: normal;
+ line-height: 1;
+ color: #999999;
+}
+h1,
+h2,
+h3 {
+ margin-top: 20px;
+ margin-bottom: 10px;
+}
+h4,
+h5,
+h6 {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+h1,
+.h1 {
+ font-size: 36px;
+}
+h2,
+.h2 {
+ font-size: 30px;
+}
+h3,
+.h3 {
+ font-size: 24px;
+}
+h4,
+.h4 {
+ font-size: 18px;
+}
+h5,
+.h5 {
+ font-size: 14px;
+}
+h6,
+.h6 {
+ font-size: 12px;
+}
+h1 small,
+.h1 small {
+ font-size: 24px;
+}
+h2 small,
+.h2 small {
+ font-size: 18px;
+}
+h3 small,
+.h3 small,
+h4 small,
+.h4 small {
+ font-size: 14px;
+}
+.page-header {
+ padding-bottom: 9px;
+ margin: 40px 0 20px;
+ border-bottom: 1px solid #eeeeee;
+}
+ul,
+ol {
+ margin-top: 0;
+ margin-bottom: 10px;
+}
+ul ul,
+ol ul,
+ul ol,
+ol ol {
+ margin-bottom: 0;
+}
+.list-unstyled {
+ padding-left: 0;
+ list-style: none;
+}
+.list-inline {
+ padding-left: 0;
+ list-style: none;
+}
+.list-inline > li {
+ display: inline-block;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+dl {
+ margin-bottom: 20px;
+}
+dt,
+dd {
+ line-height: 1.428571429;
+}
+dt {
+ font-weight: bold;
+}
+dd {
+ margin-left: 0;
+}
+ at media (min-width: 980px) {
+ .dl-horizontal dt {
+ float: left;
+ width: 160px;
+ clear: left;
+ text-align: right;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .dl-horizontal dd {
+ margin-left: 180px;
+ }
+ .dl-horizontal dd:before,
+ .dl-horizontal dd:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+ }
+ .dl-horizontal dd:after {
+ clear: both;
+ }
+ .dl-horizontal dd:before,
+ .dl-horizontal dd:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+ }
+ .dl-horizontal dd:after {
+ clear: both;
+ }
+}
+abbr[title],
+abbr[data-original-title] {
+ cursor: help;
+ border-bottom: 1px dotted #999999;
+}
+abbr.initialism {
+ font-size: 90%;
+ text-transform: uppercase;
+}
+blockquote {
+ padding: 10px 20px;
+ margin: 0 0 20px;
+ border-left: 5px solid #eeeeee;
+}
+blockquote p {
+ font-size: 17.5px;
+ font-weight: 300;
+ line-height: 1.25;
+}
+blockquote p:last-child {
+ margin-bottom: 0;
+}
+blockquote small {
+ display: block;
+ line-height: 1.428571429;
+ color: #999999;
+}
+blockquote small:before {
+ content: '\2014 \00A0';
+}
+blockquote.pull-right {
+ padding-right: 15px;
+ padding-left: 0;
+ border-right: 5px solid #eeeeee;
+ border-left: 0;
+}
+blockquote.pull-right p,
+blockquote.pull-right small {
+ text-align: right;
+}
+blockquote.pull-right small:before {
+ content: '';
+}
+blockquote.pull-right small:after {
+ content: '\00A0 \2014';
+}
+q:before,
+q:after,
+blockquote:before,
+blockquote:after {
+ content: "";
+}
+address {
+ display: block;
+ margin-bottom: 20px;
+ font-style: normal;
+ line-height: 1.428571429;
+}
+code,
+pre {
+ font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
+}
+code {
+ padding: 2px 4px;
+ font-size: 90%;
+ color: #c7254e;
+ background-color: #f9f2f4;
+ white-space: nowrap;
+ border-radius: 4px;
+}
+pre {
+ display: block;
+ padding: 9.5px;
+ margin: 0 0 10px;
+ font-size: 13px;
+ line-height: 1.428571429;
+ word-break: break-all;
+ word-wrap: break-word;
+ color: #333333;
+ background-color: #f5f5f5;
+ border: 1px solid #cccccc;
+ border-radius: 4px;
+}
+pre.prettyprint {
+ margin-bottom: 20px;
+}
+pre code {
+ padding: 0;
+ font-size: inherit;
+ color: inherit;
+ white-space: pre-wrap;
+ background-color: transparent;
+ border: 0;
+}
+.pre-scrollable {
+ max-height: 340px;
+ overflow-y: scroll;
+}
+.container {
+ margin-right: auto;
+ margin-left: auto;
+ padding-left: 15px;
+ padding-right: 15px;
+}
+.container:before,
+.container:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.container:after {
+ clear: both;
+}
+.container:before,
+.container:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.container:after {
+ clear: both;
+}
+.row {
+ margin-left: -15px;
+ margin-right: -15px;
+}
+.row:before,
+.row:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.row:after {
+ clear: both;
+}
+.row:before,
+.row:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.row:after {
+ clear: both;
+}
+.col-xs-1,
+.col-xs-2,
+.col-xs-3,
+.col-xs-4,
+.col-xs-5,
+.col-xs-6,
+.col-xs-7,
+.col-xs-8,
+.col-xs-9,
+.col-xs-10,
+.col-xs-11,
+.col-xs-12,
+.col-sm-1,
+.col-sm-2,
+.col-sm-3,
+.col-sm-4,
+.col-sm-5,
+.col-sm-6,
+.col-sm-7,
+.col-sm-8,
+.col-sm-9,
+.col-sm-10,
+.col-sm-11,
+.col-sm-12,
+.col-md-1,
+.col-md-2,
+.col-md-3,
+.col-md-4,
+.col-md-5,
+.col-md-6,
+.col-md-7,
+.col-md-8,
+.col-md-9,
+.col-md-10,
+.col-md-11,
+.col-md-12,
+.col-lg-1,
+.col-lg-2,
+.col-lg-3,
+.col-lg-4,
+.col-lg-5,
+.col-lg-6,
+.col-lg-7,
+.col-lg-8,
+.col-lg-9,
+.col-lg-10,
+.col-lg-11,
+.col-lg-12 {
+ position: relative;
+ min-height: 1px;
+ padding-left: 15px;
+ padding-right: 15px;
+}
+.col-xs-1,
+.col-xs-2,
+.col-xs-3,
+.col-xs-4,
+.col-xs-5,
+.col-xs-6,
+.col-xs-7,
+.col-xs-8,
+.col-xs-9,
+.col-xs-10,
+.col-xs-11 {
+ float: left;
+}
+.col-xs-1 {
+ width: 8.333333333333332%;
+}
+.col-xs-2 {
+ width: 16.666666666666664%;
+}
+.col-xs-3 {
+ width: 25%;
+}
+.col-xs-4 {
+ width: 33.33333333333333%;
+}
+.col-xs-5 {
+ width: 41.66666666666667%;
+}
+.col-xs-6 {
+ width: 50%;
+}
+.col-xs-7 {
+ width: 58.333333333333336%;
+}
+.col-xs-8 {
+ width: 66.66666666666666%;
+}
+.col-xs-9 {
+ width: 75%;
+}
+.col-xs-10 {
+ width: 83.33333333333334%;
+}
+.col-xs-11 {
+ width: 91.66666666666666%;
+}
+.col-xs-12 {
+ width: 100%;
+}
+ at media (min-width: 980px) {
+ .container {
+ max-width: 750px;
+ }
+ .col-sm-1,
+ .col-sm-2,
+ .col-sm-3,
+ .col-sm-4,
+ .col-sm-5,
+ .col-sm-6,
+ .col-sm-7,
+ .col-sm-8,
+ .col-sm-9,
+ .col-sm-10,
+ .col-sm-11 {
+ float: left;
+ }
+ .col-sm-1 {
+ width: 8.333333333333332%;
+ }
+ .col-sm-2 {
+ width: 16.666666666666664%;
+ }
+ .col-sm-3 {
+ width: 25%;
+ }
+ .col-sm-4 {
+ width: 33.33333333333333%;
+ }
+ .col-sm-5 {
+ width: 41.66666666666667%;
+ }
+ .col-sm-6 {
+ width: 50%;
+ }
+ .col-sm-7 {
+ width: 58.333333333333336%;
+ }
+ .col-sm-8 {
+ width: 66.66666666666666%;
+ }
+ .col-sm-9 {
+ width: 75%;
+ }
+ .col-sm-10 {
+ width: 83.33333333333334%;
+ }
+ .col-sm-11 {
+ width: 91.66666666666666%;
+ }
+ .col-sm-12 {
+ width: 100%;
+ }
+ .col-sm-push-1 {
+ left: 8.333333333333332%;
+ }
+ .col-sm-push-2 {
+ left: 16.666666666666664%;
+ }
+ .col-sm-push-3 {
+ left: 25%;
+ }
+ .col-sm-push-4 {
+ left: 33.33333333333333%;
+ }
+ .col-sm-push-5 {
+ left: 41.66666666666667%;
+ }
+ .col-sm-push-6 {
+ left: 50%;
+ }
+ .col-sm-push-7 {
+ left: 58.333333333333336%;
+ }
+ .col-sm-push-8 {
+ left: 66.66666666666666%;
+ }
+ .col-sm-push-9 {
+ left: 75%;
+ }
+ .col-sm-push-10 {
+ left: 83.33333333333334%;
+ }
+ .col-sm-push-11 {
+ left: 91.66666666666666%;
+ }
+ .col-sm-pull-1 {
+ right: 8.333333333333332%;
+ }
+ .col-sm-pull-2 {
+ right: 16.666666666666664%;
+ }
+ .col-sm-pull-3 {
+ right: 25%;
+ }
+ .col-sm-pull-4 {
+ right: 33.33333333333333%;
+ }
+ .col-sm-pull-5 {
+ right: 41.66666666666667%;
+ }
+ .col-sm-pull-6 {
+ right: 50%;
+ }
+ .col-sm-pull-7 {
+ right: 58.333333333333336%;
+ }
+ .col-sm-pull-8 {
+ right: 66.66666666666666%;
+ }
+ .col-sm-pull-9 {
+ right: 75%;
+ }
+ .col-sm-pull-10 {
+ right: 83.33333333333334%;
+ }
+ .col-sm-pull-11 {
+ right: 91.66666666666666%;
+ }
+ .col-sm-offset-1 {
+ margin-left: 8.333333333333332%;
+ }
+ .col-sm-offset-2 {
+ margin-left: 16.666666666666664%;
+ }
+ .col-sm-offset-3 {
+ margin-left: 25%;
+ }
+ .col-sm-offset-4 {
+ margin-left: 33.33333333333333%;
+ }
+ .col-sm-offset-5 {
+ margin-left: 41.66666666666667%;
+ }
+ .col-sm-offset-6 {
+ margin-left: 50%;
+ }
+ .col-sm-offset-7 {
+ margin-left: 58.333333333333336%;
+ }
+ .col-sm-offset-8 {
+ margin-left: 66.66666666666666%;
+ }
+ .col-sm-offset-9 {
+ margin-left: 75%;
+ }
+ .col-sm-offset-10 {
+ margin-left: 83.33333333333334%;
+ }
+ .col-sm-offset-11 {
+ margin-left: 91.66666666666666%;
+ }
+}
+ at media (min-width: 992px) {
+ .container {
+ max-width: 970px;
+ }
+ .col-md-1,
+ .col-md-2,
+ .col-md-3,
+ .col-md-4,
+ .col-md-5,
+ .col-md-6,
+ .col-md-7,
+ .col-md-8,
+ .col-md-9,
+ .col-md-10,
+ .col-md-11 {
+ float: left;
+ }
+ .col-md-1 {
+ width: 8.333333333333332%;
+ }
+ .col-md-2 {
+ width: 16.666666666666664%;
+ }
+ .col-md-3 {
+ width: 25%;
+ }
+ .col-md-4 {
+ width: 33.33333333333333%;
+ }
+ .col-md-5 {
+ width: 41.66666666666667%;
+ }
+ .col-md-6 {
+ width: 50%;
+ }
+ .col-md-7 {
+ width: 58.333333333333336%;
+ }
+ .col-md-8 {
+ width: 66.66666666666666%;
+ }
+ .col-md-9 {
+ width: 75%;
+ }
+ .col-md-10 {
+ width: 83.33333333333334%;
+ }
+ .col-md-11 {
+ width: 91.66666666666666%;
+ }
+ .col-md-12 {
+ width: 100%;
+ }
+ .col-md-push-0 {
+ left: auto;
+ }
+ .col-md-push-1 {
+ left: 8.333333333333332%;
+ }
+ .col-md-push-2 {
+ left: 16.666666666666664%;
+ }
+ .col-md-push-3 {
+ left: 25%;
+ }
+ .col-md-push-4 {
+ left: 33.33333333333333%;
+ }
+ .col-md-push-5 {
+ left: 41.66666666666667%;
+ }
+ .col-md-push-6 {
+ left: 50%;
+ }
+ .col-md-push-7 {
+ left: 58.333333333333336%;
+ }
+ .col-md-push-8 {
+ left: 66.66666666666666%;
+ }
+ .col-md-push-9 {
+ left: 75%;
+ }
+ .col-md-push-10 {
+ left: 83.33333333333334%;
+ }
+ .col-md-push-11 {
+ left: 91.66666666666666%;
+ }
+ .col-md-pull-0 {
+ right: auto;
+ }
+ .col-md-pull-1 {
+ right: 8.333333333333332%;
+ }
+ .col-md-pull-2 {
+ right: 16.666666666666664%;
+ }
+ .col-md-pull-3 {
+ right: 25%;
+ }
+ .col-md-pull-4 {
+ right: 33.33333333333333%;
+ }
+ .col-md-pull-5 {
+ right: 41.66666666666667%;
+ }
+ .col-md-pull-6 {
+ right: 50%;
+ }
+ .col-md-pull-7 {
+ right: 58.333333333333336%;
+ }
+ .col-md-pull-8 {
+ right: 66.66666666666666%;
+ }
+ .col-md-pull-9 {
+ right: 75%;
+ }
+ .col-md-pull-10 {
+ right: 83.33333333333334%;
+ }
+ .col-md-pull-11 {
+ right: 91.66666666666666%;
+ }
+ .col-md-offset-0 {
+ margin-left: 0;
+ }
+ .col-md-offset-1 {
+ margin-left: 8.333333333333332%;
+ }
+ .col-md-offset-2 {
+ margin-left: 16.666666666666664%;
+ }
+ .col-md-offset-3 {
+ margin-left: 25%;
+ }
+ .col-md-offset-4 {
+ margin-left: 33.33333333333333%;
+ }
+ .col-md-offset-5 {
+ margin-left: 41.66666666666667%;
+ }
+ .col-md-offset-6 {
+ margin-left: 50%;
+ }
+ .col-md-offset-7 {
+ margin-left: 58.333333333333336%;
+ }
+ .col-md-offset-8 {
+ margin-left: 66.66666666666666%;
+ }
+ .col-md-offset-9 {
+ margin-left: 75%;
+ }
+ .col-md-offset-10 {
+ margin-left: 83.33333333333334%;
+ }
+ .col-md-offset-11 {
+ margin-left: 91.66666666666666%;
+ }
+}
+ at media (min-width: 1200px) {
+ .container {
+ max-width: 1170px;
+ }
+ .col-lg-1,
+ .col-lg-2,
+ .col-lg-3,
+ .col-lg-4,
+ .col-lg-5,
+ .col-lg-6,
+ .col-lg-7,
+ .col-lg-8,
+ .col-lg-9,
+ .col-lg-10,
+ .col-lg-11 {
+ float: left;
+ }
+ .col-lg-1 {
+ width: 8.333333333333332%;
+ }
+ .col-lg-2 {
+ width: 16.666666666666664%;
+ }
+ .col-lg-3 {
+ width: 25%;
+ }
+ .col-lg-4 {
+ width: 33.33333333333333%;
+ }
+ .col-lg-5 {
+ width: 41.66666666666667%;
+ }
+ .col-lg-6 {
+ width: 50%;
+ }
+ .col-lg-7 {
+ width: 58.333333333333336%;
+ }
+ .col-lg-8 {
+ width: 66.66666666666666%;
+ }
+ .col-lg-9 {
+ width: 75%;
+ }
+ .col-lg-10 {
+ width: 83.33333333333334%;
+ }
+ .col-lg-11 {
+ width: 91.66666666666666%;
+ }
+ .col-lg-12 {
+ width: 100%;
+ }
+ .col-lg-push-0 {
+ left: auto;
+ }
+ .col-lg-push-1 {
+ left: 8.333333333333332%;
+ }
+ .col-lg-push-2 {
+ left: 16.666666666666664%;
+ }
+ .col-lg-push-3 {
+ left: 25%;
+ }
+ .col-lg-push-4 {
+ left: 33.33333333333333%;
+ }
+ .col-lg-push-5 {
+ left: 41.66666666666667%;
+ }
+ .col-lg-push-6 {
+ left: 50%;
+ }
+ .col-lg-push-7 {
+ left: 58.333333333333336%;
+ }
+ .col-lg-push-8 {
+ left: 66.66666666666666%;
+ }
+ .col-lg-push-9 {
+ left: 75%;
+ }
+ .col-lg-push-10 {
+ left: 83.33333333333334%;
+ }
+ .col-lg-push-11 {
+ left: 91.66666666666666%;
+ }
+ .col-lg-pull-0 {
+ right: auto;
+ }
+ .col-lg-pull-1 {
+ right: 8.333333333333332%;
+ }
+ .col-lg-pull-2 {
+ right: 16.666666666666664%;
+ }
+ .col-lg-pull-3 {
+ right: 25%;
+ }
+ .col-lg-pull-4 {
+ right: 33.33333333333333%;
+ }
+ .col-lg-pull-5 {
+ right: 41.66666666666667%;
+ }
+ .col-lg-pull-6 {
+ right: 50%;
+ }
+ .col-lg-pull-7 {
+ right: 58.333333333333336%;
+ }
+ .col-lg-pull-8 {
+ right: 66.66666666666666%;
+ }
+ .col-lg-pull-9 {
+ right: 75%;
+ }
+ .col-lg-pull-10 {
+ right: 83.33333333333334%;
+ }
+ .col-lg-pull-11 {
+ right: 91.66666666666666%;
+ }
+ .col-lg-offset-0 {
+ margin-left: 0;
+ }
+ .col-lg-offset-1 {
+ margin-left: 8.333333333333332%;
+ }
+ .col-lg-offset-2 {
+ margin-left: 16.666666666666664%;
+ }
+ .col-lg-offset-3 {
+ margin-left: 25%;
+ }
+ .col-lg-offset-4 {
+ margin-left: 33.33333333333333%;
+ }
+ .col-lg-offset-5 {
+ margin-left: 41.66666666666667%;
+ }
+ .col-lg-offset-6 {
+ margin-left: 50%;
+ }
+ .col-lg-offset-7 {
+ margin-left: 58.333333333333336%;
+ }
+ .col-lg-offset-8 {
+ margin-left: 66.66666666666666%;
+ }
+ .col-lg-offset-9 {
+ margin-left: 75%;
+ }
+ .col-lg-offset-10 {
+ margin-left: 83.33333333333334%;
+ }
+ .col-lg-offset-11 {
+ margin-left: 91.66666666666666%;
+ }
+}
+table {
+ max-width: 100%;
+ background-color: transparent;
+}
+th {
+ text-align: left;
+}
+.table {
+ width: 100%;
+ margin-bottom: 20px;
+}
+.table thead > tr > th,
+.table tbody > tr > th,
+.table tfoot > tr > th,
+.table thead > tr > td,
+.table tbody > tr > td,
+.table tfoot > tr > td {
+ padding: 8px;
+ line-height: 1.428571429;
+ vertical-align: top;
+ border-top: 1px solid #dddddd;
+}
+.table thead > tr > th {
+ vertical-align: bottom;
+ border-bottom: 2px solid #dddddd;
+}
+.table caption + thead tr:first-child th,
+.table colgroup + thead tr:first-child th,
+.table thead:first-child tr:first-child th,
+.table caption + thead tr:first-child td,
+.table colgroup + thead tr:first-child td,
+.table thead:first-child tr:first-child td {
+ border-top: 0;
+}
+.table tbody + tbody {
+ border-top: 2px solid #dddddd;
+}
+.table .table {
+ background-color: #ffffff;
+}
+.table-condensed thead > tr > th,
+.table-condensed tbody > tr > th,
+.table-condensed tfoot > tr > th,
+.table-condensed thead > tr > td,
+.table-condensed tbody > tr > td,
+.table-condensed tfoot > tr > td {
+ padding: 5px;
+}
+.table-bordered {
+ border: 1px solid #dddddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > tbody > tr > th,
+.table-bordered > tfoot > tr > th,
+.table-bordered > thead > tr > td,
+.table-bordered > tbody > tr > td,
+.table-bordered > tfoot > tr > td {
+ border: 1px solid #dddddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > thead > tr > td {
+ border-bottom-width: 2px;
+}
+.table-striped > tbody > tr:nth-child(odd) > td,
+.table-striped > tbody > tr:nth-child(odd) > th {
+ background-color: #f9f9f9;
+}
+.table-hover > tbody > tr:hover > td,
+.table-hover > tbody > tr:hover > th {
+ background-color: #f5f5f5;
+}
+table col[class*="col-"] {
+ float: none;
+ display: table-column;
+}
+table td[class*="col-"],
+table th[class*="col-"] {
+ float: none;
+ display: table-cell;
+}
+.table > thead > tr > td.active,
+.table > tbody > tr > td.active,
+.table > tfoot > tr > td.active,
+.table > thead > tr > th.active,
+.table > tbody > tr > th.active,
+.table > tfoot > tr > th.active,
+.table > thead > tr.active > td,
+.table > tbody > tr.active > td,
+.table > tfoot > tr.active > td,
+.table > thead > tr.active > th,
+.table > tbody > tr.active > th,
+.table > tfoot > tr.active > th {
+ background-color: #f5f5f5;
+}
+.table > thead > tr > td.success,
+.table > tbody > tr > td.success,
+.table > tfoot > tr > td.success,
+.table > thead > tr > th.success,
+.table > tbody > tr > th.success,
+.table > tfoot > tr > th.success,
+.table > thead > tr.success > td,
+.table > tbody > tr.success > td,
+.table > tfoot > tr.success > td,
+.table > thead > tr.success > th,
+.table > tbody > tr.success > th,
+.table > tfoot > tr.success > th {
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+}
+.table-hover > tbody > tr > td.success:hover,
+.table-hover > tbody > tr > th.success:hover,
+.table-hover > tbody > tr.success:hover > td {
+ background-color: #d0e9c6;
+ border-color: #c9e2b3;
+}
+.table > thead > tr > td.danger,
+.table > tbody > tr > td.danger,
+.table > tfoot > tr > td.danger,
+.table > thead > tr > th.danger,
+.table > tbody > tr > th.danger,
+.table > tfoot > tr > th.danger,
+.table > thead > tr.danger > td,
+.table > tbody > tr.danger > td,
+.table > tfoot > tr.danger > td,
+.table > thead > tr.danger > th,
+.table > tbody > tr.danger > th,
+.table > tfoot > tr.danger > th {
+ background-color: #f2dede;
+ border-color: #eed3d7;
+}
+.table-hover > tbody > tr > td.danger:hover,
+.table-hover > tbody > tr > th.danger:hover,
+.table-hover > tbody > tr.danger:hover > td {
+ background-color: #ebcccc;
+ border-color: #e6c1c7;
+}
+.table > thead > tr > td.warning,
+.table > tbody > tr > td.warning,
+.table > tfoot > tr > td.warning,
+.table > thead > tr > th.warning,
+.table > tbody > tr > th.warning,
+.table > tfoot > tr > th.warning,
+.table > thead > tr.warning > td,
+.table > tbody > tr.warning > td,
+.table > tfoot > tr.warning > td,
+.table > thead > tr.warning > th,
+.table > tbody > tr.warning > th,
+.table > tfoot > tr.warning > th {
+ background-color: #fcf8e3;
+ border-color: #fbeed5;
+}
+.table-hover > tbody > tr > td.warning:hover,
+.table-hover > tbody > tr > th.warning:hover,
+.table-hover > tbody > tr.warning:hover > td {
+ background-color: #faf2cc;
+ border-color: #f8e5be;
+}
+ at media (max-width: 980px) {
+ .table-responsive {
+ width: 100%;
+ margin-bottom: 15px;
+ overflow-y: hidden;
+ overflow-x: scroll;
+ border: 1px solid #dddddd;
+ }
+ .table-responsive > .table {
+ margin-bottom: 0;
+ background-color: #fff;
+ }
+ .table-responsive > .table > thead > tr > th,
+ .table-responsive > .table > tbody > tr > th,
+ .table-responsive > .table > tfoot > tr > th,
+ .table-responsive > .table > thead > tr > td,
+ .table-responsive > .table > tbody > tr > td,
+ .table-responsive > .table > tfoot > tr > td {
+ white-space: nowrap;
+ }
+ .table-responsive > .table-bordered {
+ border: 0;
+ }
+ .table-responsive > .table-bordered > thead > tr > th:first-child,
+ .table-responsive > .table-bordered > tbody > tr > th:first-child,
+ .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+ .table-responsive > .table-bordered > thead > tr > td:first-child,
+ .table-responsive > .table-bordered > tbody > tr > td:first-child,
+ .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+ border-left: 0;
+ }
+ .table-responsive > .table-bordered > thead > tr > th:last-child,
+ .table-responsive > .table-bordered > tbody > tr > th:last-child,
+ .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+ .table-responsive > .table-bordered > thead > tr > td:last-child,
+ .table-responsive > .table-bordered > tbody > tr > td:last-child,
+ .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+ border-right: 0;
+ }
+ .table-responsive > .table-bordered > thead > tr:last-child > th,
+ .table-responsive > .table-bordered > tbody > tr:last-child > th,
+ .table-responsive > .table-bordered > tfoot > tr:last-child > th,
+ .table-responsive > .table-bordered > thead > tr:last-child > td,
+ .table-responsive > .table-bordered > tbody > tr:last-child > td,
+ .table-responsive > .table-bordered > tfoot > tr:last-child > td {
+ border-bottom: 0;
+ }
+}
+fieldset {
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+legend {
+ display: block;
+ width: 100%;
+ padding: 0;
+ margin-bottom: 20px;
+ font-size: 21px;
+ line-height: inherit;
+ color: #333333;
+ border: 0;
+ border-bottom: 1px solid #e5e5e5;
+}
+label {
+ display: inline-block;
+ margin-bottom: 5px;
+ font-weight: bold;
+}
+input[type="search"] {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+input[type="radio"],
+input[type="checkbox"] {
+ margin: 4px 0 0;
+ margin-top: 1px \9;
+ /* IE8-9 */
+
+ line-height: normal;
+}
+input[type="file"] {
+ display: block;
+}
+select[multiple],
+select[size] {
+ height: auto;
+}
+select optgroup {
+ font-size: inherit;
+ font-style: inherit;
+ font-family: inherit;
+}
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+input[type="number"]::-webkit-outer-spin-button,
+input[type="number"]::-webkit-inner-spin-button {
+ height: auto;
+}
+.form-control:-moz-placeholder {
+ color: #999999;
+}
+.form-control::-moz-placeholder {
+ color: #999999;
+}
+.form-control:-ms-input-placeholder {
+ color: #999999;
+}
+.form-control::-webkit-input-placeholder {
+ color: #999999;
+}
+.form-control {
+ display: block;
+ width: 100%;
+ height: 34px;
+ padding: 6px 12px;
+ font-size: 14px;
+ line-height: 1.428571429;
+ color: #555555;
+ vertical-align: middle;
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+ transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+}
+.form-control:focus {
+ border-color: #66afe9;
+ outline: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
+}
+.form-control[disabled],
+.form-control[readonly],
+fieldset[disabled] .form-control {
+ cursor: not-allowed;
+ background-color: #eeeeee;
+}
+textarea.form-control {
+ height: auto;
+}
+.form-group {
+ margin-bottom: 15px;
+}
+.radio,
+.checkbox {
+ display: block;
+ min-height: 20px;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding-left: 20px;
+ vertical-align: middle;
+}
+.radio label,
+.checkbox label {
+ display: inline;
+ margin-bottom: 0;
+ font-weight: normal;
+ cursor: pointer;
+}
+.radio input[type="radio"],
+.radio-inline input[type="radio"],
+.checkbox input[type="checkbox"],
+.checkbox-inline input[type="checkbox"] {
+ float: left;
+ margin-left: -20px;
+}
+.radio + .radio,
+.checkbox + .checkbox {
+ margin-top: -5px;
+}
+.radio-inline,
+.checkbox-inline {
+ display: inline-block;
+ padding-left: 20px;
+ margin-bottom: 0;
+ vertical-align: middle;
+ font-weight: normal;
+ cursor: pointer;
+}
+.radio-inline + .radio-inline,
+.checkbox-inline + .checkbox-inline {
+ margin-top: 0;
+ margin-left: 10px;
+}
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+.radio[disabled],
+.radio-inline[disabled],
+.checkbox[disabled],
+.checkbox-inline[disabled],
+fieldset[disabled] input[type="radio"],
+fieldset[disabled] input[type="checkbox"],
+fieldset[disabled] .radio,
+fieldset[disabled] .radio-inline,
+fieldset[disabled] .checkbox,
+fieldset[disabled] .checkbox-inline {
+ cursor: not-allowed;
+}
+.input-sm {
+ height: 30px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+select.input-sm {
+ height: 30px;
+ line-height: 30px;
+}
+textarea.input-sm {
+ height: auto;
+}
+.input-lg {
+ height: 45px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
+}
+select.input-lg {
+ height: 45px;
+ line-height: 45px;
+}
+textarea.input-lg {
+ height: auto;
+}
+.has-warning .help-block,
+.has-warning .control-label {
+ color: #c09853;
+}
+.has-warning .form-control {
+ border-color: #c09853;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+.has-warning .form-control:focus {
+ border-color: #a47e3c;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+}
+.has-warning .input-group-addon {
+ color: #c09853;
+ border-color: #c09853;
+ background-color: #fcf8e3;
+}
+.has-error .help-block,
+.has-error .control-label {
+ color: #b94a48;
+}
+.has-error .form-control {
+ border-color: #b94a48;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+.has-error .form-control:focus {
+ border-color: #953b39;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+}
+.has-error .input-group-addon {
+ color: #b94a48;
+ border-color: #b94a48;
+ background-color: #f2dede;
+}
+.has-success .help-block,
+.has-success .control-label {
+ color: #468847;
+}
+.has-success .form-control {
+ border-color: #468847;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+.has-success .form-control:focus {
+ border-color: #356635;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+}
+.has-success .input-group-addon {
+ color: #468847;
+ border-color: #468847;
+ background-color: #dff0d8;
+}
+.form-control-static {
+ margin-bottom: 0;
+ padding-top: 7px;
+}
+.help-block {
+ display: block;
+ margin-top: 5px;
+ margin-bottom: 10px;
+ color: #737373;
+}
+ at media (min-width: 980px) {
+ .form-inline .form-group {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .form-inline .form-control {
+ display: inline-block;
+ }
+ .form-inline .radio,
+ .form-inline .checkbox {
+ display: inline-block;
+ margin-top: 0;
+ margin-bottom: 0;
+ padding-left: 0;
+ }
+ .form-inline .radio input[type="radio"],
+ .form-inline .checkbox input[type="checkbox"] {
+ float: none;
+ margin-left: 0;
+ }
+}
+.form-horizontal .control-label,
+.form-horizontal .radio,
+.form-horizontal .checkbox,
+.form-horizontal .radio-inline,
+.form-horizontal .checkbox-inline {
+ margin-top: 0;
+ margin-bottom: 0;
+ padding-top: 7px;
+}
+.form-horizontal .form-group {
+ margin-left: -15px;
+ margin-right: -15px;
+}
+.form-horizontal .form-group:before,
+.form-horizontal .form-group:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.form-horizontal .form-group:after {
+ clear: both;
+}
+.form-horizontal .form-group:before,
+.form-horizontal .form-group:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.form-horizontal .form-group:after {
+ clear: both;
+}
+ at media (min-width: 980px) {
+ .form-horizontal .control-label {
+ text-align: right;
+ }
+}
+.btn {
+ display: inline-block;
+ padding: 6px 12px;
+ margin-bottom: 0;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 1.428571429;
+ text-align: center;
+ vertical-align: middle;
+ cursor: pointer;
+ border: 1px solid transparent;
+ border-radius: 4px;
+ white-space: nowrap;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+}
+.btn:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+.btn:hover,
+.btn:focus {
+ color: #333333;
+ text-decoration: none;
+}
+.btn:active,
+.btn.active {
+ outline: 0;
+ background-image: none;
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
+.btn.disabled,
+.btn[disabled],
+fieldset[disabled] .btn {
+ cursor: not-allowed;
+ pointer-events: none;
+ opacity: 0.65;
+ filter: alpha(opacity=65);
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+.btn-default {
+ color: #333333;
+ background-color: #ffffff;
+ border-color: #cccccc;
+}
+.btn-default:hover,
+.btn-default:focus,
+.btn-default:active,
+.btn-default.active,
+.open .dropdown-toggle.btn-default {
+ color: #333333;
+ background-color: #ebebeb;
+ border-color: #adadad;
+}
+.btn-default:active,
+.btn-default.active,
+.open .dropdown-toggle.btn-default {
+ background-image: none;
+}
+.btn-default.disabled,
+.btn-default[disabled],
+fieldset[disabled] .btn-default,
+.btn-default.disabled:hover,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus,
+.btn-default[disabled]:focus,
+fieldset[disabled] .btn-default:focus,
+.btn-default.disabled:active,
+.btn-default[disabled]:active,
+fieldset[disabled] .btn-default:active,
+.btn-default.disabled.active,
+.btn-default[disabled].active,
+fieldset[disabled] .btn-default.active {
+ background-color: #ffffff;
+ border-color: #cccccc;
+}
+.btn-primary {
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #357ebd;
+}
+.btn-primary:hover,
+.btn-primary:focus,
+.btn-primary:active,
+.btn-primary.active,
+.open .dropdown-toggle.btn-primary {
+ color: #ffffff;
+ background-color: #3276b1;
+ border-color: #285e8e;
+}
+.btn-primary:active,
+.btn-primary.active,
+.open .dropdown-toggle.btn-primary {
+ background-image: none;
+}
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+ background-color: #428bca;
+ border-color: #357ebd;
+}
+.btn-warning {
+ color: #ffffff;
+ background-color: #f0ad4e;
+ border-color: #eea236;
+}
+.btn-warning:hover,
+.btn-warning:focus,
+.btn-warning:active,
+.btn-warning.active,
+.open .dropdown-toggle.btn-warning {
+ color: #ffffff;
+ background-color: #ed9c28;
+ border-color: #d58512;
+}
+.btn-warning:active,
+.btn-warning.active,
+.open .dropdown-toggle.btn-warning {
+ background-image: none;
+}
+.btn-warning.disabled,
+.btn-warning[disabled],
+fieldset[disabled] .btn-warning,
+.btn-warning.disabled:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled:active,
+.btn-warning[disabled]:active,
+fieldset[disabled] .btn-warning:active,
+.btn-warning.disabled.active,
+.btn-warning[disabled].active,
+fieldset[disabled] .btn-warning.active {
+ background-color: #f0ad4e;
+ border-color: #eea236;
+}
+.btn-danger {
+ color: #ffffff;
+ background-color: #d9534f;
+ border-color: #d43f3a;
+}
+.btn-danger:hover,
+.btn-danger:focus,
+.btn-danger:active,
+.btn-danger.active,
+.open .dropdown-toggle.btn-danger {
+ color: #ffffff;
+ background-color: #d2322d;
+ border-color: #ac2925;
+}
+.btn-danger:active,
+.btn-danger.active,
+.open .dropdown-toggle.btn-danger {
+ background-image: none;
+}
+.btn-danger.disabled,
+.btn-danger[disabled],
+fieldset[disabled] .btn-danger,
+.btn-danger.disabled:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled:active,
+.btn-danger[disabled]:active,
+fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active,
+.btn-danger[disabled].active,
+fieldset[disabled] .btn-danger.active {
+ background-color: #d9534f;
+ border-color: #d43f3a;
+}
+.btn-success {
+ color: #ffffff;
+ background-color: #5cb85c;
+ border-color: #4cae4c;
+}
+.btn-success:hover,
+.btn-success:focus,
+.btn-success:active,
+.btn-success.active,
+.open .dropdown-toggle.btn-success {
+ color: #ffffff;
+ background-color: #47a447;
+ border-color: #398439;
+}
+.btn-success:active,
+.btn-success.active,
+.open .dropdown-toggle.btn-success {
+ background-image: none;
+}
+.btn-success.disabled,
+.btn-success[disabled],
+fieldset[disabled] .btn-success,
+.btn-success.disabled:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled:active,
+.btn-success[disabled]:active,
+fieldset[disabled] .btn-success:active,
+.btn-success.disabled.active,
+.btn-success[disabled].active,
+fieldset[disabled] .btn-success.active {
+ background-color: #5cb85c;
+ border-color: #4cae4c;
+}
+.btn-info {
+ color: #ffffff;
+ background-color: #5bc0de;
+ border-color: #46b8da;
+}
+.btn-info:hover,
+.btn-info:focus,
+.btn-info:active,
+.btn-info.active,
+.open .dropdown-toggle.btn-info {
+ color: #ffffff;
+ background-color: #39b3d7;
+ border-color: #269abc;
+}
+.btn-info:active,
+.btn-info.active,
+.open .dropdown-toggle.btn-info {
+ background-image: none;
+}
+.btn-info.disabled,
+.btn-info[disabled],
+fieldset[disabled] .btn-info,
+.btn-info.disabled:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled:active,
+.btn-info[disabled]:active,
+fieldset[disabled] .btn-info:active,
+.btn-info.disabled.active,
+.btn-info[disabled].active,
+fieldset[disabled] .btn-info.active {
+ background-color: #5bc0de;
+ border-color: #46b8da;
+}
+.btn-link {
+ color: #428bca;
+ font-weight: normal;
+ cursor: pointer;
+ border-radius: 0;
+}
+.btn-link,
+.btn-link:active,
+.btn-link[disabled],
+fieldset[disabled] .btn-link {
+ background-color: transparent;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+.btn-link,
+.btn-link:hover,
+.btn-link:focus,
+.btn-link:active {
+ border-color: transparent;
+}
+.btn-link:hover,
+.btn-link:focus {
+ color: #2a6496;
+ text-decoration: underline;
+ background-color: transparent;
+}
+.btn-link[disabled]:hover,
+fieldset[disabled] .btn-link:hover,
+.btn-link[disabled]:focus,
+fieldset[disabled] .btn-link:focus {
+ color: #999999;
+ text-decoration: none;
+}
+.btn-lg {
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
+}
+.btn-sm,
+.btn-xs {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+.btn-xs {
+ padding: 1px 5px;
+}
+.btn-block {
+ display: block;
+ width: 100%;
+ padding-left: 0;
+ padding-right: 0;
+}
+.btn-block + .btn-block {
+ margin-top: 5px;
+}
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+ width: 100%;
+}
+ at font-face {
+ font-family: 'Glyphicons Halflings';
+ src: url('../fonts/glyphicons-halflings-regular.eot');
+ src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');
+}
+.glyphicon {
+ position: relative;
+ top: 1px;
+ display: inline-block;
+ font-family: 'Glyphicons Halflings';
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ -webkit-font-smoothing: antialiased;
+}
+.glyphicon-asterisk:before {
+ content: "\2a";
+}
+.glyphicon-plus:before {
+ content: "\2b";
+}
+.glyphicon-euro:before {
+ content: "\20ac";
+}
+.glyphicon-minus:before {
+ content: "\2212";
+}
+.glyphicon-cloud:before {
+ content: "\2601";
+}
+.glyphicon-envelope:before {
+ content: "\2709";
+}
+.glyphicon-pencil:before {
+ content: "\270f";
+}
+.glyphicon-glass:before {
+ content: "\e001";
+}
+.glyphicon-music:before {
+ content: "\e002";
+}
+.glyphicon-search:before {
+ content: "\e003";
+}
+.glyphicon-heart:before {
+ content: "\e005";
+}
+.glyphicon-star:before {
+ content: "\e006";
+}
+.glyphicon-star-empty:before {
+ content: "\e007";
+}
+.glyphicon-user:before {
+ content: "\e008";
+}
+.glyphicon-film:before {
+ content: "\e009";
+}
+.glyphicon-th-large:before {
+ content: "\e010";
+}
+.glyphicon-th:before {
+ content: "\e011";
+}
+.glyphicon-th-list:before {
+ content: "\e012";
+}
+.glyphicon-ok:before {
+ content: "\e013";
+}
+.glyphicon-remove:before {
+ content: "\e014";
+}
+.glyphicon-zoom-in:before {
+ content: "\e015";
+}
+.glyphicon-zoom-out:before {
+ content: "\e016";
+}
+.glyphicon-off:before {
+ content: "\e017";
+}
+.glyphicon-signal:before {
+ content: "\e018";
+}
+.glyphicon-cog:before {
+ content: "\e019";
+}
+.glyphicon-trash:before {
+ content: "\e020";
+}
+.glyphicon-home:before {
+ content: "\e021";
+}
+.glyphicon-file:before {
+ content: "\e022";
+}
+.glyphicon-time:before {
+ content: "\e023";
+}
+.glyphicon-road:before {
+ content: "\e024";
+}
+.glyphicon-download-alt:before {
+ content: "\e025";
+}
+.glyphicon-download:before {
+ content: "\e026";
+}
+.glyphicon-upload:before {
+ content: "\e027";
+}
+.glyphicon-inbox:before {
+ content: "\e028";
+}
+.glyphicon-play-circle:before {
+ content: "\e029";
+}
+.glyphicon-repeat:before {
+ content: "\e030";
+}
+.glyphicon-refresh:before {
+ content: "\e031";
+}
+.glyphicon-list-alt:before {
+ content: "\e032";
+}
+.glyphicon-flag:before {
+ content: "\e034";
+}
+.glyphicon-headphones:before {
+ content: "\e035";
+}
+.glyphicon-volume-off:before {
+ content: "\e036";
+}
+.glyphicon-volume-down:before {
+ content: "\e037";
+}
+.glyphicon-volume-up:before {
+ content: "\e038";
+}
+.glyphicon-qrcode:before {
+ content: "\e039";
+}
+.glyphicon-barcode:before {
+ content: "\e040";
+}
+.glyphicon-tag:before {
+ content: "\e041";
+}
+.glyphicon-tags:before {
+ content: "\e042";
+}
+.glyphicon-book:before {
+ content: "\e043";
+}
+.glyphicon-print:before {
+ content: "\e045";
+}
+.glyphicon-font:before {
+ content: "\e047";
+}
+.glyphicon-bold:before {
+ content: "\e048";
+}
+.glyphicon-italic:before {
+ content: "\e049";
+}
+.glyphicon-text-height:before {
+ content: "\e050";
+}
+.glyphicon-text-width:before {
+ content: "\e051";
+}
+.glyphicon-align-left:before {
+ content: "\e052";
+}
+.glyphicon-align-center:before {
+ content: "\e053";
+}
+.glyphicon-align-right:before {
+ content: "\e054";
+}
+.glyphicon-align-justify:before {
+ content: "\e055";
+}
+.glyphicon-list:before {
+ content: "\e056";
+}
+.glyphicon-indent-left:before {
+ content: "\e057";
+}
+.glyphicon-indent-right:before {
+ content: "\e058";
+}
+.glyphicon-facetime-video:before {
+ content: "\e059";
+}
+.glyphicon-picture:before {
+ content: "\e060";
+}
+.glyphicon-map-marker:before {
+ content: "\e062";
+}
+.glyphicon-adjust:before {
+ content: "\e063";
+}
+.glyphicon-tint:before {
+ content: "\e064";
+}
+.glyphicon-edit:before {
+ content: "\e065";
+}
+.glyphicon-share:before {
+ content: "\e066";
+}
+.glyphicon-check:before {
+ content: "\e067";
+}
+.glyphicon-move:before {
+ content: "\e068";
+}
+.glyphicon-step-backward:before {
+ content: "\e069";
+}
+.glyphicon-fast-backward:before {
+ content: "\e070";
+}
+.glyphicon-backward:before {
+ content: "\e071";
+}
+.glyphicon-play:before {
+ content: "\e072";
+}
+.glyphicon-pause:before {
+ content: "\e073";
+}
+.glyphicon-stop:before {
+ content: "\e074";
+}
+.glyphicon-forward:before {
+ content: "\e075";
+}
+.glyphicon-fast-forward:before {
+ content: "\e076";
+}
+.glyphicon-step-forward:before {
+ content: "\e077";
+}
+.glyphicon-eject:before {
+ content: "\e078";
+}
+.glyphicon-chevron-left:before {
+ content: "\e079";
+}
+.glyphicon-chevron-right:before {
+ content: "\e080";
+}
+.glyphicon-plus-sign:before {
+ content: "\e081";
+}
+.glyphicon-minus-sign:before {
+ content: "\e082";
+}
+.glyphicon-remove-sign:before {
+ content: "\e083";
+}
+.glyphicon-ok-sign:before {
+ content: "\e084";
+}
+.glyphicon-question-sign:before {
+ content: "\e085";
+}
+.glyphicon-info-sign:before {
+ content: "\e086";
+}
+.glyphicon-screenshot:before {
+ content: "\e087";
+}
+.glyphicon-remove-circle:before {
+ content: "\e088";
+}
+.glyphicon-ok-circle:before {
+ content: "\e089";
+}
+.glyphicon-ban-circle:before {
+ content: "\e090";
+}
+.glyphicon-arrow-left:before {
+ content: "\e091";
+}
+.glyphicon-arrow-right:before {
+ content: "\e092";
+}
+.glyphicon-arrow-up:before {
+ content: "\e093";
+}
+.glyphicon-arrow-down:before {
+ content: "\e094";
+}
+.glyphicon-share-alt:before {
+ content: "\e095";
+}
+.glyphicon-resize-full:before {
+ content: "\e096";
+}
+.glyphicon-resize-small:before {
+ content: "\e097";
+}
+.glyphicon-exclamation-sign:before {
+ content: "\e101";
+}
+.glyphicon-gift:before {
+ content: "\e102";
+}
+.glyphicon-leaf:before {
+ content: "\e103";
+}
+.glyphicon-eye-open:before {
+ content: "\e105";
+}
+.glyphicon-eye-close:before {
+ content: "\e106";
+}
+.glyphicon-warning-sign:before {
+ content: "\e107";
+}
+.glyphicon-plane:before {
+ content: "\e108";
+}
+.glyphicon-random:before {
+ content: "\e110";
+}
+.glyphicon-comment:before {
+ content: "\e111";
+}
+.glyphicon-magnet:before {
+ content: "\e112";
+}
+.glyphicon-chevron-up:before {
+ content: "\e113";
+}
+.glyphicon-chevron-down:before {
+ content: "\e114";
+}
+.glyphicon-retweet:before {
+ content: "\e115";
+}
+.glyphicon-shopping-cart:before {
+ content: "\e116";
+}
+.glyphicon-folder-close:before {
+ content: "\e117";
+}
+.glyphicon-folder-open:before {
+ content: "\e118";
+}
+.glyphicon-resize-vertical:before {
+ content: "\e119";
+}
+.glyphicon-resize-horizontal:before {
+ content: "\e120";
+}
+.glyphicon-hdd:before {
+ content: "\e121";
+}
+.glyphicon-bullhorn:before {
+ content: "\e122";
+}
+.glyphicon-certificate:before {
+ content: "\e124";
+}
+.glyphicon-thumbs-up:before {
+ content: "\e125";
+}
+.glyphicon-thumbs-down:before {
+ content: "\e126";
+}
+.glyphicon-hand-right:before {
+ content: "\e127";
+}
+.glyphicon-hand-left:before {
+ content: "\e128";
+}
+.glyphicon-hand-up:before {
+ content: "\e129";
+}
+.glyphicon-hand-down:before {
+ content: "\e130";
+}
+.glyphicon-circle-arrow-right:before {
+ content: "\e131";
+}
+.glyphicon-circle-arrow-left:before {
+ content: "\e132";
+}
+.glyphicon-circle-arrow-up:before {
+ content: "\e133";
+}
+.glyphicon-circle-arrow-down:before {
+ content: "\e134";
+}
+.glyphicon-globe:before {
+ content: "\e135";
+}
+.glyphicon-tasks:before {
+ content: "\e137";
+}
+.glyphicon-filter:before {
+ content: "\e138";
+}
+.glyphicon-fullscreen:before {
+ content: "\e140";
+}
+.glyphicon-dashboard:before {
+ content: "\e141";
+}
+.glyphicon-heart-empty:before {
+ content: "\e143";
+}
+.glyphicon-link:before {
+ content: "\e144";
+}
+.glyphicon-phone:before {
+ content: "\e145";
+}
+.glyphicon-usd:before {
+ content: "\e148";
+}
+.glyphicon-gbp:before {
+ content: "\e149";
+}
+.glyphicon-sort:before {
+ content: "\e150";
+}
+.glyphicon-sort-by-alphabet:before {
+ content: "\e151";
+}
+.glyphicon-sort-by-alphabet-alt:before {
+ content: "\e152";
+}
+.glyphicon-sort-by-order:before {
+ content: "\e153";
+}
+.glyphicon-sort-by-order-alt:before {
+ content: "\e154";
+}
+.glyphicon-sort-by-attributes:before {
+ content: "\e155";
+}
+.glyphicon-sort-by-attributes-alt:before {
+ content: "\e156";
+}
+.glyphicon-unchecked:before {
+ content: "\e157";
+}
+.glyphicon-expand:before {
+ content: "\e158";
+}
+.glyphicon-collapse-down:before {
+ content: "\e159";
+}
+.glyphicon-collapse-up:before {
+ content: "\e160";
+}
+.glyphicon-log-in:before {
+ content: "\e161";
+}
+.glyphicon-flash:before {
+ content: "\e162";
+}
+.glyphicon-log-out:before {
+ content: "\e163";
+}
+.glyphicon-new-window:before {
+ content: "\e164";
+}
+.glyphicon-record:before {
+ content: "\e165";
+}
+.glyphicon-save:before {
+ content: "\e166";
+}
+.glyphicon-open:before {
+ content: "\e167";
+}
+.glyphicon-saved:before {
+ content: "\e168";
+}
+.glyphicon-import:before {
+ content: "\e169";
+}
+.glyphicon-export:before {
+ content: "\e170";
+}
+.glyphicon-send:before {
+ content: "\e171";
+}
+.glyphicon-floppy-disk:before {
+ content: "\e172";
+}
+.glyphicon-floppy-saved:before {
+ content: "\e173";
+}
+.glyphicon-floppy-remove:before {
+ content: "\e174";
+}
+.glyphicon-floppy-save:before {
+ content: "\e175";
+}
+.glyphicon-floppy-open:before {
+ content: "\e176";
+}
+.glyphicon-credit-card:before {
+ content: "\e177";
+}
+.glyphicon-transfer:before {
+ content: "\e178";
+}
+.glyphicon-cutlery:before {
+ content: "\e179";
+}
+.glyphicon-header:before {
+ content: "\e180";
+}
+.glyphicon-compressed:before {
+ content: "\e181";
+}
+.glyphicon-earphone:before {
+ content: "\e182";
+}
+.glyphicon-phone-alt:before {
+ content: "\e183";
+}
+.glyphicon-tower:before {
+ content: "\e184";
+}
+.glyphicon-stats:before {
+ content: "\e185";
+}
+.glyphicon-sd-video:before {
+ content: "\e186";
+}
+.glyphicon-hd-video:before {
+ content: "\e187";
+}
+.glyphicon-subtitles:before {
+ content: "\e188";
+}
+.glyphicon-sound-stereo:before {
+ content: "\e189";
+}
+.glyphicon-sound-dolby:before {
+ content: "\e190";
+}
+.glyphicon-sound-5-1:before {
+ content: "\e191";
+}
+.glyphicon-sound-6-1:before {
+ content: "\e192";
+}
+.glyphicon-sound-7-1:before {
+ content: "\e193";
+}
+.glyphicon-copyright-mark:before {
+ content: "\e194";
+}
+.glyphicon-registration-mark:before {
+ content: "\e195";
+}
+.glyphicon-cloud-download:before {
+ content: "\e197";
+}
+.glyphicon-cloud-upload:before {
+ content: "\e198";
+}
+.glyphicon-tree-conifer:before {
+ content: "\e199";
+}
+.glyphicon-tree-deciduous:before {
+ content: "\e200";
+}
+.glyphicon-briefcase:before {
+ content: "\1f4bc";
+}
+.glyphicon-calendar:before {
+ content: "\1f4c5";
+}
+.glyphicon-pushpin:before {
+ content: "\1f4cc";
+}
+.glyphicon-paperclip:before {
+ content: "\1f4ce";
+}
+.glyphicon-camera:before {
+ content: "\1f4f7";
+}
+.glyphicon-lock:before {
+ content: "\1f512";
+}
+.glyphicon-bell:before {
+ content: "\1f514";
+}
+.glyphicon-bookmark:before {
+ content: "\1f516";
+}
+.glyphicon-fire:before {
+ content: "\1f525";
+}
+.glyphicon-wrench:before {
+ content: "\1f527";
+}
+.btn-default .caret {
+ border-top-color: #333333;
+}
+.btn-primary .caret,
+.btn-success .caret,
+.btn-warning .caret,
+.btn-danger .caret,
+.btn-info .caret {
+ border-top-color: #fff;
+}
+.dropup .btn-default .caret {
+ border-bottom-color: #333333;
+}
+.dropup .btn-primary .caret,
+.dropup .btn-success .caret,
+.dropup .btn-warning .caret,
+.dropup .btn-danger .caret,
+.dropup .btn-info .caret {
+ border-bottom-color: #fff;
+}
+.btn-group,
+.btn-group-vertical {
+ position: relative;
+ display: inline-block;
+ vertical-align: middle;
+}
+.btn-group > .btn,
+.btn-group-vertical > .btn {
+ position: relative;
+ float: left;
+}
+.btn-group > .btn:hover,
+.btn-group-vertical > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group-vertical > .btn:focus,
+.btn-group > .btn:active,
+.btn-group-vertical > .btn:active,
+.btn-group > .btn.active,
+.btn-group-vertical > .btn.active {
+ z-index: 2;
+}
+.btn-group > .btn:focus,
+.btn-group-vertical > .btn:focus {
+ outline: none;
+}
+.btn-group .btn + .btn,
+.btn-group .btn + .btn-group,
+.btn-group .btn-group + .btn,
+.btn-group .btn-group + .btn-group {
+ margin-left: -1px;
+}
+.btn-toolbar:before,
+.btn-toolbar:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.btn-toolbar:after {
+ clear: both;
+}
+.btn-toolbar:before,
+.btn-toolbar:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.btn-toolbar:after {
+ clear: both;
+}
+.btn-toolbar .btn-group {
+ float: left;
+}
+.btn-toolbar > .btn + .btn,
+.btn-toolbar > .btn-group + .btn,
+.btn-toolbar > .btn + .btn-group,
+.btn-toolbar > .btn-group + .btn-group {
+ margin-left: 5px;
+}
+.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
+ border-radius: 0;
+}
+.btn-group > .btn:first-child {
+ margin-left: 0;
+}
+.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+}
+.btn-group > .btn:last-child:not(:first-child),
+.btn-group > .dropdown-toggle:not(:first-child) {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+}
+.btn-group > .btn-group {
+ float: left;
+}
+.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
+ border-radius: 0;
+}
+.btn-group > .btn-group:first-child > .btn:last-child,
+.btn-group > .btn-group:first-child > .dropdown-toggle {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+}
+.btn-group > .btn-group:last-child > .btn:first-child {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+}
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+ outline: 0;
+}
+.btn-group-xs > .btn {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+ padding: 1px 5px;
+}
+.btn-group-sm > .btn {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+.btn-group-lg > .btn {
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
+}
+.btn-group > .btn + .dropdown-toggle {
+ padding-left: 8px;
+ padding-right: 8px;
+}
+.btn-group > .btn-lg + .dropdown-toggle {
+ padding-left: 12px;
+ padding-right: 12px;
+}
+.btn-group.open .dropdown-toggle {
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
+.btn .caret {
+ margin-left: 0;
+}
+.btn-lg .caret {
+ border-width: 5px 5px 0;
+ border-bottom-width: 0;
+}
+.dropup .btn-lg .caret {
+ border-width: 0 5px 5px;
+}
+.btn-group-vertical > .btn,
+.btn-group-vertical > .btn-group {
+ display: block;
+ float: none;
+ width: 100%;
+ max-width: 100%;
+}
+.btn-group-vertical > .btn-group:before,
+.btn-group-vertical > .btn-group:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.btn-group-vertical > .btn-group:after {
+ clear: both;
+}
+.btn-group-vertical > .btn-group:before,
+.btn-group-vertical > .btn-group:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.btn-group-vertical > .btn-group:after {
+ clear: both;
+}
+.btn-group-vertical > .btn-group > .btn {
+ float: none;
+}
+.btn-group-vertical > .btn + .btn,
+.btn-group-vertical > .btn + .btn-group,
+.btn-group-vertical > .btn-group + .btn,
+.btn-group-vertical > .btn-group + .btn-group {
+ margin-top: -1px;
+ margin-left: 0;
+}
+.btn-group-vertical > .btn:not(:first-child):not(:last-child) {
+ border-radius: 0;
+}
+.btn-group-vertical > .btn:first-child:not(:last-child) {
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn:last-child:not(:first-child) {
+ border-bottom-left-radius: 4px;
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
+ border-radius: 0;
+}
+.btn-group-vertical > .btn-group:first-child > .btn:last-child,
+.btn-group-vertical > .btn-group:first-child > .dropdown-toggle {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn-group:last-child > .btn:first-child {
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+.btn-group-justified {
+ display: table;
+ width: 100%;
+ table-layout: fixed;
+ border-collapse: separate;
+}
+.btn-group-justified .btn {
+ float: none;
+ display: table-cell;
+ width: 1%;
+}
+[data-toggle="buttons"] > .btn > input[type="radio"],
+[data-toggle="buttons"] > .btn > input[type="checkbox"] {
+ display: none;
+}
+.input-group {
+ position: relative;
+ display: table;
+ border-collapse: separate;
+}
+.input-group.col {
+ float: none;
+ padding-left: 0;
+ padding-right: 0;
+}
+.input-group .form-control {
+ width: 100%;
+ margin-bottom: 0;
+}
+.input-group-lg > .form-control,
+.input-group-lg > .input-group-addon,
+.input-group-lg > .input-group-btn > .btn {
+ height: 45px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
+}
+select.input-group-lg > .form-control,
+select.input-group-lg > .input-group-addon,
+select.input-group-lg > .input-group-btn > .btn {
+ height: 45px;
+ line-height: 45px;
+}
+textarea.input-group-lg > .form-control,
+textarea.input-group-lg > .input-group-addon,
+textarea.input-group-lg > .input-group-btn > .btn {
+ height: auto;
+}
+.input-group-sm > .form-control,
+.input-group-sm > .input-group-addon,
+.input-group-sm > .input-group-btn > .btn {
+ height: 30px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+select.input-group-sm > .form-control,
+select.input-group-sm > .input-group-addon,
+select.input-group-sm > .input-group-btn > .btn {
+ height: 30px;
+ line-height: 30px;
+}
+textarea.input-group-sm > .form-control,
+textarea.input-group-sm > .input-group-addon,
+textarea.input-group-sm > .input-group-btn > .btn {
+ height: auto;
+}
+.input-group-addon,
+.input-group-btn,
+.input-group .form-control {
+ display: table-cell;
+}
+.input-group-addon:not(:first-child):not(:last-child),
+.input-group-btn:not(:first-child):not(:last-child),
+.input-group .form-control:not(:first-child):not(:last-child) {
+ border-radius: 0;
+}
+.input-group-addon,
+.input-group-btn {
+ width: 1%;
+ white-space: nowrap;
+ vertical-align: middle;
+}
+.input-group-addon {
+ padding: 6px 12px;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 1;
+ text-align: center;
+ background-color: #eeeeee;
+ border: 1px solid #cccccc;
+ border-radius: 4px;
+}
+.input-group-addon.input-sm {
+ padding: 5px 10px;
+ font-size: 12px;
+ border-radius: 3px;
+}
+.input-group-addon.input-lg {
+ padding: 10px 16px;
+ font-size: 18px;
+ border-radius: 6px;
+}
+.input-group-addon input[type="radio"],
+.input-group-addon input[type="checkbox"] {
+ margin-top: 0;
+}
+.input-group .form-control:first-child,
+.input-group-addon:first-child,
+.input-group-btn:first-child > .btn,
+.input-group-btn:first-child > .dropdown-toggle,
+.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+}
+.input-group-addon:first-child {
+ border-right: 0;
+}
+.input-group .form-control:last-child,
+.input-group-addon:last-child,
+.input-group-btn:last-child > .btn,
+.input-group-btn:last-child > .dropdown-toggle,
+.input-group-btn:first-child > .btn:not(:first-child) {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+}
+.input-group-addon:last-child {
+ border-left: 0;
+}
+.input-group-btn {
+ position: relative;
+ white-space: nowrap;
+}
+.input-group-btn > .btn {
+ position: relative;
+}
+.input-group-btn > .btn + .btn {
+ margin-left: -4px;
+}
+.input-group-btn > .btn:hover,
+.input-group-btn > .btn:active {
+ z-index: 2;
+}
+.nav {
+ margin-bottom: 0;
+ padding-left: 0;
+ list-style: none;
+}
+.nav:before,
+.nav:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.nav:after {
+ clear: both;
+}
+.nav:before,
+.nav:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.nav:after {
+ clear: both;
+}
+.nav > li {
+ position: relative;
+ display: block;
+}
+.nav > li > a {
+ position: relative;
+ display: block;
+ padding: 10px 15px;
+}
+.nav > li > a:hover,
+.nav > li > a:focus {
+ text-decoration: none;
+ background-color: #eeeeee;
+}
+.nav > li.disabled > a {
+ color: #999999;
+}
+.nav > li.disabled > a:hover,
+.nav > li.disabled > a:focus {
+ color: #999999;
+ text-decoration: none;
+ background-color: transparent;
+ cursor: not-allowed;
+}
+.nav .open > a,
+.nav .open > a:hover,
+.nav .open > a:focus {
+ background-color: #eeeeee;
+ border-color: #428bca;
+}
+.nav .nav-divider {
+ height: 1px;
+ margin: 9px 0;
+ overflow: hidden;
+ background-color: #e5e5e5;
+}
+.nav > li > a > img {
+ max-width: none;
+}
+.nav-tabs {
+ border-bottom: 1px solid #dddddd;
+}
+.nav-tabs > li {
+ float: left;
+ margin-bottom: -1px;
+}
+.nav-tabs > li > a {
+ margin-right: 2px;
+ line-height: 1.428571429;
+ border: 1px solid transparent;
+ border-radius: 4px 4px 0 0;
+}
+.nav-tabs > li > a:hover {
+ border-color: #eeeeee #eeeeee #dddddd;
+}
+.nav-tabs > li.active > a,
+.nav-tabs > li.active > a:hover,
+.nav-tabs > li.active > a:focus {
+ color: #555555;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-bottom-color: transparent;
+ cursor: default;
+}
+.nav-tabs.nav-justified {
+ width: 100%;
+ border-bottom: 0;
+}
+.nav-tabs.nav-justified > li {
+ float: none;
+}
+.nav-tabs.nav-justified > li > a {
+ text-align: center;
+}
+ at media (min-width: 980px) {
+ .nav-tabs.nav-justified > li {
+ display: table-cell;
+ width: 1%;
+ }
+}
+.nav-tabs.nav-justified > li > a {
+ border-bottom: 1px solid #dddddd;
+ margin-right: 0;
+}
+.nav-tabs.nav-justified > .active > a {
+ border-bottom-color: #ffffff;
+}
+.nav-pills > li {
+ float: left;
+}
+.nav-pills > li > a {
+ border-radius: 5px;
+}
+.nav-pills > li + li {
+ margin-left: 2px;
+}
+.nav-pills > li.active > a,
+.nav-pills > li.active > a:hover,
+.nav-pills > li.active > a:focus {
+ color: #ffffff;
+ background-color: #428bca;
+}
+.nav-stacked > li {
+ float: none;
+}
+.nav-stacked > li + li {
+ margin-top: 2px;
+ margin-left: 0;
+}
+.nav-justified {
+ width: 100%;
+}
+.nav-justified > li {
+ float: none;
+}
+.nav-justified > li > a {
+ text-align: center;
+}
+ at media (min-width: 980px) {
+ .nav-justified > li {
+ display: table-cell;
+ width: 1%;
+ }
+}
+.nav-tabs-justified {
+ border-bottom: 0;
+}
+.nav-tabs-justified > li > a {
+ border-bottom: 1px solid #dddddd;
+ margin-right: 0;
+}
+.nav-tabs-justified > .active > a {
+ border-bottom-color: #ffffff;
+}
+.tabbable:before,
+.tabbable:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.tabbable:after {
+ clear: both;
+}
+.tabbable:before,
+.tabbable:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.tabbable:after {
+ clear: both;
+}
+.tab-content > .tab-pane,
+.pill-content > .pill-pane {
+ display: none;
+}
+.tab-content > .active,
+.pill-content > .active {
+ display: block;
+}
+.nav .caret {
+ border-top-color: #428bca;
+ border-bottom-color: #428bca;
+}
+.nav a:hover .caret {
+ border-top-color: #2a6496;
+ border-bottom-color: #2a6496;
+}
+.nav-tabs .dropdown-menu {
+ margin-top: -1px;
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+.navbar {
+ position: relative;
+ z-index: 1000;
+ min-height: 50px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
+}
+.navbar:before,
+.navbar:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.navbar:after {
+ clear: both;
+}
+.navbar:before,
+.navbar:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.navbar:after {
+ clear: both;
+}
+ at media (min-width: 980px) {
+ .navbar {
+ border-radius: 4px;
+ }
+}
+.navbar-header:before,
+.navbar-header:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.navbar-header:after {
+ clear: both;
+}
+.navbar-header:before,
+.navbar-header:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.navbar-header:after {
+ clear: both;
+}
+ at media (min-width: 980px) {
+ .navbar-header {
+ float: left;
+ }
+}
+.navbar-collapse {
+ max-height: 340px;
+ overflow-x: visible;
+ padding-right: 15px;
+ padding-left: 15px;
+ border-top: 1px solid transparent;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
+ -webkit-overflow-scrolling: touch;
+}
+.navbar-collapse:before,
+.navbar-collapse:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.navbar-collapse:after {
+ clear: both;
+}
+.navbar-collapse:before,
+.navbar-collapse:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.navbar-collapse:after {
+ clear: both;
+}
+.navbar-collapse.in {
+ overflow-y: auto;
+}
+ at media (min-width: 980px) {
+ .navbar-collapse {
+ width: auto;
+ border-top: 0;
+ box-shadow: none;
+ }
+ .navbar-collapse.collapse {
+ display: block !important;
+ height: auto !important;
+ padding-bottom: 0;
+ overflow: visible !important;
+ }
+ .navbar-collapse.in {
+ overflow-y: visible;
+ }
+ .navbar-collapse .navbar-nav.navbar-left:first-child {
+ margin-left: -15px;
+ }
+ .navbar-collapse .navbar-nav.navbar-right:last-child {
+ margin-right: -15px;
+ }
+ .navbar-collapse .navbar-text:last-child {
+ margin-right: 0;
+ }
+}
+.container > .navbar-header,
+.container > .navbar-collapse {
+ margin-right: -15px;
+ margin-left: -15px;
+}
+ at media (min-width: 980px) {
+ .container > .navbar-header,
+ .container > .navbar-collapse {
+ margin-right: 0;
+ margin-left: 0;
+ }
+}
+.navbar-static-top {
+ border-width: 0 0 1px;
+}
+ at media (min-width: 980px) {
+ .navbar-static-top {
+ border-radius: 0;
+ }
+}
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+ position: fixed;
+ right: 0;
+ left: 0;
+ border-width: 0 0 1px;
+}
+ at media (min-width: 980px) {
+ .navbar-fixed-top,
+ .navbar-fixed-bottom {
+ border-radius: 0;
+ }
+}
+.navbar-fixed-top {
+ z-index: 1030;
+ top: 0;
+}
+.navbar-fixed-bottom {
+ bottom: 0;
+ margin-bottom: 0;
+}
+.navbar-brand {
+ float: left;
+ padding: 15px 15px;
+ font-size: 18px;
+ line-height: 20px;
+}
+.navbar-brand:hover,
+.navbar-brand:focus {
+ text-decoration: none;
+}
+ at media (min-width: 980px) {
+ .navbar > .container .navbar-brand {
+ margin-left: -15px;
+ }
+}
+.navbar-toggle {
+ position: relative;
+ float: right;
+ margin-right: 15px;
+ padding: 9px 10px;
+ margin-top: 8px;
+ margin-bottom: 8px;
+ background-color: transparent;
+ border: 1px solid transparent;
+ border-radius: 4px;
+}
+.navbar-toggle .icon-bar {
+ display: block;
+ width: 22px;
+ height: 2px;
+ border-radius: 1px;
+}
+.navbar-toggle .icon-bar + .icon-bar {
+ margin-top: 4px;
+}
+ at media (min-width: 980px) {
+ .navbar-toggle {
+ display: none;
+ }
+}
+.navbar-nav {
+ margin: 7.5px -15px;
+}
+.navbar-nav > li > a {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ line-height: 20px;
+}
+ at media (max-width: 979px) {
+ .navbar-nav .open .dropdown-menu {
+ position: static;
+ float: none;
+ width: auto;
+ margin-top: 0;
+ background-color: transparent;
+ border: 0;
+ box-shadow: none;
+ }
+ .navbar-nav .open .dropdown-menu > li > a,
+ .navbar-nav .open .dropdown-menu .dropdown-header {
+ padding: 5px 15px 5px 25px;
+ }
+ .navbar-nav .open .dropdown-menu > li > a {
+ line-height: 20px;
+ }
+ .navbar-nav .open .dropdown-menu > li > a:hover,
+ .navbar-nav .open .dropdown-menu > li > a:focus {
+ background-image: none;
+ }
+}
+ at media (min-width: 980px) {
+ .navbar-nav {
+ float: left;
+ margin: 0;
+ }
+ .navbar-nav > li {
+ float: left;
+ }
+ .navbar-nav > li > a {
+ padding-top: 15px;
+ padding-bottom: 15px;
+ }
+}
+ at media (min-width: 980px) {
+ .navbar-left {
+ float: left !important;
+ }
+ .navbar-right {
+ float: right !important;
+ }
+}
+.navbar-form {
+ margin-left: -15px;
+ margin-right: -15px;
+ padding: 10px 15px;
+ border-top: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+ margin-top: 8px;
+ margin-bottom: 8px;
+}
+ at media (min-width: 980px) {
+ .navbar-form .form-group {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .navbar-form .form-control {
+ display: inline-block;
+ }
+ .navbar-form .radio,
+ .navbar-form .checkbox {
+ display: inline-block;
+ margin-top: 0;
+ margin-bottom: 0;
+ padding-left: 0;
+ }
+ .navbar-form .radio input[type="radio"],
+ .navbar-form .checkbox input[type="checkbox"] {
+ float: none;
+ margin-left: 0;
+ }
+}
+ at media (max-width: 979px) {
+ .navbar-form .form-group {
+ margin-bottom: 5px;
+ }
+}
+ at media (min-width: 980px) {
+ .navbar-form {
+ width: auto;
+ border: 0;
+ margin-left: 0;
+ margin-right: 0;
+ padding-top: 0;
+ padding-bottom: 0;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ }
+}
+.navbar-nav > li > .dropdown-menu {
+ margin-top: 0;
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.navbar-nav.pull-right > li > .dropdown-menu,
+.navbar-nav > li > .dropdown-menu.pull-right {
+ left: auto;
+ right: 0;
+}
+.navbar-btn {
+ margin-top: 8px;
+ margin-bottom: 8px;
+}
+.navbar-text {
+ float: left;
+ margin-top: 15px;
+ margin-bottom: 15px;
+}
+ at media (min-width: 980px) {
+ .navbar-text {
+ margin-left: 15px;
+ margin-right: 15px;
+ }
+}
+.navbar-default {
+ background-color: #f8f8f8;
+ border-color: #e7e7e7;
+}
+.navbar-default .navbar-brand {
+ color: #777777;
+}
+.navbar-default .navbar-brand:hover,
+.navbar-default .navbar-brand:focus {
+ color: #5e5e5e;
+ background-color: transparent;
+}
+.navbar-default .navbar-text {
+ color: #777777;
+}
+.navbar-default .navbar-nav > li > a {
+ color: #777777;
+}
+.navbar-default .navbar-nav > li > a:hover,
+.navbar-default .navbar-nav > li > a:focus {
+ color: #333333;
+ background-color: transparent;
+}
+.navbar-default .navbar-nav > .active > a,
+.navbar-default .navbar-nav > .active > a:hover,
+.navbar-default .navbar-nav > .active > a:focus {
+ color: #555555;
+ background-color: #e7e7e7;
+}
+.navbar-default .navbar-nav > .disabled > a,
+.navbar-default .navbar-nav > .disabled > a:hover,
+.navbar-default .navbar-nav > .disabled > a:focus {
+ color: #cccccc;
+ background-color: transparent;
+}
+.navbar-default .navbar-toggle {
+ border-color: #dddddd;
+}
+.navbar-default .navbar-toggle:hover,
+.navbar-default .navbar-toggle:focus {
+ background-color: #dddddd;
+}
+.navbar-default .navbar-toggle .icon-bar {
+ background-color: #cccccc;
+}
+.navbar-default .navbar-collapse,
+.navbar-default .navbar-form {
+ border-color: #e6e6e6;
+}
+.navbar-default .navbar-nav > .dropdown > a:hover .caret,
+.navbar-default .navbar-nav > .dropdown > a:focus .caret {
+ border-top-color: #333333;
+ border-bottom-color: #333333;
+}
+.navbar-default .navbar-nav > .open > a,
+.navbar-default .navbar-nav > .open > a:hover,
+.navbar-default .navbar-nav > .open > a:focus {
+ background-color: #e7e7e7;
+ color: #555555;
+}
+.navbar-default .navbar-nav > .open > a .caret,
+.navbar-default .navbar-nav > .open > a:hover .caret,
+.navbar-default .navbar-nav > .open > a:focus .caret {
+ border-top-color: #555555;
+ border-bottom-color: #555555;
+}
+.navbar-default .navbar-nav > .dropdown > a .caret {
+ border-top-color: #777777;
+ border-bottom-color: #777777;
+}
+ at media (max-width: 979px) {
+ .navbar-default .navbar-nav .open .dropdown-menu > li > a {
+ color: #777777;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {
+ color: #333333;
+ background-color: transparent;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a,
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {
+ color: #555555;
+ background-color: #e7e7e7;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+ color: #cccccc;
+ background-color: transparent;
+ }
+}
+.navbar-default .navbar-link {
+ color: #777777;
+}
+.navbar-default .navbar-link:hover {
+ color: #333333;
+}
+.navbar-inverse {
+ background-color: #222222;
+ border-color: #080808;
+}
+.navbar-inverse .navbar-brand {
+ color: #999999;
+}
+.navbar-inverse .navbar-brand:hover,
+.navbar-inverse .navbar-brand:focus {
+ color: #ffffff;
+ background-color: transparent;
+}
+.navbar-inverse .navbar-text {
+ color: #999999;
+}
+.navbar-inverse .navbar-nav > li > a {
+ color: #999999;
+}
+.navbar-inverse .navbar-nav > li > a:hover,
+.navbar-inverse .navbar-nav > li > a:focus {
+ color: #ffffff;
+ background-color: transparent;
+}
+.navbar-inverse .navbar-nav > .active > a,
+.navbar-inverse .navbar-nav > .active > a:hover,
+.navbar-inverse .navbar-nav > .active > a:focus {
+ color: #ffffff;
+ background-color: #080808;
+}
+.navbar-inverse .navbar-nav > .disabled > a,
+.navbar-inverse .navbar-nav > .disabled > a:hover,
+.navbar-inverse .navbar-nav > .disabled > a:focus {
+ color: #444444;
+ background-color: transparent;
+}
+.navbar-inverse .navbar-toggle {
+ border-color: #333333;
+}
+.navbar-inverse .navbar-toggle:hover,
+.navbar-inverse .navbar-toggle:focus {
+ background-color: #333333;
+}
+.navbar-inverse .navbar-toggle .icon-bar {
+ background-color: #ffffff;
+}
+.navbar-inverse .navbar-collapse,
+.navbar-inverse .navbar-form {
+ border-color: #101010;
+}
+.navbar-inverse .navbar-nav > .open > a,
+.navbar-inverse .navbar-nav > .open > a:hover,
+.navbar-inverse .navbar-nav > .open > a:focus {
+ background-color: #080808;
+ color: #ffffff;
+}
+.navbar-inverse .navbar-nav > .dropdown > a:hover .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+.navbar-inverse .navbar-nav > .dropdown > a .caret {
+ border-top-color: #999999;
+ border-bottom-color: #999999;
+}
+.navbar-inverse .navbar-nav > .open > a .caret,
+.navbar-inverse .navbar-nav > .open > a:hover .caret,
+.navbar-inverse .navbar-nav > .open > a:focus .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+ at media (max-width: 979px) {
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {
+ border-color: #080808;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {
+ color: #999999;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {
+ color: #ffffff;
+ background-color: transparent;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
+ color: #ffffff;
+ background-color: #080808;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+ color: #444444;
+ background-color: transparent;
+ }
+}
+.navbar-inverse .navbar-link {
+ color: #999999;
+}
+.navbar-inverse .navbar-link:hover {
+ color: #ffffff;
+}
+.breadcrumb {
+ padding: 8px 15px;
+ margin-bottom: 20px;
+ list-style: none;
+ background-color: #f5f5f5;
+ border-radius: 4px;
+}
+.breadcrumb > li {
+ display: inline-block;
+}
+.breadcrumb > li + li:before {
+ content: "/\00a0";
+ padding: 0 5px;
+ color: #cccccc;
+}
+.breadcrumb > .active {
+ color: #999999;
+}
+.pagination {
+ display: inline-block;
+ padding-left: 0;
+ margin: 20px 0;
+ border-radius: 4px;
+}
+.pagination > li {
+ display: inline;
+}
+.pagination > li > a,
+.pagination > li > span {
+ position: relative;
+ float: left;
+ padding: 6px 12px;
+ line-height: 1.428571429;
+ text-decoration: none;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ margin-left: -1px;
+}
+.pagination > li:first-child > a,
+.pagination > li:first-child > span {
+ margin-left: 0;
+ border-bottom-left-radius: 4px;
+ border-top-left-radius: 4px;
+}
+.pagination > li:last-child > a,
+.pagination > li:last-child > span {
+ border-bottom-right-radius: 4px;
+ border-top-right-radius: 4px;
+}
+.pagination > li > a:hover,
+.pagination > li > span:hover,
+.pagination > li > a:focus,
+.pagination > li > span:focus {
+ background-color: #eeeeee;
+}
+.pagination > .active > a,
+.pagination > .active > span,
+.pagination > .active > a:hover,
+.pagination > .active > span:hover,
+.pagination > .active > a:focus,
+.pagination > .active > span:focus {
+ z-index: 2;
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #428bca;
+ cursor: default;
+}
+.pagination > .disabled > span,
+.pagination > .disabled > a,
+.pagination > .disabled > a:hover,
+.pagination > .disabled > a:focus {
+ color: #999999;
+ background-color: #ffffff;
+ border-color: #dddddd;
+ cursor: not-allowed;
+}
+.pagination-lg > li > a,
+.pagination-lg > li > span {
+ padding: 10px 16px;
+ font-size: 18px;
+}
+.pagination-lg > li:first-child > a,
+.pagination-lg > li:first-child > span {
+ border-bottom-left-radius: 6px;
+ border-top-left-radius: 6px;
+}
+.pagination-lg > li:last-child > a,
+.pagination-lg > li:last-child > span {
+ border-bottom-right-radius: 6px;
+ border-top-right-radius: 6px;
+}
+.pagination-sm > li > a,
+.pagination-sm > li > span {
+ padding: 5px 10px;
+ font-size: 12px;
+}
+.pagination-sm > li:first-child > a,
+.pagination-sm > li:first-child > span {
+ border-bottom-left-radius: 3px;
+ border-top-left-radius: 3px;
+}
+.pagination-sm > li:last-child > a,
+.pagination-sm > li:last-child > span {
+ border-bottom-right-radius: 3px;
+ border-top-right-radius: 3px;
+}
+.pager {
+ padding-left: 0;
+ margin: 20px 0;
+ list-style: none;
+ text-align: center;
+}
+.pager:before,
+.pager:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.pager:after {
+ clear: both;
+}
+.pager:before,
+.pager:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.pager:after {
+ clear: both;
+}
+.pager li {
+ display: inline;
+}
+.pager li > a,
+.pager li > span {
+ display: inline-block;
+ padding: 5px 14px;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-radius: 15px;
+}
+.pager li > a:hover,
+.pager li > a:focus {
+ text-decoration: none;
+ background-color: #eeeeee;
+}
+.pager .next > a,
+.pager .next > span {
+ float: right;
+}
+.pager .previous > a,
+.pager .previous > span {
+ float: left;
+}
+.pager .disabled > a,
+.pager .disabled > a:hover,
+.pager .disabled > a:focus,
+.pager .disabled > span {
+ color: #999999;
+ background-color: #ffffff;
+ cursor: not-allowed;
+}
+.label {
+ display: inline;
+ padding: .2em .6em .3em;
+ font-size: 75%;
+ font-weight: bold;
+ line-height: 1;
+ color: #ffffff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: .25em;
+}
+.label[href]:hover,
+.label[href]:focus {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
+}
+.label:empty {
+ display: none;
+}
+.label-default {
+ background-color: #999999;
+}
+.label-default[href]:hover,
+.label-default[href]:focus {
+ background-color: #808080;
+}
+.label-primary {
+ background-color: #428bca;
+}
+.label-primary[href]:hover,
+.label-primary[href]:focus {
+ background-color: #3071a9;
+}
+.label-success {
+ background-color: #5cb85c;
+}
+.label-success[href]:hover,
+.label-success[href]:focus {
+ background-color: #449d44;
+}
+.label-info {
+ background-color: #5bc0de;
+}
+.label-info[href]:hover,
+.label-info[href]:focus {
+ background-color: #31b0d5;
+}
+.label-warning {
+ background-color: #f0ad4e;
+}
+.label-warning[href]:hover,
+.label-warning[href]:focus {
+ background-color: #ec971f;
+}
+.label-danger {
+ background-color: #d9534f;
+}
+.label-danger[href]:hover,
+.label-danger[href]:focus {
+ background-color: #c9302c;
+}
+.badge {
+ display: inline-block;
+ min-width: 10px;
+ padding: 3px 7px;
+ font-size: 12px;
+ font-weight: bold;
+ color: #ffffff;
+ line-height: 1;
+ vertical-align: baseline;
+ white-space: nowrap;
+ text-align: center;
+ background-color: #999999;
+ border-radius: 10px;
+}
+.badge:empty {
+ display: none;
+}
+a.badge:hover,
+a.badge:focus {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
+}
+.btn .badge {
+ position: relative;
+ top: -1px;
+}
+a.list-group-item.active > .badge,
+.nav-pills > .active > a > .badge {
+ color: #428bca;
+ background-color: #ffffff;
+}
+.nav-pills > li > a > .badge {
+ margin-left: 3px;
+}
+.jumbotron {
+ padding: 30px;
+ margin-bottom: 30px;
+ font-size: 21px;
+ font-weight: 200;
+ line-height: 2.1428571435;
+ color: inherit;
+ background-color: #eeeeee;
+}
+.jumbotron h1 {
+ line-height: 1;
+ color: inherit;
+}
+.jumbotron p {
+ line-height: 1.4;
+}
+.container .jumbotron {
+ border-radius: 6px;
+}
+ at media screen and (min-width: 980px) {
+ .jumbotron {
+ padding-top: 48px;
+ padding-bottom: 48px;
+ }
+ .container .jumbotron {
+ padding-left: 60px;
+ padding-right: 60px;
+ }
+ .jumbotron h1 {
+ font-size: 63px;
+ }
+}
+.thumbnail {
+ padding: 4px;
+ line-height: 1.428571429;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-radius: 4px;
+ -webkit-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ display: inline-block;
+ max-width: 100%;
+ height: auto;
+ display: block;
+}
+.thumbnail > img {
+ display: block;
+ max-width: 100%;
+ height: auto;
+}
+a.thumbnail:hover,
+a.thumbnail:focus {
+ border-color: #428bca;
+}
+.thumbnail > img {
+ margin-left: auto;
+ margin-right: auto;
+}
+.thumbnail .caption {
+ padding: 9px;
+ color: #333333;
+}
+.alert {
+ padding: 15px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
+ border-radius: 4px;
+}
+.alert h4 {
+ margin-top: 0;
+ color: inherit;
+}
+.alert .alert-link {
+ font-weight: bold;
+}
+.alert > p,
+.alert > ul {
+ margin-bottom: 0;
+}
+.alert > p + p {
+ margin-top: 5px;
+}
+.alert-dismissable {
+ padding-right: 35px;
+}
+.alert-dismissable .close {
+ position: relative;
+ top: -2px;
+ right: -21px;
+ color: inherit;
+}
+.alert-success {
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+ color: #468847;
+}
+.alert-success hr {
+ border-top-color: #c9e2b3;
+}
+.alert-success .alert-link {
+ color: #356635;
+}
+.alert-info {
+ background-color: #d9edf7;
+ border-color: #bce8f1;
+ color: #3a87ad;
+}
+.alert-info hr {
+ border-top-color: #a6e1ec;
+}
+.alert-info .alert-link {
+ color: #2d6987;
+}
+.alert-warning {
+ background-color: #fcf8e3;
+ border-color: #fbeed5;
+ color: #c09853;
+}
+.alert-warning hr {
+ border-top-color: #f8e5be;
+}
+.alert-warning .alert-link {
+ color: #a47e3c;
+}
+.alert-danger {
+ background-color: #f2dede;
+ border-color: #eed3d7;
+ color: #b94a48;
+}
+.alert-danger hr {
+ border-top-color: #e6c1c7;
+}
+.alert-danger .alert-link {
+ color: #953b39;
+}
+ at -webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+ at -moz-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+ at -o-keyframes progress-bar-stripes {
+ from {
+ background-position: 0 0;
+ }
+ to {
+ background-position: 40px 0;
+ }
+}
+ at keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+.progress {
+ overflow: hidden;
+ height: 20px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+.progress-bar {
+ float: left;
+ width: 0%;
+ height: 100%;
+ font-size: 12px;
+ color: #ffffff;
+ text-align: center;
+ background-color: #428bca;
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -webkit-transition: width 0.6s ease;
+ transition: width 0.6s ease;
+}
+.progress-striped .progress-bar {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-size: 40px 40px;
+}
+.progress.active .progress-bar {
+ -webkit-animation: progress-bar-stripes 2s linear infinite;
+ -moz-animation: progress-bar-stripes 2s linear infinite;
+ -ms-animation: progress-bar-stripes 2s linear infinite;
+ -o-animation: progress-bar-stripes 2s linear infinite;
+ animation: progress-bar-stripes 2s linear infinite;
+}
+.progress-bar-success {
+ background-color: #5cb85c;
+}
+.progress-striped .progress-bar-success {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+.progress-bar-info {
+ background-color: #5bc0de;
+}
+.progress-striped .progress-bar-info {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+.progress-bar-warning {
+ background-color: #f0ad4e;
+}
+.progress-striped .progress-bar-warning {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+.progress-bar-danger {
+ background-color: #d9534f;
+}
+.progress-striped .progress-bar-danger {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+.media,
+.media-body {
+ overflow: hidden;
+ zoom: 1;
+}
+.media,
+.media .media {
+ margin-top: 15px;
+}
+.media:first-child {
+ margin-top: 0;
+}
+.media-object {
+ display: block;
+}
+.media-heading {
+ margin: 0 0 5px;
+}
+.media > .pull-left {
+ margin-right: 10px;
+}
+.media > .pull-right {
+ margin-left: 10px;
+}
+.media-list {
+ padding-left: 0;
+ list-style: none;
+}
+.list-group {
+ margin-bottom: 20px;
+ padding-left: 0;
+}
+.list-group-item {
+ position: relative;
+ display: block;
+ padding: 10px 15px;
+ margin-bottom: -1px;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+}
+.list-group-item:first-child {
+ border-top-right-radius: 4px;
+ border-top-left-radius: 4px;
+}
+.list-group-item:last-child {
+ margin-bottom: 0;
+ border-bottom-right-radius: 4px;
+ border-bottom-left-radius: 4px;
+}
+.list-group-item > .badge {
+ float: right;
+}
+.list-group-item > .badge + .badge {
+ margin-right: 5px;
+}
+a.list-group-item {
+ color: #555555;
+}
+a.list-group-item .list-group-item-heading {
+ color: #333333;
+}
+a.list-group-item:hover,
+a.list-group-item:focus {
+ text-decoration: none;
+ background-color: #f5f5f5;
+}
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
+ z-index: 2;
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #428bca;
+}
+.list-group-item.active .list-group-item-heading,
+.list-group-item.active:hover .list-group-item-heading,
+.list-group-item.active:focus .list-group-item-heading {
+ color: inherit;
+}
+.list-group-item.active .list-group-item-text,
+.list-group-item.active:hover .list-group-item-text,
+.list-group-item.active:focus .list-group-item-text {
+ color: #e1edf7;
+}
+.list-group-item-heading {
+ margin-top: 0;
+ margin-bottom: 5px;
+}
+.list-group-item-text {
+ margin-bottom: 0;
+ line-height: 1.3;
+}
+.panel {
+ margin-bottom: 20px;
+ background-color: #ffffff;
+ border: 1px solid transparent;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
+}
+.panel-body {
+ padding: 15px;
+}
+.panel-body:before,
+.panel-body:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.panel-body:after {
+ clear: both;
+}
+.panel-body:before,
+.panel-body:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.panel-body:after {
+ clear: both;
+}
+.panel > .list-group {
+ margin-bottom: 0;
+}
+.panel > .list-group .list-group-item {
+ border-width: 1px 0;
+}
+.panel > .list-group .list-group-item:first-child {
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+.panel > .list-group .list-group-item:last-child {
+ border-bottom: 0;
+}
+.panel-heading + .list-group .list-group-item:first-child {
+ border-top-width: 0;
+}
+.panel > .table {
+ margin-bottom: 0;
+}
+.panel > .panel-body + .table {
+ border-top: 1px solid #dddddd;
+}
+.panel-heading {
+ padding: 10px 15px;
+ border-bottom: 1px solid transparent;
+ border-top-right-radius: 3px;
+ border-top-left-radius: 3px;
+}
+.panel-title {
+ margin-top: 0;
+ margin-bottom: 0;
+ font-size: 16px;
+}
+.panel-title > a {
+ color: inherit;
+}
+.panel-footer {
+ padding: 10px 15px;
+ background-color: #f5f5f5;
+ border-top: 1px solid #dddddd;
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
+}
+.panel-group .panel {
+ margin-bottom: 0;
+ border-radius: 4px;
+ overflow: hidden;
+}
+.panel-group .panel + .panel {
+ margin-top: 5px;
+}
+.panel-group .panel-heading {
+ border-bottom: 0;
+}
+.panel-group .panel-heading + .panel-collapse .panel-body {
+ border-top: 1px solid #dddddd;
+}
+.panel-group .panel-footer {
+ border-top: 0;
+}
+.panel-group .panel-footer + .panel-collapse .panel-body {
+ border-bottom: 1px solid #dddddd;
+}
+.panel-default {
+ border-color: #dddddd;
+}
+.panel-default > .panel-heading {
+ color: #333333;
+ background-color: #f5f5f5;
+ border-color: #dddddd;
+}
+.panel-default > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #dddddd;
+}
+.panel-default > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #dddddd;
+}
+.panel-primary {
+ border-color: #428bca;
+}
+.panel-primary > .panel-heading {
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #428bca;
+}
+.panel-primary > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #428bca;
+}
+.panel-primary > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #428bca;
+}
+.panel-success {
+ border-color: #d6e9c6;
+}
+.panel-success > .panel-heading {
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+}
+.panel-success > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #d6e9c6;
+}
+.panel-success > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #d6e9c6;
+}
+.panel-warning {
+ border-color: #fbeed5;
+}
+.panel-warning > .panel-heading {
+ color: #c09853;
+ background-color: #fcf8e3;
+ border-color: #fbeed5;
+}
+.panel-warning > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #fbeed5;
+}
+.panel-warning > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #fbeed5;
+}
+.panel-danger {
+ border-color: #eed3d7;
+}
+.panel-danger > .panel-heading {
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #eed3d7;
+}
+.panel-danger > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #eed3d7;
+}
+.panel-danger > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #eed3d7;
+}
+.panel-info {
+ border-color: #bce8f1;
+}
+.panel-info > .panel-heading {
+ color: #3a87ad;
+ background-color: #d9edf7;
+ border-color: #bce8f1;
+}
+.panel-info > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #bce8f1;
+}
+.panel-info > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #bce8f1;
+}
+.well {
+ min-height: 20px;
+ padding: 19px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border: 1px solid #e3e3e3;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+}
+.well blockquote {
+ border-color: #ddd;
+ border-color: rgba(0, 0, 0, 0.15);
+}
+.well-lg {
+ padding: 24px;
+ border-radius: 6px;
+}
+.well-sm {
+ padding: 9px;
+ border-radius: 3px;
+}
+.close {
+ float: right;
+ font-size: 21px;
+ font-weight: bold;
+ line-height: 1;
+ color: #000000;
+ text-shadow: 0 1px 0 #ffffff;
+ opacity: 0.2;
+ filter: alpha(opacity=20);
+}
+.close:hover,
+.close:focus {
+ color: #000000;
+ text-decoration: none;
+ cursor: pointer;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+}
+button.close {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+}
+.caret {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ margin-left: 2px;
+ vertical-align: middle;
+ border-top: 4px solid #000000;
+ border-right: 4px solid transparent;
+ border-left: 4px solid transparent;
+ border-bottom: 0 dotted;
+ content: "";
+}
+.dropdown {
+ position: relative;
+}
+.dropdown-toggle:focus {
+ outline: 0;
+}
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ list-style: none;
+ font-size: 14px;
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 4px;
+ -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ background-clip: padding-box;
+}
+.dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
+}
+.dropdown-menu .divider {
+ height: 1px;
+ margin: 9px 0;
+ overflow: hidden;
+ background-color: #e5e5e5;
+}
+.dropdown-menu > li > a {
+ display: block;
+ padding: 3px 20px;
+ clear: both;
+ font-weight: normal;
+ line-height: 1.428571429;
+ color: #333333;
+ white-space: nowrap;
+}
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+ text-decoration: none;
+ color: #ffffff;
+ background-color: #428bca;
+}
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+ color: #ffffff;
+ text-decoration: none;
+ outline: 0;
+ background-color: #428bca;
+}
+.dropdown-menu > .disabled > a,
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+ color: #999999;
+}
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+ text-decoration: none;
+ background-color: transparent;
+ background-image: none;
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+ cursor: not-allowed;
+}
+.open > .dropdown-menu {
+ display: block;
+}
+.open > a {
+ outline: 0;
+}
+.dropdown-header {
+ display: block;
+ padding: 3px 20px;
+ font-size: 12px;
+ line-height: 1.428571429;
+ color: #999999;
+}
+.dropdown-backdrop {
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ top: 0;
+ z-index: 990;
+}
+.pull-right > .dropdown-menu {
+ right: 0;
+ left: auto;
+}
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+ border-top: 0 dotted;
+ border-bottom: 4px solid #000000;
+ content: "";
+}
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+ top: auto;
+ bottom: 100%;
+ margin-bottom: 1px;
+}
+ at media (min-width: 980px) {
+ .navbar-right .dropdown-menu {
+ right: 0;
+ left: auto;
+ }
+}
+.tooltip {
+ position: absolute;
+ z-index: 1030;
+ display: block;
+ visibility: visible;
+ font-size: 12px;
+ line-height: 1.4;
+ opacity: 0;
+ filter: alpha(opacity=0);
+}
+.tooltip.in {
+ opacity: 0.9;
+ filter: alpha(opacity=90);
+}
+.tooltip.top {
+ margin-top: -3px;
+ padding: 5px 0;
+}
+.tooltip.right {
+ margin-left: 3px;
+ padding: 0 5px;
+}
+.tooltip.bottom {
+ margin-top: 3px;
+ padding: 5px 0;
+}
+.tooltip.left {
+ margin-left: -3px;
+ padding: 0 5px;
+}
+.tooltip-inner {
+ max-width: 200px;
+ padding: 3px 8px;
+ color: #ffffff;
+ text-align: center;
+ text-decoration: none;
+ background-color: #000000;
+ border-radius: 4px;
+}
+.tooltip-arrow {
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+.tooltip.top .tooltip-arrow {
+ bottom: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000000;
+}
+.tooltip.top-left .tooltip-arrow {
+ bottom: 0;
+ left: 5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000000;
+}
+.tooltip.top-right .tooltip-arrow {
+ bottom: 0;
+ right: 5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000000;
+}
+.tooltip.right .tooltip-arrow {
+ top: 50%;
+ left: 0;
+ margin-top: -5px;
+ border-width: 5px 5px 5px 0;
+ border-right-color: #000000;
+}
+.tooltip.left .tooltip-arrow {
+ top: 50%;
+ right: 0;
+ margin-top: -5px;
+ border-width: 5px 0 5px 5px;
+ border-left-color: #000000;
+}
+.tooltip.bottom .tooltip-arrow {
+ top: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000000;
+}
+.tooltip.bottom-left .tooltip-arrow {
+ top: 0;
+ left: 5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000000;
+}
+.tooltip.bottom-right .tooltip-arrow {
+ top: 0;
+ right: 5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000000;
+}
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1010;
+ display: none;
+ max-width: 276px;
+ padding: 1px;
+ text-align: left;
+ background-color: #ffffff;
+ background-clip: padding-box;
+ border: 1px solid #cccccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ white-space: normal;
+}
+.popover.top {
+ margin-top: -10px;
+}
+.popover.right {
+ margin-left: 10px;
+}
+.popover.bottom {
+ margin-top: 10px;
+}
+.popover.left {
+ margin-left: -10px;
+}
+.popover-title {
+ margin: 0;
+ padding: 8px 14px;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 18px;
+ background-color: #f7f7f7;
+ border-bottom: 1px solid #ebebeb;
+ border-radius: 5px 5px 0 0;
+}
+.popover-content {
+ padding: 9px 14px;
+}
+.popover .arrow,
+.popover .arrow:after {
+ position: absolute;
+ display: block;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+.popover .arrow {
+ border-width: 11px;
+}
+.popover .arrow:after {
+ border-width: 10px;
+ content: "";
+}
+.popover.top .arrow {
+ left: 50%;
+ margin-left: -11px;
+ border-bottom-width: 0;
+ border-top-color: #999999;
+ border-top-color: rgba(0, 0, 0, 0.25);
+ bottom: -11px;
+}
+.popover.top .arrow:after {
+ content: " ";
+ bottom: 1px;
+ margin-left: -10px;
+ border-bottom-width: 0;
+ border-top-color: #ffffff;
+}
+.popover.right .arrow {
+ top: 50%;
+ left: -11px;
+ margin-top: -11px;
+ border-left-width: 0;
+ border-right-color: #999999;
+ border-right-color: rgba(0, 0, 0, 0.25);
+}
+.popover.right .arrow:after {
+ content: " ";
+ left: 1px;
+ bottom: -10px;
+ border-left-width: 0;
+ border-right-color: #ffffff;
+}
+.popover.bottom .arrow {
+ left: 50%;
+ margin-left: -11px;
+ border-top-width: 0;
+ border-bottom-color: #999999;
+ border-bottom-color: rgba(0, 0, 0, 0.25);
+ top: -11px;
+}
+.popover.bottom .arrow:after {
+ content: " ";
+ top: 1px;
+ margin-left: -10px;
+ border-top-width: 0;
+ border-bottom-color: #ffffff;
+}
+.popover.left .arrow {
+ top: 50%;
+ right: -11px;
+ margin-top: -11px;
+ border-right-width: 0;
+ border-left-color: #999999;
+ border-left-color: rgba(0, 0, 0, 0.25);
+}
+.popover.left .arrow:after {
+ content: " ";
+ right: 1px;
+ border-right-width: 0;
+ border-left-color: #ffffff;
+ bottom: -10px;
+}
+.modal-open {
+ overflow: hidden;
+}
+body.modal-open,
+.modal-open .navbar-fixed-top,
+.modal-open .navbar-fixed-bottom {
+ margin-right: 15px;
+}
+.modal {
+ display: none;
+ overflow: auto;
+ overflow-y: scroll;
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1040;
+}
+.modal.fade .modal-dialog {
+ -webkit-transform: translate(0, -25%);
+ -ms-transform: translate(0, -25%);
+ transform: translate(0, -25%);
+ -webkit-transition: -webkit-transform 0.3s ease-out;
+ -moz-transition: -moz-transform 0.3s ease-out;
+ -o-transition: -o-transform 0.3s ease-out;
+ transition: transform 0.3s ease-out;
+}
+.modal.in .modal-dialog {
+ -webkit-transform: translate(0, 0);
+ -ms-transform: translate(0, 0);
+ transform: translate(0, 0);
+}
+.modal-dialog {
+ margin-left: auto;
+ margin-right: auto;
+ width: auto;
+ padding: 10px;
+ z-index: 1050;
+}
+.modal-content {
+ position: relative;
+ background-color: #ffffff;
+ border: 1px solid #999999;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 6px;
+ -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
+ box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
+ background-clip: padding-box;
+ outline: none;
+}
+.modal-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1030;
+ background-color: #000000;
+}
+.modal-backdrop.fade {
+ opacity: 0;
+ filter: alpha(opacity=0);
+}
+.modal-backdrop.in {
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+}
+.modal-header {
+ padding: 15px;
+ border-bottom: 1px solid #e5e5e5;
+ min-height: 16.428571429px;
+}
+.modal-header .close {
+ margin-top: -2px;
+}
+.modal-title {
+ margin: 0;
+ line-height: 1.428571429;
+}
+.modal-body {
+ position: relative;
+ padding: 20px;
+}
+.modal-footer {
+ margin-top: 15px;
+ padding: 19px 20px 20px;
+ text-align: right;
+ border-top: 1px solid #e5e5e5;
+}
+.modal-footer:before,
+.modal-footer:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.modal-footer:after {
+ clear: both;
+}
+.modal-footer:before,
+.modal-footer:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.modal-footer:after {
+ clear: both;
+}
+.modal-footer .btn + .btn {
+ margin-left: 5px;
+ margin-bottom: 0;
+}
+.modal-footer .btn-group .btn + .btn {
+ margin-left: -1px;
+}
+.modal-footer .btn-block + .btn-block {
+ margin-left: 0;
+}
+ at media screen and (min-width: 980px) {
+ .modal-dialog {
+ left: 50%;
+ right: auto;
+ width: 600px;
+ padding-top: 30px;
+ padding-bottom: 30px;
+ }
+ .modal-content {
+ -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
+ }
+}
+.carousel {
+ position: relative;
+}
+.carousel-inner {
+ position: relative;
+ overflow: hidden;
+ width: 100%;
+}
+.carousel-inner > .item {
+ display: none;
+ position: relative;
+ -webkit-transition: 0.6s ease-in-out left;
+ transition: 0.6s ease-in-out left;
+}
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+ display: block;
+ max-width: 100%;
+ height: auto;
+ line-height: 1;
+}
+.carousel-inner > .active,
+.carousel-inner > .next,
+.carousel-inner > .prev {
+ display: block;
+}
+.carousel-inner > .active {
+ left: 0;
+}
+.carousel-inner > .next,
+.carousel-inner > .prev {
+ position: absolute;
+ top: 0;
+ width: 100%;
+}
+.carousel-inner > .next {
+ left: 100%;
+}
+.carousel-inner > .prev {
+ left: -100%;
+}
+.carousel-inner > .next.left,
+.carousel-inner > .prev.right {
+ left: 0;
+}
+.carousel-inner > .active.left {
+ left: -100%;
+}
+.carousel-inner > .active.right {
+ left: 100%;
+}
+.carousel-control {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 15%;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+ font-size: 20px;
+ color: #ffffff;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
+}
+.carousel-control.left {
+ background-image: -webkit-gradient(linear, 0% top, 100% top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001)));
+ background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0%), color-stop(rgba(0, 0, 0, 0.0001) 100%));
+ background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);
+ background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);
+}
+.carousel-control.right {
+ left: auto;
+ right: 0;
+ background-image: -webkit-gradient(linear, 0% top, 100% top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5)));
+ background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0%), color-stop(rgba(0, 0, 0, 0.5) 100%));
+ background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);
+ background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);
+}
+.carousel-control:hover,
+.carousel-control:focus {
+ color: #ffffff;
+ text-decoration: none;
+ opacity: 0.9;
+ filter: alpha(opacity=90);
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-left,
+.carousel-control .glyphicon-chevron-right {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ z-index: 5;
+ display: inline-block;
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next {
+ width: 20px;
+ height: 20px;
+ margin-top: -10px;
+ margin-left: -10px;
+ font-family: serif;
+}
+.carousel-control .icon-prev:before {
+ content: '\2039';
+}
+.carousel-control .icon-next:before {
+ content: '\203a';
+}
+.carousel-indicators {
+ position: absolute;
+ bottom: 10px;
+ left: 50%;
+ z-index: 15;
+ width: 60%;
+ margin-left: -30%;
+ padding-left: 0;
+ list-style: none;
+ text-align: center;
+}
+.carousel-indicators li {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ margin: 1px;
+ text-indent: -999px;
+ border: 1px solid #ffffff;
+ border-radius: 10px;
+ cursor: pointer;
+}
+.carousel-indicators .active {
+ margin: 0;
+ width: 12px;
+ height: 12px;
+ background-color: #ffffff;
+}
+.carousel-caption {
+ position: absolute;
+ left: 15%;
+ right: 15%;
+ bottom: 20px;
+ z-index: 10;
+ padding-top: 20px;
+ padding-bottom: 20px;
+ color: #ffffff;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
+}
+.carousel-caption .btn {
+ text-shadow: none;
+}
+ at media screen and (min-width: 980px) {
+ .carousel-control .icon-prev,
+ .carousel-control .icon-next {
+ width: 30px;
+ height: 30px;
+ margin-top: -15px;
+ margin-left: -15px;
+ font-size: 30px;
+ }
+ .carousel-caption {
+ left: 20%;
+ right: 20%;
+ padding-bottom: 30px;
+ }
+ .carousel-indicators {
+ bottom: 20px;
+ }
+}
+.clearfix:before,
+.clearfix:after {
+ content: " ";
+ /* 1 */
+
+ display: table;
+ /* 2 */
+
+}
+.clearfix:after {
+ clear: both;
+}
+.pull-right {
+ float: right !important;
+}
+.pull-left {
+ float: left !important;
+}
+.hide {
+ display: none !important;
+}
+.show {
+ display: block !important;
+}
+.invisible {
+ visibility: hidden;
+}
+.text-hide {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+.affix {
+ position: fixed;
+}
+ at -ms-viewport {
+ width: device-width;
+}
+ at media screen and (max-width: 400px) {
+ @-ms-viewport {
+ width: 320px;
+ }
+}
+.hidden {
+ display: none !important;
+ visibility: hidden !important;
+}
+.visible-xs {
+ display: none !important;
+}
+tr.visible-xs {
+ display: none !important;
+}
+th.visible-xs,
+td.visible-xs {
+ display: none !important;
+}
+ at media (max-width: 979px) {
+ .visible-xs {
+ display: block !important;
+ }
+ tr.visible-xs {
+ display: table-row !important;
+ }
+ th.visible-xs,
+ td.visible-xs {
+ display: table-cell !important;
+ }
+}
+ at media (min-width: 980px) and (max-width: 991px) {
+ .visible-xs.visible-sm {
+ display: block !important;
+ }
+ tr.visible-xs.visible-sm {
+ display: table-row !important;
+ }
+ th.visible-xs.visible-sm,
+ td.visible-xs.visible-sm {
+ display: table-cell !important;
+ }
+}
+ at media (min-width: 992px) and (max-width: 1199px) {
+ .visible-xs.visible-md {
+ display: block !important;
+ }
+ tr.visible-xs.visible-md {
+ display: table-row !important;
+ }
+ th.visible-xs.visible-md,
+ td.visible-xs.visible-md {
+ display: table-cell !important;
+ }
+}
+ at media (min-width: 1200px) {
+ .visible-xs.visible-lg {
+ display: block !important;
+ }
+ tr.visible-xs.visible-lg {
+ display: table-row !important;
+ }
+ th.visible-xs.visible-lg,
+ td.visible-xs.visible-lg {
+ display: table-cell !important;
+ }
+}
+.visible-sm {
+ display: none !important;
+}
+tr.visible-sm {
+ display: none !important;
+}
+th.visible-sm,
+td.visible-sm {
+ display: none !important;
+}
+ at media (max-width: 979px) {
+ .visible-sm.visible-xs {
+ display: block !important;
+ }
+ tr.visible-sm.visible-xs {
+ display: table-row !important;
+ }
+ th.visible-sm.visible-xs,
+ td.visible-sm.visible-xs {
+ display: table-cell !important;
+ }
+}
+ at media (min-width: 980px) and (max-width: 991px) {
+ .visible-sm {
+ display: block !important;
+ }
+ tr.visible-sm {
+ display: table-row !important;
+ }
+ th.visible-sm,
+ td.visible-sm {
+ display: table-cell !important;
+ }
+}
+ at media (min-width: 992px) and (max-width: 1199px) {
+ .visible-sm.visible-md {
+ display: block !important;
+ }
+ tr.visible-sm.visible-md {
+ display: table-row !important;
+ }
+ th.visible-sm.visible-md,
+ td.visible-sm.visible-md {
+ display: table-cell !important;
+ }
+}
+ at media (min-width: 1200px) {
+ .visible-sm.visible-lg {
+ display: block !important;
+ }
+ tr.visible-sm.visible-lg {
+ display: table-row !important;
+ }
+ th.visible-sm.visible-lg,
+ td.visible-sm.visible-lg {
+ display: table-cell !important;
+ }
+}
+.visible-md {
+ display: none !important;
+}
+tr.visible-md {
+ display: none !important;
+}
+th.visible-md,
+td.visible-md {
+ display: none !important;
+}
+ at media (max-width: 979px) {
+ .visible-md.visible-xs {
+ display: block !important;
+ }
+ tr.visible-md.visible-xs {
+ display: table-row !important;
+ }
+ th.visible-md.visible-xs,
+ td.visible-md.visible-xs {
+ display: table-cell !important;
+ }
+}
+ at media (min-width: 980px) and (max-width: 991px) {
+ .visible-md.visible-sm {
+ display: block !important;
+ }
+ tr.visible-md.visible-sm {
+ display: table-row !important;
+ }
+ th.visible-md.visible-sm,
+ td.visible-md.visible-sm {
+ display: table-cell !important;
+ }
+}
+ at media (min-width: 992px) and (max-width: 1199px) {
+ .visible-md {
+ display: block !important;
+ }
+ tr.visible-md {
+ display: table-row !important;
+ }
+ th.visible-md,
+ td.visible-md {
+ display: table-cell !important;
+ }
+}
+ at media (min-width: 1200px) {
+ .visible-md.visible-lg {
+ display: block !important;
+ }
+ tr.visible-md.visible-lg {
+ display: table-row !important;
+ }
+ th.visible-md.visible-lg,
+ td.visible-md.visible-lg {
+ display: table-cell !important;
+ }
+}
+.visible-lg {
+ display: none !important;
+}
+tr.visible-lg {
+ display: none !important;
+}
+th.visible-lg,
+td.visible-lg {
+ display: none !important;
+}
+ at media (max-width: 979px) {
+ .visible-lg.visible-xs {
+ display: block !important;
+ }
+ tr.visible-lg.visible-xs {
+ display: table-row !important;
+ }
+ th.visible-lg.visible-xs,
+ td.visible-lg.visible-xs {
+ display: table-cell !important;
+ }
+}
+ at media (min-width: 980px) and (max-width: 991px) {
+ .visible-lg.visible-sm {
+ display: block !important;
+ }
+ tr.visible-lg.visible-sm {
+ display: table-row !important;
+ }
+ th.visible-lg.visible-sm,
+ td.visible-lg.visible-sm {
+ display: table-cell !important;
+ }
+}
+ at media (min-width: 992px) and (max-width: 1199px) {
+ .visible-lg.visible-md {
+ display: block !important;
+ }
+ tr.visible-lg.visible-md {
+ display: table-row !important;
+ }
+ th.visible-lg.visible-md,
+ td.visible-lg.visible-md {
+ display: table-cell !important;
+ }
+}
+ at media (min-width: 1200px) {
+ .visible-lg {
+ display: block !important;
+ }
+ tr.visible-lg {
+ display: table-row !important;
+ }
+ th.visible-lg,
+ td.visible-lg {
+ display: table-cell !important;
+ }
+}
+.hidden-xs {
+ display: block !important;
+}
+tr.hidden-xs {
+ display: table-row !important;
+}
+th.hidden-xs,
+td.hidden-xs {
+ display: table-cell !important;
+}
+ at media (max-width: 979px) {
+ .hidden-xs {
+ display: none !important;
+ }
+ tr.hidden-xs {
+ display: none !important;
+ }
+ th.hidden-xs,
+ td.hidden-xs {
+ display: none !important;
+ }
+}
+ at media (min-width: 980px) and (max-width: 991px) {
+ .hidden-xs.hidden-sm {
+ display: none !important;
+ }
+ tr.hidden-xs.hidden-sm {
+ display: none !important;
+ }
+ th.hidden-xs.hidden-sm,
+ td.hidden-xs.hidden-sm {
+ display: none !important;
+ }
+}
+ at media (min-width: 992px) and (max-width: 1199px) {
+ .hidden-xs.hidden-md {
+ display: none !important;
+ }
+ tr.hidden-xs.hidden-md {
+ display: none !important;
+ }
+ th.hidden-xs.hidden-md,
+ td.hidden-xs.hidden-md {
+ display: none !important;
+ }
+}
+ at media (min-width: 1200px) {
+ .hidden-xs.hidden-lg {
+ display: none !important;
+ }
+ tr.hidden-xs.hidden-lg {
+ display: none !important;
+ }
+ th.hidden-xs.hidden-lg,
+ td.hidden-xs.hidden-lg {
+ display: none !important;
+ }
+}
+.hidden-sm {
+ display: block !important;
+}
+tr.hidden-sm {
+ display: table-row !important;
+}
+th.hidden-sm,
+td.hidden-sm {
+ display: table-cell !important;
+}
+ at media (max-width: 979px) {
+ .hidden-sm.hidden-xs {
+ display: none !important;
+ }
+ tr.hidden-sm.hidden-xs {
+ display: none !important;
+ }
+ th.hidden-sm.hidden-xs,
+ td.hidden-sm.hidden-xs {
+ display: none !important;
+ }
+}
+ at media (min-width: 980px) and (max-width: 991px) {
+ .hidden-sm {
+ display: none !important;
+ }
+ tr.hidden-sm {
+ display: none !important;
+ }
+ th.hidden-sm,
+ td.hidden-sm {
+ display: none !important;
+ }
+}
+ at media (min-width: 992px) and (max-width: 1199px) {
+ .hidden-sm.hidden-md {
+ display: none !important;
+ }
+ tr.hidden-sm.hidden-md {
+ display: none !important;
+ }
+ th.hidden-sm.hidden-md,
+ td.hidden-sm.hidden-md {
+ display: none !important;
+ }
+}
+ at media (min-width: 1200px) {
+ .hidden-sm.hidden-lg {
+ display: none !important;
+ }
+ tr.hidden-sm.hidden-lg {
+ display: none !important;
+ }
+ th.hidden-sm.hidden-lg,
+ td.hidden-sm.hidden-lg {
+ display: none !important;
+ }
+}
+.hidden-md {
+ display: block !important;
+}
+tr.hidden-md {
+ display: table-row !important;
+}
+th.hidden-md,
+td.hidden-md {
+ display: table-cell !important;
+}
+ at media (max-width: 979px) {
+ .hidden-md.hidden-xs {
+ display: none !important;
+ }
+ tr.hidden-md.hidden-xs {
+ display: none !important;
+ }
+ th.hidden-md.hidden-xs,
+ td.hidden-md.hidden-xs {
+ display: none !important;
+ }
+}
+ at media (min-width: 980px) and (max-width: 991px) {
+ .hidden-md.hidden-sm {
+ display: none !important;
+ }
+ tr.hidden-md.hidden-sm {
+ display: none !important;
+ }
+ th.hidden-md.hidden-sm,
+ td.hidden-md.hidden-sm {
+ display: none !important;
+ }
+}
+ at media (min-width: 992px) and (max-width: 1199px) {
+ .hidden-md {
+ display: none !important;
+ }
+ tr.hidden-md {
+ display: none !important;
+ }
+ th.hidden-md,
+ td.hidden-md {
+ display: none !important;
+ }
+}
+ at media (min-width: 1200px) {
+ .hidden-md.hidden-lg {
+ display: none !important;
+ }
+ tr.hidden-md.hidden-lg {
+ display: none !important;
+ }
+ th.hidden-md.hidden-lg,
+ td.hidden-md.hidden-lg {
+ display: none !important;
+ }
+}
+.hidden-lg {
+ display: block !important;
+}
+tr.hidden-lg {
+ display: table-row !important;
+}
+th.hidden-lg,
+td.hidden-lg {
+ display: table-cell !important;
+}
+ at media (max-width: 979px) {
+ .hidden-lg.hidden-xs {
+ display: none !important;
+ }
+ tr.hidden-lg.hidden-xs {
+ display: none !important;
+ }
+ th.hidden-lg.hidden-xs,
+ td.hidden-lg.hidden-xs {
+ display: none !important;
+ }
+}
+ at media (min-width: 980px) and (max-width: 991px) {
+ .hidden-lg.hidden-sm {
+ display: none !important;
+ }
+ tr.hidden-lg.hidden-sm {
+ display: none !important;
+ }
+ th.hidden-lg.hidden-sm,
+ td.hidden-lg.hidden-sm {
+ display: none !important;
+ }
+}
+ at media (min-width: 992px) and (max-width: 1199px) {
+ .hidden-lg.hidden-md {
+ display: none !important;
+ }
+ tr.hidden-lg.hidden-md {
+ display: none !important;
+ }
+ th.hidden-lg.hidden-md,
+ td.hidden-lg.hidden-md {
+ display: none !important;
+ }
+}
+ at media (min-width: 1200px) {
+ .hidden-lg {
+ display: none !important;
+ }
+ tr.hidden-lg {
+ display: none !important;
+ }
+ th.hidden-lg,
+ td.hidden-lg {
+ display: none !important;
+ }
+}
+.visible-print {
+ display: none !important;
+}
+tr.visible-print {
+ display: none !important;
+}
+th.visible-print,
+td.visible-print {
+ display: none !important;
+}
+ at media print {
+ .visible-print {
+ display: block !important;
+ }
+ tr.visible-print {
+ display: table-row !important;
+ }
+ th.visible-print,
+ td.visible-print {
+ display: table-cell !important;
+ }
+ .hidden-print {
+ display: none !important;
+ }
+ tr.hidden-print {
+ display: none !important;
+ }
+ th.hidden-print,
+ td.hidden-print {
+ display: none !important;
+ }
+}
+.fade {
+ opacity: 0;
+ -webkit-transition: opacity 0.15s linear;
+ transition: opacity 0.15s linear;
+}
+.fade.in {
+ opacity: 1;
+}
+.collapse {
+ display: none;
+}
+.collapse.in {
+ display: block;
+}
+.collapsing {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ -webkit-transition: height 0.35s ease;
+ transition: height 0.35s ease;
+}
diff --git a/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/css/bootstrap.min.css b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/css/bootstrap.min.css
new file mode 100755
index 0000000..1dd7574
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/css/bootstrap.min.css
@@ -0,0 +1,6435 @@
+/*!
+ * Bootstrap v3.0.0
+ *
+ * Copyright 2013 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+
+article,
+aside, details, figcaption, figure, footer, header, hgroup, main, nav,
+section, summary {
+ display: block;
+}
+
+audio, canvas, video {
+ display: inline-block;
+}
+
+audio:not([controls]) {
+ display: none;
+ height: 0;
+}
+
+[hidden] {
+ display: none;
+}
+
+html {
+ font-family: sans-serif;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+
+body {
+ margin: 0;
+}
+
+a:focus {
+ outline: thin dotted;
+}
+
+a:active, a:hover {
+ outline: 0;
+}
+
+h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
+}
+
+abbr[title] {
+ border-bottom: 1px dotted;
+}
+
+b, strong {
+ font-weight: bold;
+}
+
+dfn {
+ font-style: italic;
+}
+
+hr {
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ height: 0;
+}
+
+mark {
+ background: #ff0;
+ color: #000;
+}
+
+code, kbd, pre, samp {
+ font-family: monospace, serif;
+ font-size: 1em;
+}
+
+pre {
+ white-space: pre-wrap;
+}
+
+q {
+ quotes: "\201C" "\201D" "\2018" "\2019";
+}
+
+small {
+ font-size: 80%;
+}
+
+sub, sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+img {
+ border: 0;
+}
+
+svg:not(:root) {
+ overflow: hidden;
+}
+
+figure {
+ margin: 0;
+}
+
+fieldset {
+ border: 1px solid #c0c0c0;
+ margin: 0 2px;
+ padding: 0.35em 0.625em 0.75em;
+}
+
+legend {
+ border: 0;
+ padding: 0;
+}
+
+button, input, select, textarea {
+ font-family: inherit;
+ font-size: 100%;
+ margin: 0;
+}
+
+button, input {
+ line-height: normal;
+}
+
+button, select {
+ text-transform: none;
+}
+
+button, html input[type="button"], input[type="reset"], input[type="submit"] {
+ -webkit-appearance: button;
+ cursor: pointer;
+}
+
+button[disabled], html input[disabled] {
+ cursor: default;
+}
+
+input[type="checkbox"], input[type="radio"] {
+ box-sizing: border-box;
+ padding: 0;
+}
+
+input[type="search"] {
+ -webkit-appearance: textfield;
+ -moz-box-sizing: content-box;
+ -webkit-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+button::-moz-focus-inner, input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+
+textarea {
+ overflow: auto;
+ vertical-align: top;
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+*, *:before, *:after {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+html {
+ font-size: 62.5%;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+body {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 18px;
+ line-height: 1.428571429;
+ color: #333333;
+ background-color: #ffffff;
+}
+
+input, button, select, textarea {
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+
+button, input, select[multiple], textarea {
+ background-image: none;
+}
+
+a {
+ color: #428bca;
+ text-decoration: none;
+ font-size: 18px;
+}
+
+a:hover, a:focus {
+ color: #2a6496;
+ text-decoration: underline;
+}
+
+a:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+img {
+ vertical-align: middle;
+ margin-top: 20px;
+ margin-bottom: 20px;
+}
+
+.img-responsive {
+ display: block;
+ max-width: 100%;
+ height: auto;
+}
+
+.img-rounded {
+ border-radius: 6px;
+}
+
+.img-thumbnail {
+ padding: 4px;
+ line-height: 1.428571429;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-radius: 4px;
+ -webkit-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ display: inline-block;
+ max-width: 100%;
+ height: auto;
+}
+
+.img-circle {
+ border-radius: 50%;
+}
+
+hr {
+ margin-top: 20px;
+ margin-bottom: 20px;
+ border: 0;
+ border-top: 1px solid #eeeeee;
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0 0 0 0);
+ border: 0;
+}
+
+ at media print {
+ * {
+ text-shadow: none !important;
+ color: #000 !important;
+ background: transparent !important;
+ box-shadow: none !important;
+ }
+
+ a, a:visited {
+ text-decoration: underline;
+ }
+
+ a[href]:after {
+ content: " (" attr(href) ")";
+ }
+
+ abbr[title]:after {
+ content: " (" attr(title) ")";
+ }
+
+ .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after {
+ content: "";
+ }
+
+ pre, blockquote {
+ border: 1px solid #999;
+ page-break-inside: avoid;
+ }
+
+ thead {
+ display: table-header-group;
+ }
+
+ tr, img {
+ page-break-inside: avoid;
+ }
+
+ img {
+ max-width: 100% !important;
+ }
+
+ @page {
+ margin: 2cm .5cm;
+ }
+
+ p, h2, h3 {
+ orphans: 3;
+ widows: 3;
+ }
+
+ h2, h3 {
+ page-break-after: avoid;
+ }
+
+ .navbar {
+ display: none;
+ }
+
+ .table td, .table th {
+ background-color: #fff !important;
+ }
+
+ .btn>.caret, .dropup>.btn>.caret {
+ border-top-color: #000 !important;
+ }
+
+ .label {
+ border: 1px solid #000;
+ }
+
+ .table {
+ border-collapse: collapse !important;
+ }
+
+ .table-bordered th, .table-bordered td {
+ border: 1px solid #ddd !important;
+ }
+}
+
+p {
+ font-size: 18px;
+ margin: 0 0 20px;
+}
+
+.lead {
+ margin-bottom: 20px;
+ font-size: 16.099999999999998px;
+ font-weight: 200;
+ line-height: 1.4;
+}
+
+ at media (min-width:768px){.lead {
+ font-size: 21px;
+ }
+}
+
+small {
+ font-size: 85%;
+}
+
+cite {
+ font-style: normal;
+}
+
+.text-muted {
+ color: #999999;
+}
+
+.text-primary {
+ color: #428bca;
+}
+
+.text-warning {
+ color: #c09853;
+}
+
+.text-danger {
+ color: #b94a48;
+}
+
+.text-success {
+ color: #468847;
+}
+
+.text-info {
+ color: #3a87ad;
+}
+
+.text-left {
+ text-align: left;
+}
+
+.text-right {
+ text-align: right;
+}
+
+.text-center {
+ text-align: center;
+}
+
+h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 500;
+ line-height: 1.1;
+}
+
+h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small,
+.h3 small, .h4 small, .h5 small, .h6 small {
+ font-weight: normal;
+ line-height: 1;
+ color: #999999;
+}
+
+h1, h2, h3 {
+ margin-top: 30px;
+ margin-bottom: 30px;
+ border-bottom: 5px solid #dedede;
+ padding-bottom: 10px
+}
+
+h4, h5, h6 {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+h1, .h1 {
+ font-size: 36px;
+}
+
+h2, .h2 {
+ font-size: 30px;
+}
+
+h3, .h3 {
+ font-size: 24px;
+}
+
+h4, .h4 {
+ font-size: 18px;
+}
+
+h5, .h5 {
+ font-size: 14px;
+}
+
+h6, .h6 {
+ font-size: 12px;
+}
+
+h1 small, .h1 small {
+ font-size: 24px;
+}
+
+h2 small, .h2 small {
+ font-size: 18px;
+}
+
+h3 small, .h3 small, h4 small, .h4 small {
+ font-size: 14px;
+}
+
+.page-header {
+ padding-bottom: 9px;
+ margin: 40px 0 20px;
+ border-bottom: 1px solid #eeeeee;
+}
+
+ul, ol {
+ margin-top: 0;
+ margin-bottom: 10px;
+}
+
+ul ul, ol ul, ul ol, ol ol {
+ margin-bottom: 0;
+}
+
+.list-unstyled {
+ padding-left: 0;
+ list-style: none;
+}
+
+.list-inline {
+ padding-left: 0;
+ list-style: none;
+}
+
+.list-inline>li {
+ display: inline-block;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+dl {
+ margin-bottom: 20px;
+}
+
+dt, dd {
+ line-height: 1.428571429;
+}
+
+dt {
+ font-weight: bold;
+}
+
+dd {
+ margin-left: 0;
+}
+
+ at media (min-width:980px){.dl-horizontal dt {
+ float: left;
+ width: 160px;
+ clear: left;
+ text-align: right;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .dl-horizontal dd {
+ margin-left: 180px;
+ }
+
+ .dl-horizontal dd:before, .dl-horizontal dd:after {
+ content: " ";
+ display: table;
+ }
+
+ .dl-horizontal dd:after {
+ clear: both;
+ }
+
+ .dl-horizontal dd:before, .dl-horizontal dd:after {
+ content: " ";
+ display: table;
+ }
+
+ .dl-horizontal dd:after {
+ clear: both;
+ }
+}
+
+abbr[title], abbr[data-original-title] {
+ cursor: help;
+ border-bottom: 1px dotted #999999;
+}
+
+abbr.initialism {
+ font-size: 90%;
+ text-transform: uppercase;
+}
+
+blockquote {
+ padding: 10px 20px;
+ margin: 0 0 20px;
+ border-left: 5px solid #eeeeee;
+}
+
+blockquote p {
+ font-size: 17.5px;
+ font-weight: 300;
+ line-height: 1.25;
+}
+
+blockquote p:last-child {
+ margin-bottom: 0;
+}
+
+blockquote small {
+ display: block;
+ line-height: 1.428571429;
+ color: #999999;
+}
+
+blockquote small:before {
+ content: '\2014 \00A0';
+}
+
+blockquote.pull-right {
+ padding-right: 15px;
+ padding-left: 0;
+ border-right: 5px solid #eeeeee;
+ border-left: 0;
+}
+
+blockquote.pull-right p, blockquote.pull-right small {
+ text-align: right;
+}
+
+blockquote.pull-right small:before {
+ content: '';
+}
+
+blockquote.pull-right small:after {
+ content: '\00A0 \2014';
+}
+
+q:before, q:after, blockquote:before, blockquote:after {
+ content: "";
+}
+
+address {
+ display: block;
+ margin-bottom: 20px;
+ font-style: normal;
+ line-height: 1.428571429;
+}
+
+code, pre {
+ font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
+}
+
+code {
+ padding: 2px 4px;
+ font-size: 90%;
+ background-color: #f5f5f5;
+ ;
+ white-space: nowrap;
+ border-radius: 4px;
+}
+
+pre {
+ width: 825px;
+ display: block;
+ padding: 9.5px;
+ margin: 0 0 20px;
+ font-size: 13px;
+ line-height: 1.428571429;
+ word-break: break-all;
+ word-wrap: break-word;
+ color: #333333;
+ background-color: #f5f5f5;
+ border: 1px solid #cccccc;
+ /* border-radius: 4px; */
+}
+
+pre.prettyprint {
+ margin-bottom: 20px;
+}
+
+pre code {
+ padding: 0;
+ font-size: inherit;
+ color: inherit;
+ white-space: pre-wrap;
+ background-color: transparent;
+ border: 0;
+}
+
+.pre-scrollable {
+ max-height: 340px;
+ overflow-y: scroll;
+}
+
+.container {
+ margin-right: auto;
+ margin-left: auto;
+ padding-left: 15px;
+ padding-right: 15px;
+}
+
+.container:before, .container:after {
+ content: " ";
+ display: table;
+}
+
+.container:after {
+ clear: both;
+}
+
+.container:before, .container:after {
+ content: " ";
+ display: table;
+}
+
+.container:after {
+ clear: both;
+}
+
+.row {
+ margin-left: -15px;
+ margin-right: -15px;
+}
+
+.row:before, .row:after {
+ content: " ";
+ display: table;
+}
+
+.row:after {
+ clear: both;
+}
+
+.row:before, .row:after {
+ content: " ";
+ display: table;
+}
+
+.row:after {
+ clear: both;
+}
+
+.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6,
+.col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12,
+.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7,
+.col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-md-1,
+.col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8,
+.col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-lg-1, .col-lg-2,
+.col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9,
+.col-lg-10, .col-lg-11, .col-lg-12 {
+ position: relative;
+ min-height: 1px;
+ padding-left: 15px;
+ padding-right: 15px;
+}
+
+.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6,
+.col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11 {
+ float: left;
+}
+
+.col-xs-1 {
+ width: 8.333333333333332%;
+}
+
+.col-xs-2 {
+ width: 16.666666666666664%;
+}
+
+.col-xs-3 {
+ width: 25%;
+}
+
+.col-xs-4 {
+ width: 33.33333333333333%;
+}
+
+.col-xs-5 {
+ width: 41.66666666666667%;
+}
+
+.col-xs-6 {
+ width: 50%;
+}
+
+.col-xs-7 {
+ width: 58.333333333333336%;
+}
+
+.col-xs-8 {
+ width: 66.66666666666666%;
+}
+
+.col-xs-9 {
+ width: 75%;
+}
+
+.col-xs-10 {
+ width: 83.33333333333334%;
+}
+
+.col-xs-11 {
+ width: 91.66666666666666%;
+}
+
+.col-xs-12 {
+ width: 100%;
+}
+
+ at media (min-width:980px){.container {
+ max-width: 750px;
+ }
+
+ .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6,
+ .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11 {
+ float: left;
+ }
+
+ .col-sm-1 {
+ width: 8.333333333333332%;
+ }
+
+ .col-sm-2 {
+ width: 16.666666666666664%;
+ }
+
+ .col-sm-3 {
+ width: 25%;
+ }
+
+ .col-sm-4 {
+ width: 33.33333333333333%;
+ }
+
+ .col-sm-5 {
+ width: 41.66666666666667%;
+ }
+
+ .col-sm-6 {
+ width: 50%;
+ }
+
+ .col-sm-7 {
+ width: 58.333333333333336%;
+ }
+
+ .col-sm-8 {
+ width: 66.66666666666666%;
+ }
+
+ .col-sm-9 {
+ width: 75%;
+ }
+
+ .col-sm-10 {
+ width: 83.33333333333334%;
+ }
+
+ .col-sm-11 {
+ width: 91.66666666666666%;
+ }
+
+ .col-sm-12 {
+ width: 100%;
+ }
+
+ .col-sm-push-1 {
+ left: 8.333333333333332%;
+ }
+
+ .col-sm-push-2 {
+ left: 16.666666666666664%;
+ }
+
+ .col-sm-push-3 {
+ left: 25%;
+ }
+
+ .col-sm-push-4 {
+ left: 33.33333333333333%;
+ }
+
+ .col-sm-push-5 {
+ left: 41.66666666666667%;
+ }
+
+ .col-sm-push-6 {
+ left: 50%;
+ }
+
+ .col-sm-push-7 {
+ left: 58.333333333333336%;
+ }
+
+ .col-sm-push-8 {
+ left: 66.66666666666666%;
+ }
+
+ .col-sm-push-9 {
+ left: 75%;
+ }
+
+ .col-sm-push-10 {
+ left: 83.33333333333334%;
+ }
+
+ .col-sm-push-11 {
+ left: 91.66666666666666%;
+ }
+
+ .col-sm-pull-1 {
+ right: 8.333333333333332%;
+ }
+
+ .col-sm-pull-2 {
+ right: 16.666666666666664%;
+ }
+
+ .col-sm-pull-3 {
+ right: 25%;
+ }
+
+ .col-sm-pull-4 {
+ right: 33.33333333333333%;
+ }
+
+ .col-sm-pull-5 {
+ right: 41.66666666666667%;
+ }
+
+ .col-sm-pull-6 {
+ right: 50%;
+ }
+
+ .col-sm-pull-7 {
+ right: 58.333333333333336%;
+ }
+
+ .col-sm-pull-8 {
+ right: 66.66666666666666%;
+ }
+
+ .col-sm-pull-9 {
+ right: 75%;
+ }
+
+ .col-sm-pull-10 {
+ right: 83.33333333333334%;
+ }
+
+ .col-sm-pull-11 {
+ right: 91.66666666666666%;
+ }
+
+ .col-sm-offset-1 {
+ margin-left: 8.333333333333332%;
+ }
+
+ .col-sm-offset-2 {
+ margin-left: 16.666666666666664%;
+ }
+
+ .col-sm-offset-3 {
+ margin-left: 25%;
+ }
+
+ .col-sm-offset-4 {
+ margin-left: 33.33333333333333%;
+ }
+
+ .col-sm-offset-5 {
+ margin-left: 41.66666666666667%;
+ }
+
+ .col-sm-offset-6 {
+ margin-left: 50%;
+ }
+
+ .col-sm-offset-7 {
+ margin-left: 58.333333333333336%;
+ }
+
+ .col-sm-offset-8 {
+ margin-left: 66.66666666666666%;
+ }
+
+ .col-sm-offset-9 {
+ margin-left: 75%;
+ }
+
+ .col-sm-offset-10 {
+ margin-left: 83.33333333333334%;
+ }
+
+ .col-sm-offset-11 {
+ margin-left: 91.66666666666666%;
+ }
+}
+
+ at media (min-width:992px){.container {
+ max-width: 970px;
+ }
+
+ .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6,
+ .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11 {
+ float: left;
+ }
+
+ .col-md-1 {
+ width: 8.333333333333332%;
+ }
+
+ .col-md-2 {
+ width: 16.666666666666664%;
+ }
+
+ .col-md-3 {
+ width: 25%;
+ }
+
+ .col-md-4 {
+ width: 33.33333333333333%;
+ }
+
+ .col-md-5 {
+ width: 41.66666666666667%;
+ }
+
+ .col-md-6 {
+ width: 50%;
+ }
+
+ .col-md-7 {
+ width: 58.333333333333336%;
+ }
+
+ .col-md-8 {
+ width: 66.66666666666666%;
+ }
+
+ .col-md-9 {
+ width: 75%;
+ }
+
+ .col-md-10 {
+ width: 83.33333333333334%;
+ }
+
+ .col-md-11 {
+ width: 91.66666666666666%;
+ }
+
+ .col-md-12 {
+ width: 100%;
+ }
+
+ .col-md-push-0 {
+ left: auto;
+ }
+
+ .col-md-push-1 {
+ left: 8.333333333333332%;
+ }
+
+ .col-md-push-2 {
+ left: 16.666666666666664%;
+ }
+
+ .col-md-push-3 {
+ left: 25%;
+ }
+
+ .col-md-push-4 {
+ left: 33.33333333333333%;
+ }
+
+ .col-md-push-5 {
+ left: 41.66666666666667%;
+ }
+
+ .col-md-push-6 {
+ left: 50%;
+ }
+
+ .col-md-push-7 {
+ left: 58.333333333333336%;
+ }
+
+ .col-md-push-8 {
+ left: 66.66666666666666%;
+ }
+
+ .col-md-push-9 {
+ left: 75%;
+ }
+
+ .col-md-push-10 {
+ left: 83.33333333333334%;
+ }
+
+ .col-md-push-11 {
+ left: 91.66666666666666%;
+ }
+
+ .col-md-pull-0 {
+ right: auto;
+ }
+
+ .col-md-pull-1 {
+ right: 8.333333333333332%;
+ }
+
+ .col-md-pull-2 {
+ right: 16.666666666666664%;
+ }
+
+ .col-md-pull-3 {
+ right: 25%;
+ }
+
+ .col-md-pull-4 {
+ right: 33.33333333333333%;
+ }
+
+ .col-md-pull-5 {
+ right: 41.66666666666667%;
+ }
+
+ .col-md-pull-6 {
+ right: 50%;
+ }
+
+ .col-md-pull-7 {
+ right: 58.333333333333336%;
+ }
+
+ .col-md-pull-8 {
+ right: 66.66666666666666%;
+ }
+
+ .col-md-pull-9 {
+ right: 75%;
+ }
+
+ .col-md-pull-10 {
+ right: 83.33333333333334%;
+ }
+
+ .col-md-pull-11 {
+ right: 91.66666666666666%;
+ }
+
+ .col-md-offset-0 {
+ margin-left: 0;
+ }
+
+ .col-md-offset-1 {
+ margin-left: 8.333333333333332%;
+ }
+
+ .col-md-offset-2 {
+ margin-left: 16.666666666666664%;
+ }
+
+ .col-md-offset-3 {
+ margin-left: 25%;
+ }
+
+ .col-md-offset-4 {
+ margin-left: 33.33333333333333%;
+ }
+
+ .col-md-offset-5 {
+ margin-left: 41.66666666666667%;
+ }
+
+ .col-md-offset-6 {
+ margin-left: 50%;
+ }
+
+ .col-md-offset-7 {
+ margin-left: 58.333333333333336%;
+ }
+
+ .col-md-offset-8 {
+ margin-left: 66.66666666666666%;
+ }
+
+ .col-md-offset-9 {
+ margin-left: 75%;
+ }
+
+ .col-md-offset-10 {
+ margin-left: 83.33333333333334%;
+ }
+
+ .col-md-offset-11 {
+ margin-left: 91.66666666666666%;
+ }
+}
+
+ at media (min-width:1200px){.container {
+ max-width: 1170px;
+ }
+
+ .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6,
+ .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11 {
+ float: left;
+ }
+
+ .col-lg-1 {
+ width: 8.333333333333332%;
+ }
+
+ .col-lg-2 {
+ width: 16.666666666666664%;
+ }
+
+ .col-lg-3 {
+ width: 25%;
+ }
+
+ .col-lg-4 {
+ width: 33.33333333333333%;
+ }
+
+ .col-lg-5 {
+ width: 41.66666666666667%;
+ }
+
+ .col-lg-6 {
+ width: 50%;
+ }
+
+ .col-lg-7 {
+ width: 58.333333333333336%;
+ }
+
+ .col-lg-8 {
+ width: 66.66666666666666%;
+ }
+
+ .col-lg-9 {
+ width: 75%;
+ }
+
+ .col-lg-10 {
+ width: 83.33333333333334%;
+ }
+
+ .col-lg-11 {
+ width: 91.66666666666666%;
+ }
+
+ .col-lg-12 {
+ width: 100%;
+ }
+
+ .col-lg-push-0 {
+ left: auto;
+ }
+
+ .col-lg-push-1 {
+ left: 8.333333333333332%;
+ }
+
+ .col-lg-push-2 {
+ left: 16.666666666666664%;
+ }
+
+ .col-lg-push-3 {
+ left: 25%;
+ }
+
+ .col-lg-push-4 {
+ left: 33.33333333333333%;
+ }
+
+ .col-lg-push-5 {
+ left: 41.66666666666667%;
+ }
+
+ .col-lg-push-6 {
+ left: 50%;
+ }
+
+ .col-lg-push-7 {
+ left: 58.333333333333336%;
+ }
+
+ .col-lg-push-8 {
+ left: 66.66666666666666%;
+ }
+
+ .col-lg-push-9 {
+ left: 75%;
+ }
+
+ .col-lg-push-10 {
+ left: 83.33333333333334%;
+ }
+
+ .col-lg-push-11 {
+ left: 91.66666666666666%;
+ }
+
+ .col-lg-pull-0 {
+ right: auto;
+ }
+
+ .col-lg-pull-1 {
+ right: 8.333333333333332%;
+ }
+
+ .col-lg-pull-2 {
+ right: 16.666666666666664%;
+ }
+
+ .col-lg-pull-3 {
+ right: 25%;
+ }
+
+ .col-lg-pull-4 {
+ right: 33.33333333333333%;
+ }
+
+ .col-lg-pull-5 {
+ right: 41.66666666666667%;
+ }
+
+ .col-lg-pull-6 {
+ right: 50%;
+ }
+
+ .col-lg-pull-7 {
+ right: 58.333333333333336%;
+ }
+
+ .col-lg-pull-8 {
+ right: 66.66666666666666%;
+ }
+
+ .col-lg-pull-9 {
+ right: 75%;
+ }
+
+ .col-lg-pull-10 {
+ right: 83.33333333333334%;
+ }
+
+ .col-lg-pull-11 {
+ right: 91.66666666666666%;
+ }
+
+ .col-lg-offset-0 {
+ margin-left: 0;
+ }
+
+ .col-lg-offset-1 {
+ margin-left: 8.333333333333332%;
+ }
+
+ .col-lg-offset-2 {
+ margin-left: 16.666666666666664%;
+ }
+
+ .col-lg-offset-3 {
+ margin-left: 25%;
+ }
+
+ .col-lg-offset-4 {
+ margin-left: 33.33333333333333%;
+ }
+
+ .col-lg-offset-5 {
+ margin-left: 41.66666666666667%;
+ }
+
+ .col-lg-offset-6 {
+ margin-left: 50%;
+ }
+
+ .col-lg-offset-7 {
+ margin-left: 58.333333333333336%;
+ }
+
+ .col-lg-offset-8 {
+ margin-left: 66.66666666666666%;
+ }
+
+ .col-lg-offset-9 {
+ margin-left: 75%;
+ }
+
+ .col-lg-offset-10 {
+ margin-left: 83.33333333333334%;
+ }
+
+ .col-lg-offset-11 {
+ margin-left: 91.66666666666666%;
+ }
+}
+
+table {
+ max-width: 100%;
+ background-color: transparent;
+}
+
+th {
+ text-align: left;
+}
+
+.table {
+ /* width: 100%; */
+ margin-bottom: 20px;
+ margin-left: 50px;
+}
+
+.table thead>tr>th, .table tbody>tr>th, .table tfoot>tr>th, .table thead>tr>td,
+.table tbody>tr>td, .table tfoot>tr>td {
+ padding: 8px;
+ line-height: 1.428571429;
+ vertical-align: top;
+ border: 1px solid #dddddd;
+}
+
+.table thead>tr>th {
+ vertical-align: bottom;
+ border: 1px solid #dddddd;
+ background-color: #f2f2f2;
+}
+
+/* .table caption+thead tr:first-child th, .table colgroup+thead tr:first-child th, */
+/* .table thead:first-child tr:first-child th, .table caption+thead tr:first-child td, */
+/* .table colgroup+thead tr:first-child td, .table thead:first-child tr:first-child td { */
+/* border-top: 0; */
+/* } */
+
+.table tbody+tbody {
+ border-top: 2px solid #dddddd;
+}
+
+.table .table {
+ background-color: #ffffff;
+}
+
+.table-condensed thead>tr>th, .table-condensed tbody>tr>th, .table-condensed tfoot>tr>th,
+.table-condensed thead>tr>td, .table-condensed tbody>tr>td, .table-condensed tfoot>tr>td {
+ padding: 5px;
+}
+
+.table-bordered {
+ border: 1px solid #dddddd;
+}
+
+.table-bordered>thead>tr>th, .table-bordered>tbody>tr>th, .table-bordered>tfoot>tr>th,
+.table-bordered>thead>tr>td, .table-bordered>tbody>tr>td, .table-bordered>tfoot>tr>td {
+ border: 1px solid #dddddd;
+}
+
+.table-bordered>thead>tr>th, .table-bordered>thead>tr>td {
+ border-bottom-width: 2px;
+}
+
+.table-striped>tbody>tr:nth-child(odd)>td, .table-striped>tbody>tr:nth-child(odd)>th {
+ background-color: #f9f9f9;
+}
+
+.table-hover>tbody>tr:hover>td, .table-hover>tbody>tr:hover>th {
+ background-color: #f5f5f5;
+}
+
+table col[class*="col-"] {
+ float: none;
+ display: table-column;
+}
+
+table td[class*="col-"], table th[class*="col-"] {
+ float: none;
+ display: table-cell;
+}
+
+.table>thead>tr>td.active, .table>tbody>tr>td.active, .table>tfoot>tr>td.active,
+.table>thead>tr>th.active, .table>tbody>tr>th.active, .table>tfoot>tr>th.active,
+.table>thead>tr.active>td, .table>tbody>tr.active>td, .table>tfoot>tr.active>td,
+.table>thead>tr.active>th, .table>tbody>tr.active>th, .table>tfoot>tr.active>th {
+ background-color: #f5f5f5;
+}
+
+.table>thead>tr>td.success, .table>tbody>tr>td.success, .table>tfoot>tr>td.success,
+.table>thead>tr>th.success, .table>tbody>tr>th.success, .table>tfoot>tr>th.success,
+.table>thead>tr.success>td, .table>tbody>tr.success>td, .table>tfoot>tr.success>td,
+.table>thead>tr.success>th, .table>tbody>tr.success>th, .table>tfoot>tr.success>th {
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+}
+
+.table-hover>tbody>tr>td.success:hover, .table-hover>tbody>tr>th.success:hover,
+.table-hover>tbody>tr.success:hover>td {
+ background-color: #d0e9c6;
+ border-color: #c9e2b3;
+}
+
+.table>thead>tr>td.danger, .table>tbody>tr>td.danger, .table>tfoot>tr>td.danger,
+.table>thead>tr>th.danger, .table>tbody>tr>th.danger, .table>tfoot>tr>th.danger,
+.table>thead>tr.danger>td, .table>tbody>tr.danger>td, .table>tfoot>tr.danger>td,
+.table>thead>tr.danger>th, .table>tbody>tr.danger>th, .table>tfoot>tr.danger>th {
+ background-color: #f2dede;
+ border-color: #eed3d7;
+}
+
+.table-hover>tbody>tr>td.danger:hover, .table-hover>tbody>tr>th.danger:hover,
+.table-hover>tbody>tr.danger:hover>td {
+ background-color: #ebcccc;
+ border-color: #e6c1c7;
+}
+
+.table>thead>tr>td.warning, .table>tbody>tr>td.warning, .table>tfoot>tr>td.warning,
+.table>thead>tr>th.warning, .table>tbody>tr>th.warning, .table>tfoot>tr>th.warning,
+.table>thead>tr.warning>td, .table>tbody>tr.warning>td, .table>tfoot>tr.warning>td,
+.table>thead>tr.warning>th, .table>tbody>tr.warning>th, .table>tfoot>tr.warning>th {
+ background-color: #fcf8e3;
+ border-color: #fbeed5;
+}
+
+.table-hover>tbody>tr>td.warning:hover, .table-hover>tbody>tr>th.warning:hover,
+.table-hover>tbody>tr.warning:hover>td {
+ background-color: #faf2cc;
+ border-color: #f8e5be;
+}
+
+ at media (max-width:980px){.table-responsive {
+ width: 100%;
+ margin-bottom: 15px;
+ overflow-y: hidden;
+ overflow-x: scroll;
+ border: 1px solid #dddddd;
+ }
+
+ .table-responsive>.table {
+ margin-bottom: 0;
+ background-color: #fff;
+ }
+
+ .table-responsive>.table>thead>tr>th, .table-responsive>.table>tbody>tr>th,
+ .table-responsive>.table>tfoot>tr>th, .table-responsive>.table>thead>tr>td,
+ .table-responsive>.table>tbody>tr>td, .table-responsive>.table>tfoot>tr>td {
+ white-space: nowrap;
+ }
+
+ .table-responsive>.table-bordered {
+ border: 0;
+ }
+
+ .table-responsive>.table-bordered>thead>tr>th:first-child, .table-responsive>.table-bordered>tbody>tr>th:first-child,
+ .table-responsive>.table-bordered>tfoot>tr>th:first-child, .table-responsive>.table-bordered>thead>tr>td:first-child,
+ .table-responsive>.table-bordered>tbody>tr>td:first-child, .table-responsive>.table-bordered>tfoot>tr>td:first-child {
+ border-left: 0;
+ }
+
+ .table-responsive>.table-bordered>thead>tr>th:last-child, .table-responsive>.table-bordered>tbody>tr>th:last-child,
+ .table-responsive>.table-bordered>tfoot>tr>th:last-child, .table-responsive>.table-bordered>thead>tr>td:last-child,
+ .table-responsive>.table-bordered>tbody>tr>td:last-child, .table-responsive>.table-bordered>tfoot>tr>td:last-child {
+ border-right: 0;
+ }
+
+ .table-responsive>.table-bordered>thead>tr:last-child>th, .table-responsive>.table-bordered>tbody>tr:last-child>th,
+ .table-responsive>.table-bordered>tfoot>tr:last-child>th, .table-responsive>.table-bordered>thead>tr:last-child>td,
+ .table-responsive>.table-bordered>tbody>tr:last-child>td, .table-responsive>.table-bordered>tfoot>tr:last-child>td {
+ border-bottom: 0;
+ }
+}
+
+fieldset {
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+
+legend {
+ display: block;
+ width: 100%;
+ padding: 0;
+ margin-bottom: 20px;
+ font-size: 21px;
+ line-height: inherit;
+ color: #333333;
+ border: 0;
+ border-bottom: 1px solid #e5e5e5;
+}
+
+label {
+ display: inline-block;
+ margin-bottom: 5px;
+ font-weight: bold;
+}
+
+input[type="search"] {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+input[type="radio"], input[type="checkbox"] {
+ margin: 4px 0 0;
+ margin-top: 1px \9;
+ line-height: normal;
+}
+
+input[type="file"] {
+ display: block;
+}
+
+select[multiple], select[size] {
+ height: auto;
+}
+
+select optgroup {
+ font-size: inherit;
+ font-style: inherit;
+ font-family: inherit;
+}
+
+input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button {
+ height: auto;
+}
+
+.form-control:-moz-placeholder {
+ color: #999999;
+}
+
+.form-control::-moz-placeholder {
+ color: #999999;
+}
+
+.form-control:-ms-input-placeholder {
+ color: #999999;
+}
+
+.form-control::-webkit-input-placeholder {
+ color: #999999;
+}
+
+.form-control {
+ display: block;
+ width: 100%;
+ height: 34px;
+ padding: 6px 12px;
+ font-size: 14px;
+ line-height: 1.428571429;
+ color: #555555;
+ vertical-align: middle;
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+ transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+}
+
+.form-control:focus {
+ border-color: #66afe9;
+ outline: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);
+}
+
+.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control {
+ cursor: not-allowed;
+ background-color: #eeeeee;
+}
+
+textarea.form-control {
+ height: auto;
+}
+
+.form-group {
+ margin-bottom: 15px;
+}
+
+.radio, .checkbox {
+ display: block;
+ min-height: 20px;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding-left: 20px;
+ vertical-align: middle;
+}
+
+.radio label, .checkbox label {
+ display: inline;
+ margin-bottom: 0;
+ font-weight: normal;
+ cursor: pointer;
+}
+
+.radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"],
+.checkbox-inline input[type="checkbox"] {
+ float: left;
+ margin-left: -20px;
+}
+
+.radio+.radio, .checkbox+.checkbox {
+ margin-top: -5px;
+}
+
+.radio-inline, .checkbox-inline {
+ display: inline-block;
+ padding-left: 20px;
+ margin-bottom: 0;
+ vertical-align: middle;
+ font-weight: normal;
+ cursor: pointer;
+}
+
+.radio-inline+.radio-inline, .checkbox-inline+.checkbox-inline {
+ margin-top: 0;
+ margin-left: 10px;
+}
+
+input[type="radio"][disabled], input[type="checkbox"][disabled],
+.radio[disabled], .radio-inline[disabled], .checkbox[disabled],
+.checkbox-inline[disabled], fieldset[disabled] input[type="radio"],
+fieldset[disabled] input[type="checkbox"], fieldset[disabled] .radio,
+fieldset[disabled] .radio-inline, fieldset[disabled] .checkbox, fieldset[disabled] .checkbox-inline {
+ cursor: not-allowed;
+}
+
+.input-sm {
+ height: 30px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+
+select.input-sm {
+ height: 30px;
+ line-height: 30px;
+}
+
+textarea.input-sm {
+ height: auto;
+}
+
+.input-lg {
+ height: 45px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
+}
+
+select.input-lg {
+ height: 45px;
+ line-height: 45px;
+}
+
+textarea.input-lg {
+ height: auto;
+}
+
+.has-warning .help-block, .has-warning .control-label {
+ color: #c09853;
+}
+
+.has-warning .form-control {
+ border-color: #c09853;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.has-warning .form-control:focus {
+ border-color: #a47e3c;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+}
+
+.has-warning .input-group-addon {
+ color: #c09853;
+ border-color: #c09853;
+ background-color: #fcf8e3;
+}
+
+.has-error .help-block, .has-error .control-label {
+ color: #b94a48;
+}
+
+.has-error .form-control {
+ border-color: #b94a48;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.has-error .form-control:focus {
+ border-color: #953b39;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+}
+
+.has-error .input-group-addon {
+ color: #b94a48;
+ border-color: #b94a48;
+ background-color: #f2dede;
+}
+
+.has-success .help-block, .has-success .control-label {
+ color: #468847;
+}
+
+.has-success .form-control {
+ border-color: #468847;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.has-success .form-control:focus {
+ border-color: #356635;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+}
+
+.has-success .input-group-addon {
+ color: #468847;
+ border-color: #468847;
+ background-color: #dff0d8;
+}
+
+.form-control-static {
+ margin-bottom: 0;
+ padding-top: 7px;
+}
+
+.help-block {
+ display: block;
+ margin-top: 5px;
+ margin-bottom: 10px;
+ color: #737373;
+}
+
+ at media (min-width:980px){.form-inline .form-group {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+
+ .form-inline .form-control {
+ display: inline-block;
+ }
+
+ .form-inline .radio, .form-inline .checkbox {
+ display: inline-block;
+ margin-top: 0;
+ margin-bottom: 0;
+ padding-left: 0;
+ }
+
+ .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] {
+ float: none;
+ margin-left: 0;
+ }
+}
+
+.form-horizontal .control-label, .form-horizontal .radio, .form-horizontal .checkbox,
+.form-horizontal .radio-inline, .form-horizontal .checkbox-inline {
+ margin-top: 0;
+ margin-bottom: 0;
+ padding-top: 7px;
+}
+
+.form-horizontal .form-group {
+ margin-left: -15px;
+ margin-right: -15px;
+}
+
+.form-horizontal .form-group:before, .form-horizontal .form-group:after {
+ content: " ";
+ display: table;
+}
+
+.form-horizontal .form-group:after {
+ clear: both;
+}
+
+.form-horizontal .form-group:before, .form-horizontal .form-group:after {
+ content: " ";
+ display: table;
+}
+
+.form-horizontal .form-group:after {
+ clear: both;
+}
+
+ at media (min-width:980px){.form-horizontal .control-label {
+ text-align: right;
+ }
+}
+
+.btn {
+ display: inline-block;
+ padding: 6px 12px;
+ margin-bottom: 0;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 1.428571429;
+ text-align: center;
+ vertical-align: middle;
+ cursor: pointer;
+ border: 1px solid transparent;
+ border-radius: 4px;
+ white-space: nowrap;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+}
+
+.btn:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+.btn:hover, .btn:focus {
+ color: #333333;
+ text-decoration: none;
+}
+
+.btn:active, .btn.active {
+ outline: 0;
+ background-image: none;
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
+
+.btn.disabled, .btn[disabled], fieldset[disabled] .btn {
+ cursor: not-allowed;
+ pointer-events: none;
+ opacity: 0.65;
+ filter: alpha(opacity=65);
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+
+.btn-default {
+ color: #333333;
+ background-color: #ffffff;
+ border-color: #cccccc;
+}
+
+.btn-default:hover, .btn-default:focus, .btn-default:active, .btn-default.active,
+.open .dropdown-toggle.btn-default {
+ color: #333333;
+ background-color: #ebebeb;
+ border-color: #adadad;
+}
+
+.btn-default:active, .btn-default.active, .open .dropdown-toggle.btn-default {
+ background-image: none;
+}
+
+.btn-default.disabled, .btn-default[disabled], fieldset[disabled] .btn-default,
+.btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus,
+.btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active,
+.btn-default.disabled.active, .btn-default[disabled].active, fieldset[disabled] .btn-default.active {
+ background-color: #ffffff;
+ border-color: #cccccc;
+}
+
+.btn-primary {
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #357ebd;
+}
+
+.btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active,
+.open .dropdown-toggle.btn-primary {
+ color: #ffffff;
+ background-color: #3276b1;
+ border-color: #285e8e;
+}
+
+.btn-primary:active, .btn-primary.active, .open .dropdown-toggle.btn-primary {
+ background-image: none;
+}
+
+.btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active {
+ background-color: #428bca;
+ border-color: #357ebd;
+}
+
+.btn-warning {
+ color: #ffffff;
+ background-color: #f0ad4e;
+ border-color: #eea236;
+}
+
+.btn-warning:hover, .btn-warning:focus, .btn-warning:active, .btn-warning.active,
+.open .dropdown-toggle.btn-warning {
+ color: #ffffff;
+ background-color: #ed9c28;
+ border-color: #d58512;
+}
+
+.btn-warning:active, .btn-warning.active, .open .dropdown-toggle.btn-warning {
+ background-image: none;
+}
+
+.btn-warning.disabled, .btn-warning[disabled], fieldset[disabled] .btn-warning,
+.btn-warning.disabled:hover, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled:active, .btn-warning[disabled]:active, fieldset[disabled] .btn-warning:active,
+.btn-warning.disabled.active, .btn-warning[disabled].active, fieldset[disabled] .btn-warning.active {
+ background-color: #f0ad4e;
+ border-color: #eea236;
+}
+
+.btn-danger {
+ color: #ffffff;
+ background-color: #d9534f;
+ border-color: #d43f3a;
+}
+
+.btn-danger:hover, .btn-danger:focus, .btn-danger:active, .btn-danger.active,
+.open .dropdown-toggle.btn-danger {
+ color: #ffffff;
+ background-color: #d2322d;
+ border-color: #ac2925;
+}
+
+.btn-danger:active, .btn-danger.active, .open .dropdown-toggle.btn-danger {
+ background-image: none;
+}
+
+.btn-danger.disabled, .btn-danger[disabled], fieldset[disabled] .btn-danger,
+.btn-danger.disabled:hover, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled:active, .btn-danger[disabled]:active, fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active, .btn-danger[disabled].active, fieldset[disabled] .btn-danger.active {
+ background-color: #d9534f;
+ border-color: #d43f3a;
+}
+
+.btn-success {
+ color: #ffffff;
+ background-color: #5cb85c;
+ border-color: #4cae4c;
+}
+
+.btn-success:hover, .btn-success:focus, .btn-success:active, .btn-success.active,
+.open .dropdown-toggle.btn-success {
+ color: #ffffff;
+ background-color: #47a447;
+ border-color: #398439;
+}
+
+.btn-success:active, .btn-success.active, .open .dropdown-toggle.btn-success {
+ background-image: none;
+}
+
+.btn-success.disabled, .btn-success[disabled], fieldset[disabled] .btn-success,
+.btn-success.disabled:hover, .btn-success[disabled]:hover, fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus,
+.btn-success.disabled:active, .btn-success[disabled]:active, fieldset[disabled] .btn-success:active,
+.btn-success.disabled.active, .btn-success[disabled].active, fieldset[disabled] .btn-success.active {
+ background-color: #5cb85c;
+ border-color: #4cae4c;
+}
+
+.btn-info {
+ color: #ffffff;
+ background-color: #5bc0de;
+ border-color: #46b8da;
+}
+
+.btn-info:hover, .btn-info:focus, .btn-info:active, .btn-info.active,
+.open .dropdown-toggle.btn-info {
+ color: #ffffff;
+ background-color: #39b3d7;
+ border-color: #269abc;
+}
+
+.btn-info:active, .btn-info.active, .open .dropdown-toggle.btn-info {
+ background-image: none;
+}
+
+.btn-info.disabled, .btn-info[disabled], fieldset[disabled] .btn-info,
+.btn-info.disabled:hover, .btn-info[disabled]:hover, fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus,
+.btn-info.disabled:active, .btn-info[disabled]:active, fieldset[disabled] .btn-info:active,
+.btn-info.disabled.active, .btn-info[disabled].active, fieldset[disabled] .btn-info.active {
+ background-color: #5bc0de;
+ border-color: #46b8da;
+}
+
+.btn-link {
+ color: #428bca;
+ font-weight: normal;
+ cursor: pointer;
+ border-radius: 0;
+}
+
+.btn-link, .btn-link:active, .btn-link[disabled], fieldset[disabled] .btn-link {
+ background-color: transparent;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+
+.btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active {
+ border-color: transparent;
+}
+
+.btn-link:hover, .btn-link:focus {
+ color: #2a6496;
+ text-decoration: underline;
+ background-color: transparent;
+}
+
+.btn-link[disabled]:hover, fieldset[disabled] .btn-link:hover,
+.btn-link[disabled]:focus, fieldset[disabled] .btn-link:focus {
+ color: #999999;
+ text-decoration: none;
+}
+
+.btn-lg {
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
+}
+
+.btn-sm, .btn-xs {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+
+.btn-xs {
+ padding: 1px 5px;
+}
+
+.btn-block {
+ display: block;
+ width: 100%;
+ padding-left: 0;
+ padding-right: 0;
+}
+
+.btn-block+.btn-block {
+ margin-top: 5px;
+}
+
+input[type="submit"].btn-block, input[type="reset"].btn-block,
+input[type="button"].btn-block {
+ width: 100%;
+}
+
+ at font-face {
+ font-family: 'Glyphicons Halflings';
+ src: url('../fonts/glyphicons-halflings-regular.eot');
+ src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');
+}
+
+.glyphicon {
+ position: relative;
+ top: 1px;
+ display: inline-block;
+ font-family: 'Glyphicons Halflings';
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ -webkit-font-smoothing: antialiased;
+}
+
+.glyphicon-asterisk:before {
+ content: "\2a";
+}
+
+.glyphicon-plus:before {
+ content: "\2b";
+}
+
+.glyphicon-euro:before {
+ content: "\20ac";
+}
+
+.glyphicon-minus:before {
+ content: "\2212";
+}
+
+.glyphicon-cloud:before {
+ content: "\2601";
+}
+
+.glyphicon-envelope:before {
+ content: "\2709";
+}
+
+.glyphicon-pencil:before {
+ content: "\270f";
+}
+
+.glyphicon-glass:before {
+ content: "\e001";
+}
+
+.glyphicon-music:before {
+ content: "\e002";
+}
+
+.glyphicon-search:before {
+ content: "\e003";
+}
+
+.glyphicon-heart:before {
+ content: "\e005";
+}
+
+.glyphicon-star:before {
+ content: "\e006";
+}
+
+.glyphicon-star-empty:before {
+ content: "\e007";
+}
+
+.glyphicon-user:before {
+ content: "\e008";
+}
+
+.glyphicon-film:before {
+ content: "\e009";
+}
+
+.glyphicon-th-large:before {
+ content: "\e010";
+}
+
+.glyphicon-th:before {
+ content: "\e011";
+}
+
+.glyphicon-th-list:before {
+ content: "\e012";
+}
+
+.glyphicon-ok:before {
+ content: "\e013";
+}
+
+.glyphicon-remove:before {
+ content: "\e014";
+}
+
+.glyphicon-zoom-in:before {
+ content: "\e015";
+}
+
+.glyphicon-zoom-out:before {
+ content: "\e016";
+}
+
+.glyphicon-off:before {
+ content: "\e017";
+}
+
+.glyphicon-signal:before {
+ content: "\e018";
+}
+
+.glyphicon-cog:before {
+ content: "\e019";
+}
+
+.glyphicon-trash:before {
+ content: "\e020";
+}
+
+.glyphicon-home:before {
+ content: "\e021";
+}
+
+.glyphicon-file:before {
+ content: "\e022";
+}
+
+.glyphicon-time:before {
+ content: "\e023";
+}
+
+.glyphicon-road:before {
+ content: "\e024";
+}
+
+.glyphicon-download-alt:before {
+ content: "\e025";
+}
+
+.glyphicon-download:before {
+ content: "\e026";
+}
+
+.glyphicon-upload:before {
+ content: "\e027";
+}
+
+.glyphicon-inbox:before {
+ content: "\e028";
+}
+
+.glyphicon-play-circle:before {
+ content: "\e029";
+}
+
+.glyphicon-repeat:before {
+ content: "\e030";
+}
+
+.glyphicon-refresh:before {
+ content: "\e031";
+}
+
+.glyphicon-list-alt:before {
+ content: "\e032";
+}
+
+.glyphicon-flag:before {
+ content: "\e034";
+}
+
+.glyphicon-headphones:before {
+ content: "\e035";
+}
+
+.glyphicon-volume-off:before {
+ content: "\e036";
+}
+
+.glyphicon-volume-down:before {
+ content: "\e037";
+}
+
+.glyphicon-volume-up:before {
+ content: "\e038";
+}
+
+.glyphicon-qrcode:before {
+ content: "\e039";
+}
+
+.glyphicon-barcode:before {
+ content: "\e040";
+}
+
+.glyphicon-tag:before {
+ content: "\e041";
+}
+
+.glyphicon-tags:before {
+ content: "\e042";
+}
+
+.glyphicon-book:before {
+ content: "\e043";
+}
+
+.glyphicon-print:before {
+ content: "\e045";
+}
+
+.glyphicon-font:before {
+ content: "\e047";
+}
+
+.glyphicon-bold:before {
+ content: "\e048";
+}
+
+.glyphicon-italic:before {
+ content: "\e049";
+}
+
+.glyphicon-text-height:before {
+ content: "\e050";
+}
+
+.glyphicon-text-width:before {
+ content: "\e051";
+}
+
+.glyphicon-align-left:before {
+ content: "\e052";
+}
+
+.glyphicon-align-center:before {
+ content: "\e053";
+}
+
+.glyphicon-align-right:before {
+ content: "\e054";
+}
+
+.glyphicon-align-justify:before {
+ content: "\e055";
+}
+
+.glyphicon-list:before {
+ content: "\e056";
+}
+
+.glyphicon-indent-left:before {
+ content: "\e057";
+}
+
+.glyphicon-indent-right:before {
+ content: "\e058";
+}
+
+.glyphicon-facetime-video:before {
+ content: "\e059";
+}
+
+.glyphicon-picture:before {
+ content: "\e060";
+}
+
+.glyphicon-map-marker:before {
+ content: "\e062";
+}
+
+.glyphicon-adjust:before {
+ content: "\e063";
+}
+
+.glyphicon-tint:before {
+ content: "\e064";
+}
+
+.glyphicon-edit:before {
+ content: "\e065";
+}
+
+.glyphicon-share:before {
+ content: "\e066";
+}
+
+.glyphicon-check:before {
+ content: "\e067";
+}
+
+.glyphicon-move:before {
+ content: "\e068";
+}
+
+.glyphicon-step-backward:before {
+ content: "\e069";
+}
+
+.glyphicon-fast-backward:before {
+ content: "\e070";
+}
+
+.glyphicon-backward:before {
+ content: "\e071";
+}
+
+.glyphicon-play:before {
+ content: "\e072";
+}
+
+.glyphicon-pause:before {
+ content: "\e073";
+}
+
+.glyphicon-stop:before {
+ content: "\e074";
+}
+
+.glyphicon-forward:before {
+ content: "\e075";
+}
+
+.glyphicon-fast-forward:before {
+ content: "\e076";
+}
+
+.glyphicon-step-forward:before {
+ content: "\e077";
+}
+
+.glyphicon-eject:before {
+ content: "\e078";
+}
+
+.glyphicon-chevron-left:before {
+ content: "\e079";
+}
+
+.glyphicon-chevron-right:before {
+ content: "\e080";
+}
+
+.glyphicon-plus-sign:before {
+ content: "\e081";
+}
+
+.glyphicon-minus-sign:before {
+ content: "\e082";
+}
+
+.glyphicon-remove-sign:before {
+ content: "\e083";
+}
+
+.glyphicon-ok-sign:before {
+ content: "\e084";
+}
+
+.glyphicon-question-sign:before {
+ content: "\e085";
+}
+
+.glyphicon-info-sign:before {
+ content: "\e086";
+}
+
+.glyphicon-screenshot:before {
+ content: "\e087";
+}
+
+.glyphicon-remove-circle:before {
+ content: "\e088";
+}
+
+.glyphicon-ok-circle:before {
+ content: "\e089";
+}
+
+.glyphicon-ban-circle:before {
+ content: "\e090";
+}
+
+.glyphicon-arrow-left:before {
+ content: "\e091";
+}
+
+.glyphicon-arrow-right:before {
+ content: "\e092";
+}
+
+.glyphicon-arrow-up:before {
+ content: "\e093";
+}
+
+.glyphicon-arrow-down:before {
+ content: "\e094";
+}
+
+.glyphicon-share-alt:before {
+ content: "\e095";
+}
+
+.glyphicon-resize-full:before {
+ content: "\e096";
+}
+
+.glyphicon-resize-small:before {
+ content: "\e097";
+}
+
+.glyphicon-exclamation-sign:before {
+ content: "\e101";
+}
+
+.glyphicon-gift:before {
+ content: "\e102";
+}
+
+.glyphicon-leaf:before {
+ content: "\e103";
+}
+
+.glyphicon-eye-open:before {
+ content: "\e105";
+}
+
+.glyphicon-eye-close:before {
+ content: "\e106";
+}
+
+.glyphicon-warning-sign:before {
+ content: "\e107";
+}
+
+.glyphicon-plane:before {
+ content: "\e108";
+}
+
+.glyphicon-random:before {
+ content: "\e110";
+}
+
+.glyphicon-comment:before {
+ content: "\e111";
+}
+
+.glyphicon-magnet:before {
+ content: "\e112";
+}
+
+.glyphicon-chevron-up:before {
+ content: "\e113";
+}
+
+.glyphicon-chevron-down:before {
+ content: "\e114";
+}
+
+.glyphicon-retweet:before {
+ content: "\e115";
+}
+
+.glyphicon-shopping-cart:before {
+ content: "\e116";
+}
+
+.glyphicon-folder-close:before {
+ content: "\e117";
+}
+
+.glyphicon-folder-open:before {
+ content: "\e118";
+}
+
+.glyphicon-resize-vertical:before {
+ content: "\e119";
+}
+
+.glyphicon-resize-horizontal:before {
+ content: "\e120";
+}
+
+.glyphicon-hdd:before {
+ content: "\e121";
+}
+
+.glyphicon-bullhorn:before {
+ content: "\e122";
+}
+
+.glyphicon-certificate:before {
+ content: "\e124";
+}
+
+.glyphicon-thumbs-up:before {
+ content: "\e125";
+}
+
+.glyphicon-thumbs-down:before {
+ content: "\e126";
+}
+
+.glyphicon-hand-right:before {
+ content: "\e127";
+}
+
+.glyphicon-hand-left:before {
+ content: "\e128";
+}
+
+.glyphicon-hand-up:before {
+ content: "\e129";
+}
+
+.glyphicon-hand-down:before {
+ content: "\e130";
+}
+
+.glyphicon-circle-arrow-right:before {
+ content: "\e131";
+}
+
+.glyphicon-circle-arrow-left:before {
+ content: "\e132";
+}
+
+.glyphicon-circle-arrow-up:before {
+ content: "\e133";
+}
+
+.glyphicon-circle-arrow-down:before {
+ content: "\e134";
+}
+
+.glyphicon-globe:before {
+ content: "\e135";
+}
+
+.glyphicon-tasks:before {
+ content: "\e137";
+}
+
+.glyphicon-filter:before {
+ content: "\e138";
+}
+
+.glyphicon-fullscreen:before {
+ content: "\e140";
+}
+
+.glyphicon-dashboard:before {
+ content: "\e141";
+}
+
+.glyphicon-heart-empty:before {
+ content: "\e143";
+}
+
+.glyphicon-link:before {
+ content: "\e144";
+}
+
+.glyphicon-phone:before {
+ content: "\e145";
+}
+
+.glyphicon-usd:before {
+ content: "\e148";
+}
+
+.glyphicon-gbp:before {
+ content: "\e149";
+}
+
+.glyphicon-sort:before {
+ content: "\e150";
+}
+
+.glyphicon-sort-by-alphabet:before {
+ content: "\e151";
+}
+
+.glyphicon-sort-by-alphabet-alt:before {
+ content: "\e152";
+}
+
+.glyphicon-sort-by-order:before {
+ content: "\e153";
+}
+
+.glyphicon-sort-by-order-alt:before {
+ content: "\e154";
+}
+
+.glyphicon-sort-by-attributes:before {
+ content: "\e155";
+}
+
+.glyphicon-sort-by-attributes-alt:before {
+ content: "\e156";
+}
+
+.glyphicon-unchecked:before {
+ content: "\e157";
+}
+
+.glyphicon-expand:before {
+ content: "\e158";
+}
+
+.glyphicon-collapse-down:before {
+ content: "\e159";
+}
+
+.glyphicon-collapse-up:before {
+ content: "\e160";
+}
+
+.glyphicon-log-in:before {
+ content: "\e161";
+}
+
+.glyphicon-flash:before {
+ content: "\e162";
+}
+
+.glyphicon-log-out:before {
+ content: "\e163";
+}
+
+.glyphicon-new-window:before {
+ content: "\e164";
+}
+
+.glyphicon-record:before {
+ content: "\e165";
+}
+
+.glyphicon-save:before {
+ content: "\e166";
+}
+
+.glyphicon-open:before {
+ content: "\e167";
+}
+
+.glyphicon-saved:before {
+ content: "\e168";
+}
+
+.glyphicon-import:before {
+ content: "\e169";
+}
+
+.glyphicon-export:before {
+ content: "\e170";
+}
+
+.glyphicon-send:before {
+ content: "\e171";
+}
+
+.glyphicon-floppy-disk:before {
+ content: "\e172";
+}
+
+.glyphicon-floppy-saved:before {
+ content: "\e173";
+}
+
+.glyphicon-floppy-remove:before {
+ content: "\e174";
+}
+
+.glyphicon-floppy-save:before {
+ content: "\e175";
+}
+
+.glyphicon-floppy-open:before {
+ content: "\e176";
+}
+
+.glyphicon-credit-card:before {
+ content: "\e177";
+}
+
+.glyphicon-transfer:before {
+ content: "\e178";
+}
+
+.glyphicon-cutlery:before {
+ content: "\e179";
+}
+
+.glyphicon-header:before {
+ content: "\e180";
+}
+
+.glyphicon-compressed:before {
+ content: "\e181";
+}
+
+.glyphicon-earphone:before {
+ content: "\e182";
+}
+
+.glyphicon-phone-alt:before {
+ content: "\e183";
+}
+
+.glyphicon-tower:before {
+ content: "\e184";
+}
+
+.glyphicon-stats:before {
+ content: "\e185";
+}
+
+.glyphicon-sd-video:before {
+ content: "\e186";
+}
+
+.glyphicon-hd-video:before {
+ content: "\e187";
+}
+
+.glyphicon-subtitles:before {
+ content: "\e188";
+}
+
+.glyphicon-sound-stereo:before {
+ content: "\e189";
+}
+
+.glyphicon-sound-dolby:before {
+ content: "\e190";
+}
+
+.glyphicon-sound-5-1:before {
+ content: "\e191";
+}
+
+.glyphicon-sound-6-1:before {
+ content: "\e192";
+}
+
+.glyphicon-sound-7-1:before {
+ content: "\e193";
+}
+
+.glyphicon-copyright-mark:before {
+ content: "\e194";
+}
+
+.glyphicon-registration-mark:before {
+ content: "\e195";
+}
+
+.glyphicon-cloud-download:before {
+ content: "\e197";
+}
+
+.glyphicon-cloud-upload:before {
+ content: "\e198";
+}
+
+.glyphicon-tree-conifer:before {
+ content: "\e199";
+}
+
+.glyphicon-tree-deciduous:before {
+ content: "\e200";
+}
+
+.glyphicon-briefcase:before {
+ content: "\1f4bc";
+}
+
+.glyphicon-calendar:before {
+ content: "\1f4c5";
+}
+
+.glyphicon-pushpin:before {
+ content: "\1f4cc";
+}
+
+.glyphicon-paperclip:before {
+ content: "\1f4ce";
+}
+
+.glyphicon-camera:before {
+ content: "\1f4f7";
+}
+
+.glyphicon-lock:before {
+ content: "\1f512";
+}
+
+.glyphicon-bell:before {
+ content: "\1f514";
+}
+
+.glyphicon-bookmark:before {
+ content: "\1f516";
+}
+
+.glyphicon-fire:before {
+ content: "\1f525";
+}
+
+.glyphicon-wrench:before {
+ content: "\1f527";
+}
+
+.btn-default .caret {
+ border-top-color: #333333;
+}
+
+.btn-primary .caret, .btn-success .caret, .btn-warning .caret, .btn-danger .caret,
+.btn-info .caret {
+ border-top-color: #fff;
+}
+
+.dropup .btn-default .caret {
+ border-bottom-color: #333333;
+}
+
+.dropup .btn-primary .caret, .dropup .btn-success .caret, .dropup .btn-warning .caret,
+.dropup .btn-danger .caret, .dropup .btn-info .caret {
+ border-bottom-color: #fff;
+}
+
+.btn-group, .btn-group-vertical {
+ position: relative;
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.btn-group>.btn, .btn-group-vertical>.btn {
+ position: relative;
+ float: left;
+}
+
+.btn-group>.btn:hover, .btn-group-vertical>.btn:hover, .btn-group>.btn:focus,
+.btn-group-vertical>.btn:focus, .btn-group>.btn:active, .btn-group-vertical>.btn:active,
+.btn-group>.btn.active, .btn-group-vertical>.btn.active {
+ z-index: 2;
+}
+
+.btn-group>.btn:focus, .btn-group-vertical>.btn:focus {
+ outline: none;
+}
+
+.btn-group .btn+.btn, .btn-group .btn+.btn-group, .btn-group .btn-group+.btn,
+.btn-group .btn-group+.btn-group {
+ margin-left: -1px;
+}
+
+.btn-toolbar:before, .btn-toolbar:after {
+ content: " ";
+ display: table;
+}
+
+.btn-toolbar:after {
+ clear: both;
+}
+
+.btn-toolbar:before, .btn-toolbar:after {
+ content: " ";
+ display: table;
+}
+
+.btn-toolbar:after {
+ clear: both;
+}
+
+.btn-toolbar .btn-group {
+ float: left;
+}
+
+.btn-toolbar>.btn+.btn, .btn-toolbar>.btn-group+.btn, .btn-toolbar>.btn+.btn-group,
+.btn-toolbar>.btn-group+.btn-group {
+ margin-left: 5px;
+}
+
+.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
+ border-radius: 0;
+}
+
+.btn-group>.btn:first-child {
+ margin-left: 0;
+}
+
+.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle) {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.btn-group>.btn:last-child:not(:first-child), .btn-group>.dropdown-toggle:not(:first-child) {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+}
+
+.btn-group>.btn-group {
+ float: left;
+}
+
+.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn {
+ border-radius: 0;
+}
+
+.btn-group>.btn-group:first-child>.btn:last-child, .btn-group>.btn-group:first-child>.dropdown-toggle {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.btn-group>.btn-group:last-child>.btn:first-child {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+}
+
+.btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle {
+ outline: 0;
+}
+
+.btn-group-xs>.btn {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+ padding: 1px 5px;
+}
+
+.btn-group-sm>.btn {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+
+.btn-group-lg>.btn {
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
+}
+
+.btn-group>.btn+.dropdown-toggle {
+ padding-left: 8px;
+ padding-right: 8px;
+}
+
+.btn-group>.btn-lg+.dropdown-toggle {
+ padding-left: 12px;
+ padding-right: 12px;
+}
+
+.btn-group.open .dropdown-toggle {
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
+
+.btn .caret {
+ margin-left: 0;
+}
+
+.btn-lg .caret {
+ border-width: 5px 5px 0;
+ border-bottom-width: 0;
+}
+
+.dropup .btn-lg .caret {
+ border-width: 0 5px 5px;
+}
+
+.btn-group-vertical>.btn, .btn-group-vertical>.btn-group {
+ display: block;
+ float: none;
+ width: 100%;
+ max-width: 100%;
+}
+
+.btn-group-vertical>.btn-group:before, .btn-group-vertical>.btn-group:after {
+ content: " ";
+ display: table;
+}
+
+.btn-group-vertical>.btn-group:after {
+ clear: both;
+}
+
+.btn-group-vertical>.btn-group:before, .btn-group-vertical>.btn-group:after {
+ content: " ";
+ display: table;
+}
+
+.btn-group-vertical>.btn-group:after {
+ clear: both;
+}
+
+.btn-group-vertical>.btn-group>.btn {
+ float: none;
+}
+
+.btn-group-vertical>.btn+.btn, .btn-group-vertical>.btn+.btn-group,
+.btn-group-vertical>.btn-group+.btn, .btn-group-vertical>.btn-group+.btn-group {
+ margin-top: -1px;
+ margin-left: 0;
+}
+
+.btn-group-vertical>.btn:not(:first-child):not(:last-child) {
+ border-radius: 0;
+}
+
+.btn-group-vertical>.btn:first-child:not(:last-child) {
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.btn-group-vertical>.btn:last-child:not(:first-child) {
+ border-bottom-left-radius: 4px;
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+
+.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn {
+ border-radius: 0;
+}
+
+.btn-group-vertical>.btn-group:first-child>.btn:last-child, .btn-group-vertical>.btn-group:first-child>.dropdown-toggle {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.btn-group-vertical>.btn-group:last-child>.btn:first-child {
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+
+.btn-group-justified {
+ display: table;
+ width: 100%;
+ table-layout: fixed;
+ border-collapse: separate;
+}
+
+.btn-group-justified .btn {
+ float: none;
+ display: table-cell;
+ width: 1%;
+}
+
+[data-toggle="buttons"]>.btn>input[type="radio"], [data-toggle="buttons"]>.btn>input[type="checkbox"] {
+ display: none;
+}
+
+.input-group {
+ position: relative;
+ display: table;
+ border-collapse: separate;
+}
+
+.input-group.col {
+ float: none;
+ padding-left: 0;
+ padding-right: 0;
+}
+
+.input-group .form-control {
+ width: 100%;
+ margin-bottom: 0;
+}
+
+.input-group-lg>.form-control, .input-group-lg>.input-group-addon,
+.input-group-lg>.input-group-btn>.btn {
+ height: 45px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
+}
+
+select.input-group-lg>.form-control, select.input-group-lg>.input-group-addon,
+select.input-group-lg>.input-group-btn>.btn {
+ height: 45px;
+ line-height: 45px;
+}
+
+textarea.input-group-lg>.form-control, textarea.input-group-lg>.input-group-addon,
+textarea.input-group-lg>.input-group-btn>.btn {
+ height: auto;
+}
+
+.input-group-sm>.form-control, .input-group-sm>.input-group-addon,
+.input-group-sm>.input-group-btn>.btn {
+ height: 30px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+
+select.input-group-sm>.form-control, select.input-group-sm>.input-group-addon,
+select.input-group-sm>.input-group-btn>.btn {
+ height: 30px;
+ line-height: 30px;
+}
+
+textarea.input-group-sm>.form-control, textarea.input-group-sm>.input-group-addon,
+textarea.input-group-sm>.input-group-btn>.btn {
+ height: auto;
+}
+
+.input-group-addon, .input-group-btn, .input-group .form-control {
+ display: table-cell;
+}
+
+.input-group-addon:not(:first-child):not(:last-child), .input-group-btn:not(:first-child):not(:last-child),
+.input-group .form-control:not(:first-child):not(:last-child) {
+ border-radius: 0;
+}
+
+.input-group-addon, .input-group-btn {
+ width: 1%;
+ white-space: nowrap;
+ vertical-align: middle;
+}
+
+.input-group-addon {
+ padding: 6px 12px;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 1;
+ text-align: center;
+ background-color: #eeeeee;
+ border: 1px solid #cccccc;
+ border-radius: 4px;
+}
+
+.input-group-addon.input-sm {
+ padding: 5px 10px;
+ font-size: 12px;
+ border-radius: 3px;
+}
+
+.input-group-addon.input-lg {
+ padding: 10px 16px;
+ font-size: 18px;
+ border-radius: 6px;
+}
+
+.input-group-addon input[type="radio"], .input-group-addon input[type="checkbox"] {
+ margin-top: 0;
+}
+
+.input-group .form-control:first-child, .input-group-addon:first-child,
+.input-group-btn:first-child>.btn, .input-group-btn:first-child>.dropdown-toggle,
+.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.input-group-addon:first-child {
+ border-right: 0;
+}
+
+.input-group .form-control:last-child, .input-group-addon:last-child,
+.input-group-btn:last-child>.btn, .input-group-btn:last-child>.dropdown-toggle,
+.input-group-btn:first-child>.btn:not(:first-child) {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+}
+
+.input-group-addon:last-child {
+ border-left: 0;
+}
+
+.input-group-btn {
+ position: relative;
+ white-space: nowrap;
+}
+
+.input-group-btn>.btn {
+ position: relative;
+}
+
+.input-group-btn>.btn+.btn {
+ margin-left: -4px;
+}
+
+.input-group-btn>.btn:hover, .input-group-btn>.btn:active {
+ z-index: 2;
+}
+
+.nav {
+ margin-bottom: 0;
+ padding-left: 0;
+ list-style: none;
+}
+
+.nav:before, .nav:after {
+ content: " ";
+ display: table;
+}
+
+.nav:after {
+ clear: both;
+}
+
+.nav:before, .nav:after {
+ content: " ";
+ display: table;
+}
+
+.nav:after {
+ clear: both;
+}
+
+.nav>li {
+ position: relative;
+ display: block;
+}
+
+.nav>li>a {
+ position: relative;
+ display: block;
+ padding: 10px 15px;
+}
+
+.nav>li>a:hover, .nav>li>a:focus {
+ text-decoration: none;
+ background-color: #eeeeee;
+}
+
+.nav>li.disabled>a {
+ color: #999999;
+}
+
+.nav>li.disabled>a:hover, .nav>li.disabled>a:focus {
+ color: #999999;
+ text-decoration: none;
+ background-color: transparent;
+ cursor: not-allowed;
+}
+
+.nav .open>a, .nav .open>a:hover, .nav .open>a:focus {
+ background-color: #eeeeee;
+ border-color: #428bca;
+}
+
+.nav .nav-divider {
+ height: 1px;
+ margin: 9px 0;
+ overflow: hidden;
+ background-color: #e5e5e5;
+}
+
+.nav>li>a>img {
+ max-width: none;
+}
+
+.nav-tabs {
+ border-bottom: 1px solid #dddddd;
+}
+
+.nav-tabs>li {
+ float: left;
+ margin-bottom: -1px;
+}
+
+.nav-tabs>li>a {
+ margin-right: 2px;
+ line-height: 1.428571429;
+ border: 1px solid transparent;
+ border-radius: 4px 4px 0 0;
+}
+
+.nav-tabs>li>a:hover {
+ border-color: #eeeeee #eeeeee #dddddd;
+}
+
+.nav-tabs>li.active>a, .nav-tabs>li.active>a:hover, .nav-tabs>li.active>a:focus {
+ color: #555555;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-bottom-color: transparent;
+ cursor: default;
+}
+
+.nav-tabs.nav-justified {
+ width: 100%;
+ border-bottom: 0;
+}
+
+.nav-tabs.nav-justified>li {
+ float: none;
+}
+
+.nav-tabs.nav-justified>li>a {
+ text-align: center;
+}
+
+ at media (min-width:980px){.nav-tabs.nav-justified>li {
+ display: table-cell;
+ width: 1%;
+ }
+}
+
+.nav-tabs.nav-justified>li>a {
+ border-bottom: 1px solid #dddddd;
+ margin-right: 0;
+}
+
+.nav-tabs.nav-justified>.active>a {
+ border-bottom-color: #ffffff;
+}
+
+.nav-pills>li {
+ float: left;
+}
+
+.nav-pills>li>a {
+ border-radius: 5px;
+}
+
+.nav-pills>li+li {
+ margin-left: 2px;
+}
+
+.nav-pills>li.active>a, .nav-pills>li.active>a:hover, .nav-pills>li.active>a:focus {
+ color: #ffffff;
+ background-color: #428bca;
+}
+
+.nav-stacked>li {
+ float: none;
+}
+
+.nav-stacked>li+li {
+ margin-top: 2px;
+ margin-left: 0;
+}
+
+.nav-justified {
+ width: 100%;
+}
+
+.nav-justified>li {
+ float: none;
+}
+
+.nav-justified>li>a {
+ text-align: center;
+}
+
+ at media (min-width:980px){.nav-justified>li {
+ display: table-cell;
+ width: 1%;
+ }
+}
+
+.nav-tabs-justified {
+ border-bottom: 0;
+}
+
+.nav-tabs-justified>li>a {
+ border-bottom: 1px solid #dddddd;
+ margin-right: 0;
+}
+
+.nav-tabs-justified>.active>a {
+ border-bottom-color: #ffffff;
+}
+
+.tabbable:before, .tabbable:after {
+ content: " ";
+ display: table;
+}
+
+.tabbable:after {
+ clear: both;
+}
+
+.tabbable:before, .tabbable:after {
+ content: " ";
+ display: table;
+}
+
+.tabbable:after {
+ clear: both;
+}
+
+.tab-content>.tab-pane, .pill-content>.pill-pane {
+ display: none;
+}
+
+.tab-content>.active, .pill-content>.active {
+ display: block;
+}
+
+.nav .caret {
+ border-top-color: #428bca;
+ border-bottom-color: #428bca;
+}
+
+.nav a:hover .caret {
+ border-top-color: #2a6496;
+ border-bottom-color: #2a6496;
+}
+
+.nav-tabs .dropdown-menu {
+ margin-top: -1px;
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+
+.navbar {
+ position: relative;
+ z-index: 1000;
+ min-height: 50px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
+}
+
+.navbar:before, .navbar:after {
+ content: " ";
+ display: table;
+}
+
+.navbar:after {
+ clear: both;
+}
+
+.navbar:before, .navbar:after {
+ content: " ";
+ display: table;
+}
+
+.navbar:after {
+ clear: both;
+}
+
+ at media (min-width:980px){.navbar {
+ border-radius: 4px;
+ }
+}
+
+.navbar-header:before, .navbar-header:after {
+ content: " ";
+ display: table;
+}
+
+.navbar-header:after {
+ clear: both;
+}
+
+.navbar-header:before, .navbar-header:after {
+ content: " ";
+ display: table;
+}
+
+.navbar-header:after {
+ clear: both;
+}
+
+ at media (min-width:980px){.navbar-header {
+ float: left;
+ }
+}
+
+.navbar-collapse {
+ max-height: 340px;
+ overflow-x: visible;
+ padding-right: 15px;
+ padding-left: 15px;
+ border-top: 1px solid transparent;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
+ -webkit-overflow-scrolling: touch;
+}
+
+.navbar-collapse:before, .navbar-collapse:after {
+ content: " ";
+ display: table;
+}
+
+.navbar-collapse:after {
+ clear: both;
+}
+
+.navbar-collapse:before, .navbar-collapse:after {
+ content: " ";
+ display: table;
+}
+
+.navbar-collapse:after {
+ clear: both;
+}
+
+.navbar-collapse.in {
+ overflow-y: auto;
+}
+
+ at media (min-width:980px){.navbar-collapse {
+ width: auto;
+ border-top: 0;
+ box-shadow: none;
+ }
+
+ .navbar-collapse.collapse {
+ display: block !important;
+ height: auto !important;
+ padding-bottom: 0;
+ overflow: visible !important;
+ }
+
+ .navbar-collapse.in {
+ overflow-y: visible;
+ }
+
+ .navbar-collapse .navbar-nav.navbar-left:first-child {
+ margin-left: -15px;
+ }
+
+ .navbar-collapse .navbar-nav.navbar-right:last-child {
+ margin-right: -15px;
+ }
+
+ .navbar-collapse .navbar-text:last-child {
+ margin-right: 0;
+ }
+}
+
+.container>.navbar-header, .container>.navbar-collapse {
+ margin-right: -15px;
+ margin-left: -15px;
+}
+
+ at media (min-width:980px){.container>.navbar-header, .container>.navbar-collapse {
+ margin-right: 0;
+ margin-left: 0;
+ }
+}
+
+.navbar-static-top {
+ border-width: 0 0 1px;
+}
+
+ at media (min-width:980px){.navbar-static-top {
+ border-radius: 0;
+ }
+}
+
+.navbar-fixed-top, .navbar-fixed-bottom {
+ position: fixed;
+ right: 0;
+ left: 0;
+ border-width: 0 0 1px;
+}
+
+ at media (min-width:980px){.navbar-fixed-top, .navbar-fixed-bottom {
+ border-radius: 0;
+ }
+}
+
+.navbar-fixed-top {
+ z-index: 1030;
+ top: 0;
+}
+
+.navbar-fixed-bottom {
+ bottom: 0;
+ margin-bottom: 0;
+}
+
+.navbar-brand {
+ float: left;
+ padding: 15px 15px;
+ font-size: 25px;
+ line-height: 20px;
+ font-weight:bold;
+}
+
+.navbar-brand:hover, .navbar-brand:focus {
+ text-decoration: none;
+}
+
+ at media (min-width:980px){.navbar>.container .navbar-brand {
+ margin-left: -15px;
+ }
+}
+
+.navbar-toggle {
+ position: relative;
+ float: right;
+ margin-right: 15px;
+ padding: 9px 10px;
+ margin-top: 8px;
+ margin-bottom: 8px;
+ background-color: transparent;
+ border: 1px solid transparent;
+ border-radius: 4px;
+}
+
+.navbar-toggle .icon-bar {
+ display: block;
+ width: 22px;
+ height: 2px;
+ border-radius: 1px;
+}
+
+.navbar-toggle .icon-bar+.icon-bar {
+ margin-top: 4px;
+}
+
+ at media (min-width:980px){.navbar-toggle {
+ display: none;
+ }
+}
+
+.navbar-nav {
+ margin: 7.5px -15px;
+}
+
+.navbar-nav>li>a {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ line-height: 20px;
+}
+
+ at media (max-width:979px){.navbar-nav .open .dropdown-menu {
+ position: static;
+ float: none;
+ width: auto;
+ margin-top: 0;
+ background-color: transparent;
+ border: 0;
+ box-shadow: none;
+ }
+
+ .navbar-nav .open .dropdown-menu>li>a, .navbar-nav .open .dropdown-menu .dropdown-header {
+ padding: 5px 15px 5px 25px;
+ }
+
+ .navbar-nav .open .dropdown-menu>li>a {
+ line-height: 20px;
+ }
+
+ .navbar-nav .open .dropdown-menu>li>a:hover, .navbar-nav .open .dropdown-menu>li>a:focus {
+ background-image: none;
+ }
+}
+
+ at media (min-width:980px){.navbar-nav {
+ float: left;
+ margin: 0;
+ }
+
+ .navbar-nav>li {
+ float: left;
+ }
+
+ .navbar-nav>li>a {
+ padding-top: 15px;
+ padding-bottom: 15px;
+ }
+}
+
+ at media (min-width:980px){.navbar-left {
+ float: left !important;
+ }
+
+ .navbar-right {
+ float: right !important;
+ }
+}
+
+.navbar-form {
+ margin-left: -15px;
+ margin-right: -15px;
+ padding: 10px 15px;
+ border-top: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+ margin-top: 8px;
+ margin-bottom: 8px;
+}
+
+ at media (min-width:980px){.navbar-form .form-group {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+
+ .navbar-form .form-control {
+ display: inline-block;
+ }
+
+ .navbar-form .radio, .navbar-form .checkbox {
+ display: inline-block;
+ margin-top: 0;
+ margin-bottom: 0;
+ padding-left: 0;
+ }
+
+ .navbar-form .radio input[type="radio"], .navbar-form .checkbox input[type="checkbox"] {
+ float: none;
+ margin-left: 0;
+ }
+}
+
+ at media (max-width:979px){.navbar-form .form-group {
+ margin-bottom: 5px;
+ }
+}
+
+ at media (min-width:980px){.navbar-form {
+ width: auto;
+ border: 0;
+ margin-left: 0;
+ margin-right: 0;
+ padding-top: 0;
+ padding-bottom: 0;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ }
+}
+
+.navbar-nav>li>.dropdown-menu {
+ margin-top: 0;
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+
+.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.navbar-nav.pull-right>li>.dropdown-menu, .navbar-nav>li>.dropdown-menu.pull-right {
+ left: auto;
+ right: 0;
+}
+
+.navbar-btn {
+ margin-top: 8px;
+ margin-bottom: 8px;
+}
+
+.navbar-text {
+ float: left;
+ margin-top: 15px;
+ margin-bottom: 15px;
+}
+
+ at media (min-width:980px){.navbar-text {
+ margin-left: 15px;
+ margin-right: 15px;
+ }
+}
+
+.navbar-default {
+ background-color: #f8f8f8;
+ border-color: #e7e7e7;
+}
+
+.navbar-default .navbar-brand {
+ color: #777777;
+}
+
+.navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus {
+ color: #5e5e5e;
+ background-color: transparent;
+}
+
+.navbar-default .navbar-text {
+ color: #777777;
+}
+
+.navbar-default .navbar-nav>li>a {
+ color: #777777;
+}
+
+.navbar-default .navbar-nav>li>a:hover, .navbar-default .navbar-nav>li>a:focus {
+ color: #333333;
+ background-color: transparent;
+}
+
+.navbar-default .navbar-nav>.active>a, .navbar-default .navbar-nav>.active>a:hover,
+.navbar-default .navbar-nav>.active>a:focus {
+ color: #555555;
+ background-color: #e7e7e7;
+}
+
+.navbar-default .navbar-nav>.disabled>a, .navbar-default .navbar-nav>.disabled>a:hover,
+.navbar-default .navbar-nav>.disabled>a:focus {
+ color: #cccccc;
+ background-color: transparent;
+}
+
+.navbar-default .navbar-toggle {
+ border-color: #dddddd;
+}
+
+.navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus {
+ background-color: #dddddd;
+}
+
+.navbar-default .navbar-toggle .icon-bar {
+ background-color: #cccccc;
+}
+
+.navbar-default .navbar-collapse, .navbar-default .navbar-form {
+ border-color: #e6e6e6;
+}
+
+.navbar-default .navbar-nav>.dropdown>a:hover .caret, .navbar-default .navbar-nav>.dropdown>a:focus .caret {
+ border-top-color: #333333;
+ border-bottom-color: #333333;
+}
+
+.navbar-default .navbar-nav>.open>a, .navbar-default .navbar-nav>.open>a:hover,
+.navbar-default .navbar-nav>.open>a:focus {
+ background-color: #e7e7e7;
+ color: #555555;
+}
+
+.navbar-default .navbar-nav>.open>a .caret, .navbar-default .navbar-nav>.open>a:hover .caret,
+.navbar-default .navbar-nav>.open>a:focus .caret {
+ border-top-color: #555555;
+ border-bottom-color: #555555;
+}
+
+.navbar-default .navbar-nav>.dropdown>a .caret {
+ border-top-color: #777777;
+ border-bottom-color: #777777;
+}
+
+ at media (max-width:979px){.navbar-default .navbar-nav .open .dropdown-menu>li>a {
+ color: #777777;
+ }
+
+ .navbar-default .navbar-nav .open .dropdown-menu>li>a:hover, .navbar-default .navbar-nav .open .dropdown-menu>li>a:focus {
+ color: #333333;
+ background-color: transparent;
+ }
+
+ .navbar-default .navbar-nav .open .dropdown-menu>.active>a, .navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus {
+ color: #555555;
+ background-color: #e7e7e7;
+ }
+
+ .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a, .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus {
+ color: #cccccc;
+ background-color: transparent;
+ }
+}
+
+.navbar-default .navbar-link {
+ color: #777777;
+}
+
+.navbar-default .navbar-link:hover {
+ color: #333333;
+}
+
+.navbar-inverse {
+ background-color: #222222;
+ border-color: #080808;
+}
+
+.navbar-inverse .navbar-brand {
+ color: #999999;
+}
+
+.navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus {
+ color: #ffffff;
+ background-color: transparent;
+}
+
+.navbar-inverse .navbar-text {
+ color: #999999;
+}
+
+.navbar-inverse .navbar-nav>li>a {
+ color: #999999;
+}
+
+.navbar-inverse .navbar-nav>li>a:hover, .navbar-inverse .navbar-nav>li>a:focus {
+ color: #ffffff;
+ background-color: transparent;
+}
+
+.navbar-inverse .navbar-nav>.active>a, .navbar-inverse .navbar-nav>.active>a:hover,
+.navbar-inverse .navbar-nav>.active>a:focus {
+ color: #ffffff;
+ background-color: #080808;
+}
+
+.navbar-inverse .navbar-nav>.disabled>a, .navbar-inverse .navbar-nav>.disabled>a:hover,
+.navbar-inverse .navbar-nav>.disabled>a:focus {
+ color: #444444;
+ background-color: transparent;
+}
+
+.navbar-inverse .navbar-toggle {
+ border-color: #333333;
+}
+
+.navbar-inverse .navbar-toggle:hover, .navbar-inverse .navbar-toggle:focus {
+ background-color: #333333;
+}
+
+.navbar-inverse .navbar-toggle .icon-bar {
+ background-color: #ffffff;
+}
+
+.navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form {
+ border-color: #101010;
+}
+
+.navbar-inverse .navbar-nav>.open>a, .navbar-inverse .navbar-nav>.open>a:hover,
+.navbar-inverse .navbar-nav>.open>a:focus {
+ background-color: #080808;
+ color: #ffffff;
+}
+
+.navbar-inverse .navbar-nav>.dropdown>a:hover .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
+.navbar-inverse .navbar-nav>.dropdown>a .caret {
+ border-top-color: #999999;
+ border-bottom-color: #999999;
+}
+
+.navbar-inverse .navbar-nav>.open>a .caret, .navbar-inverse .navbar-nav>.open>a:hover .caret,
+.navbar-inverse .navbar-nav>.open>a:focus .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
+ at media (max-width:979px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header {
+ border-color: #080808;
+ }
+
+ .navbar-inverse .navbar-nav .open .dropdown-menu>li>a {
+ color: #999999;
+ }
+
+ .navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus {
+ color: #ffffff;
+ background-color: transparent;
+ }
+
+ .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a, .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus {
+ color: #ffffff;
+ background-color: #080808;
+ }
+
+ .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a, .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus {
+ color: #444444;
+ background-color: transparent;
+ }
+}
+
+.navbar-inverse .navbar-link {
+ color: #999999;
+}
+
+.navbar-inverse .navbar-link:hover {
+ color: #ffffff;
+}
+
+.breadcrumb {
+ padding: 8px 15px;
+ margin-bottom: 20px;
+ list-style: none;
+ background-color: #f5f5f5;
+ border-radius: 4px;
+}
+
+.breadcrumb>li {
+ display: inline-block;
+}
+
+.breadcrumb>li+li:before {
+ content: "/\00a0";
+ padding: 0 5px;
+ color: #cccccc;
+}
+
+.breadcrumb>.active {
+ color: #999999;
+}
+
+.pagination {
+ display: inline-block;
+ padding-left: 0;
+ margin: 20px 0;
+ border-radius: 4px;
+}
+
+.pagination>li {
+ display: inline;
+}
+
+.pagination>li>a, .pagination>li>span {
+ position: relative;
+ float: left;
+ padding: 6px 12px;
+ line-height: 1.428571429;
+ text-decoration: none;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ margin-left: -1px;
+}
+
+.pagination>li:first-child>a, .pagination>li:first-child>span {
+ margin-left: 0;
+ border-bottom-left-radius: 4px;
+ border-top-left-radius: 4px;
+}
+
+.pagination>li:last-child>a, .pagination>li:last-child>span {
+ border-bottom-right-radius: 4px;
+ border-top-right-radius: 4px;
+}
+
+.pagination>li>a:hover, .pagination>li>span:hover, .pagination>li>a:focus,
+.pagination>li>span:focus {
+ background-color: #eeeeee;
+}
+
+.pagination>.active>a, .pagination>.active>span, .pagination>.active>a:hover,
+.pagination>.active>span:hover, .pagination>.active>a:focus, .pagination>.active>span:focus {
+ z-index: 2;
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #428bca;
+ cursor: default;
+}
+
+.pagination>.disabled>span, .pagination>.disabled>a, .pagination>.disabled>a:hover,
+.pagination>.disabled>a:focus {
+ color: #999999;
+ background-color: #ffffff;
+ border-color: #dddddd;
+ cursor: not-allowed;
+}
+
+.pagination-lg>li>a, .pagination-lg>li>span {
+ padding: 10px 16px;
+ font-size: 18px;
+}
+
+.pagination-lg>li:first-child>a, .pagination-lg>li:first-child>span {
+ border-bottom-left-radius: 6px;
+ border-top-left-radius: 6px;
+}
+
+.pagination-lg>li:last-child>a, .pagination-lg>li:last-child>span {
+ border-bottom-right-radius: 6px;
+ border-top-right-radius: 6px;
+}
+
+.pagination-sm>li>a, .pagination-sm>li>span {
+ padding: 5px 10px;
+ font-size: 12px;
+}
+
+.pagination-sm>li:first-child>a, .pagination-sm>li:first-child>span {
+ border-bottom-left-radius: 3px;
+ border-top-left-radius: 3px;
+}
+
+.pagination-sm>li:last-child>a, .pagination-sm>li:last-child>span {
+ border-bottom-right-radius: 3px;
+ border-top-right-radius: 3px;
+}
+
+.pager {
+ padding-left: 0;
+ margin: 20px 0;
+ list-style: none;
+ text-align: center;
+}
+
+.pager:before, .pager:after {
+ content: " ";
+ display: table;
+}
+
+.pager:after {
+ clear: both;
+}
+
+.pager:before, .pager:after {
+ content: " ";
+ display: table;
+}
+
+.pager:after {
+ clear: both;
+}
+
+.pager li {
+ display: inline;
+}
+
+.pager li>a, .pager li>span {
+ display: inline-block;
+ padding: 5px 14px;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-radius: 15px;
+}
+
+.pager li>a:hover, .pager li>a:focus {
+ text-decoration: none;
+ background-color: #eeeeee;
+}
+
+.pager .next>a, .pager .next>span {
+ float: right;
+}
+
+.pager .previous>a, .pager .previous>span {
+ float: left;
+}
+
+.pager .disabled>a, .pager .disabled>a:hover, .pager .disabled>a:focus,
+.pager .disabled>span {
+ color: #999999;
+ background-color: #ffffff;
+ cursor: not-allowed;
+}
+
+.label {
+ display: inline;
+ padding: .2em .6em .3em;
+ font-size: 75%;
+ font-weight: bold;
+ line-height: 1;
+ color: #ffffff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: .25em;
+}
+
+.label[href]:hover, .label[href]:focus {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.label:empty {
+ display: none;
+}
+
+.label-default {
+ background-color: #999999;
+}
+
+.label-default[href]:hover, .label-default[href]:focus {
+ background-color: #808080;
+}
+
+.label-primary {
+ background-color: #428bca;
+}
+
+.label-primary[href]:hover, .label-primary[href]:focus {
+ background-color: #3071a9;
+}
+
+.label-success {
+ background-color: #5cb85c;
+}
+
+.label-success[href]:hover, .label-success[href]:focus {
+ background-color: #449d44;
+}
+
+.label-info {
+ background-color: #5bc0de;
+}
+
+.label-info[href]:hover, .label-info[href]:focus {
+ background-color: #31b0d5;
+}
+
+.label-warning {
+ background-color: #f0ad4e;
+}
+
+.label-warning[href]:hover, .label-warning[href]:focus {
+ background-color: #ec971f;
+}
+
+.label-danger {
+ background-color: #d9534f;
+}
+
+.label-danger[href]:hover, .label-danger[href]:focus {
+ background-color: #c9302c;
+}
+
+.badge {
+ display: inline-block;
+ min-width: 10px;
+ padding: 3px 7px;
+ font-size: 12px;
+ font-weight: bold;
+ color: #ffffff;
+ line-height: 1;
+ vertical-align: baseline;
+ white-space: nowrap;
+ text-align: center;
+ background-color: #999999;
+ border-radius: 10px;
+}
+
+.badge:empty {
+ display: none;
+}
+
+a.badge:hover, a.badge:focus {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.btn .badge {
+ position: relative;
+ top: -1px;
+}
+
+a.list-group-item.active>.badge, .nav-pills>.active>a>.badge {
+ color: #428bca;
+ background-color: #ffffff;
+}
+
+.nav-pills>li>a>.badge {
+ margin-left: 3px;
+}
+
+.jumbotron {
+ padding: 30px;
+ margin-bottom: 30px;
+ font-size: 21px;
+ font-weight: 200;
+ line-height: 2.1428571435;
+ color: inherit;
+ background-color: #eeeeee;
+}
+
+.jumbotron h1 {
+ line-height: 1;
+ color: inherit;
+}
+
+.jumbotron p {
+ line-height: 1.4;
+}
+
+.container .jumbotron {
+ border-radius: 6px;
+}
+
+ at media screen and (min-width:980px) {
+ .jumbotron {
+ padding-top: 48px;
+ padding-bottom: 48px;
+ }
+
+ .container .jumbotron {
+ padding-left: 60px;
+ padding-right: 60px;
+ }
+
+ .jumbotron h1 {
+ font-size: 63px;
+ }
+}
+
+.thumbnail {
+ padding: 4px;
+ line-height: 1.428571429;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-radius: 4px;
+ -webkit-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ display: inline-block;
+ max-width: 100%;
+ height: auto;
+ display: block;
+}
+
+.thumbnail>img {
+ display: block;
+ max-width: 100%;
+ height: auto;
+}
+
+a.thumbnail:hover, a.thumbnail:focus {
+ border-color: #428bca;
+}
+
+.thumbnail>img {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.thumbnail .caption {
+ padding: 9px;
+ color: #333333;
+}
+
+.alert {
+ padding: 15px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
+ /* border-radius: 4px; */
+}
+
+.alert h4 {
+ margin-top: 0;
+ color: inherit;
+}
+
+.alert .alert-link {
+ font-weight: bold;
+}
+
+.alert>p, .alert>ul {
+ margin-bottom: 0;
+}
+
+.alert>p+p {
+ margin-top: 5px;
+}
+
+.alert-dismissable {
+ padding-right: 35px;
+}
+
+.alert-dismissable .close {
+ position: relative;
+ top: -2px;
+ right: -21px;
+ color: inherit;
+}
+
+.alert-success {
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+ color: #468847;
+}
+
+.alert-success hr {
+ border-top-color: #c9e2b3;
+}
+
+.alert-success .alert-link {
+ color: #356635;
+}
+
+.alert-info {
+ background-color: #f5f5f5;
+ border-color: #cccccc;
+ color: #000000;
+}
+
+.alert-info hr {
+ border-top-color: #a6e1ec;
+}
+
+.alert-info .alert-link {
+ color: #2d6987;
+}
+
+.alert-warning {
+ background-color: #fcf8e3;
+ border-color: #fbeed5;
+ color: #c09853;
+}
+
+.alert-warning hr {
+ border-top-color: #f8e5be;
+}
+
+.alert-warning .alert-link {
+ color: #a47e3c;
+}
+
+.alert-danger {
+ background-color: #f2dede;
+ border-color: #eed3d7;
+ color: #b94a48;
+}
+
+.alert-danger hr {
+ border-top-color: #e6c1c7;
+}
+
+.alert-danger .alert-link {
+ color: #953b39;
+}
+
+ at -webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+
+ to {
+ background-position: 0 0;
+ }
+}
+
+ at -moz-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+
+ to {
+ background-position: 0 0;
+ }
+}
+
+ at -o-keyframes progress-bar-stripes {
+ from {
+ background-position: 0 0;
+ }
+
+ to {
+ background-position: 40px 0;
+ }
+}
+
+ at keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+
+ to {
+ background-position: 0 0;
+ }
+}
+
+.progress {
+ overflow: hidden;
+ height: 20px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+
+.progress-bar {
+ float: left;
+ width: 0%;
+ height: 100%;
+ font-size: 12px;
+ color: #ffffff;
+ text-align: center;
+ background-color: #428bca;
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -webkit-transition: width 0.6s ease;
+ transition: width 0.6s ease;
+}
+
+.progress-striped .progress-bar {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-size: 40px 40px;
+}
+
+.progress.active .progress-bar {
+ -webkit-animation: progress-bar-stripes 2s linear infinite;
+ -moz-animation: progress-bar-stripes 2s linear infinite;
+ -ms-animation: progress-bar-stripes 2s linear infinite;
+ -o-animation: progress-bar-stripes 2s linear infinite;
+ animation: progress-bar-stripes 2s linear infinite;
+}
+
+.progress-bar-success {
+ background-color: #5cb85c;
+}
+
+.progress-striped .progress-bar-success {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-bar-info {
+ background-color: #5bc0de;
+}
+
+.progress-striped .progress-bar-info {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-bar-warning {
+ background-color: #f0ad4e;
+}
+
+.progress-striped .progress-bar-warning {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-bar-danger {
+ background-color: #d9534f;
+}
+
+.progress-striped .progress-bar-danger {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.media, .media-body {
+ overflow: hidden;
+ zoom: 1;
+}
+
+.media, .media .media {
+ margin-top: 15px;
+}
+
+.media:first-child {
+ margin-top: 0;
+}
+
+.media-object {
+ display: block;
+}
+
+.media-heading {
+ margin: 0 0 5px;
+}
+
+.media>.pull-left {
+ margin-right: 10px;
+}
+
+.media>.pull-right {
+ margin-left: 10px;
+}
+
+.media-list {
+ padding-left: 0;
+ list-style: none;
+}
+
+.list-group {
+ margin-bottom: 20px;
+ padding-left: 0;
+}
+
+.list-group-item {
+ position: relative;
+ display: block;
+ padding: 10px 15px;
+ margin-bottom: -1px;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+}
+
+.list-group-item:first-child {
+ border-top-right-radius: 4px;
+ border-top-left-radius: 4px;
+}
+
+.list-group-item:last-child {
+ margin-bottom: 0;
+ border-bottom-right-radius: 4px;
+ border-bottom-left-radius: 4px;
+}
+
+.list-group-item>.badge {
+ float: right;
+}
+
+.list-group-item>.badge+.badge {
+ margin-right: 5px;
+}
+
+a.list-group-item {
+ color: #555555;
+}
+
+a.list-group-item .list-group-item-heading {
+ color: #333333;
+}
+
+a.list-group-item:hover, a.list-group-item:focus {
+ text-decoration: none;
+ background-color: #f5f5f5;
+}
+
+.list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus {
+ z-index: 2;
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #428bca;
+}
+
+.list-group-item.active .list-group-item-heading, .list-group-item.active:hover .list-group-item-heading,
+.list-group-item.active:focus .list-group-item-heading {
+ color: inherit;
+}
+
+.list-group-item.active .list-group-item-text, .list-group-item.active:hover .list-group-item-text,
+.list-group-item.active:focus .list-group-item-text {
+ color: #e1edf7;
+}
+
+.list-group-item-heading {
+ margin-top: 0;
+ margin-bottom: 5px;
+}
+
+.list-group-item-text {
+ margin-bottom: 0;
+ line-height: 1.3;
+}
+
+.panel {
+ margin-bottom: 20px;
+ background-color: #ffffff;
+ border: 1px solid transparent;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
+}
+
+.panel-body {
+ padding: 15px;
+}
+
+.panel-body:before, .panel-body:after {
+ content: " ";
+ display: table;
+}
+
+.panel-body:after {
+ clear: both;
+}
+
+.panel-body:before, .panel-body:after {
+ content: " ";
+ display: table;
+}
+
+.panel-body:after {
+ clear: both;
+}
+
+.panel>.list-group {
+ margin-bottom: 0;
+}
+
+.panel>.list-group .list-group-item {
+ border-width: 1px 0;
+}
+
+.panel>.list-group .list-group-item:first-child {
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+
+.panel>.list-group .list-group-item:last-child {
+ border-bottom: 0;
+}
+
+.panel-heading+.list-group .list-group-item:first-child {
+ border-top-width: 0;
+}
+
+.panel>.table {
+ margin-bottom: 0;
+}
+
+.panel>.panel-body+.table {
+ border-top: 1px solid #dddddd;
+}
+
+.panel-heading {
+ padding: 10px 15px;
+ border-bottom: 1px solid transparent;
+ border-top-right-radius: 3px;
+ border-top-left-radius: 3px;
+}
+
+.panel-title {
+ margin-top: 0;
+ margin-bottom: 0;
+ font-size: 16px;
+}
+
+.panel-title>a {
+ color: inherit;
+}
+
+.panel-footer {
+ padding: 10px 15px;
+ background-color: #f5f5f5;
+ border-top: 1px solid #dddddd;
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
+}
+
+.panel-group .panel {
+ margin-bottom: 0;
+ border-radius: 4px;
+ overflow: hidden;
+}
+
+.panel-group .panel+.panel {
+ margin-top: 5px;
+}
+
+.panel-group .panel-heading {
+ border-bottom: 0;
+}
+
+.panel-group .panel-heading+.panel-collapse .panel-body {
+ border-top: 1px solid #dddddd;
+}
+
+.panel-group .panel-footer {
+ border-top: 0;
+}
+
+.panel-group .panel-footer+.panel-collapse .panel-body {
+ border-bottom: 1px solid #dddddd;
+}
+
+.panel-default {
+ border-color: #dddddd;
+}
+
+.panel-default>.panel-heading {
+ color: #333333;
+ background-color: #f5f5f5;
+ border-color: #dddddd;
+}
+
+.panel-default>.panel-heading+.panel-collapse .panel-body {
+ border-top-color: #dddddd;
+}
+
+.panel-default>.panel-footer+.panel-collapse .panel-body {
+ border-bottom-color: #dddddd;
+}
+
+.panel-primary {
+ border-color: #428bca;
+}
+
+.panel-primary>.panel-heading {
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #428bca;
+}
+
+.panel-primary>.panel-heading+.panel-collapse .panel-body {
+ border-top-color: #428bca;
+}
+
+.panel-primary>.panel-footer+.panel-collapse .panel-body {
+ border-bottom-color: #428bca;
+}
+
+.panel-success {
+ border-color: #d6e9c6;
+}
+
+.panel-success>.panel-heading {
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+}
+
+.panel-success>.panel-heading+.panel-collapse .panel-body {
+ border-top-color: #d6e9c6;
+}
+
+.panel-success>.panel-footer+.panel-collapse .panel-body {
+ border-bottom-color: #d6e9c6;
+}
+
+.panel-warning {
+ border-color: #fbeed5;
+}
+
+.panel-warning>.panel-heading {
+ color: #c09853;
+ background-color: #fcf8e3;
+ border-color: #fbeed5;
+}
+
+.panel-warning>.panel-heading+.panel-collapse .panel-body {
+ border-top-color: #fbeed5;
+}
+
+.panel-warning>.panel-footer+.panel-collapse .panel-body {
+ border-bottom-color: #fbeed5;
+}
+
+.panel-danger {
+ border-color: #eed3d7;
+}
+
+.panel-danger>.panel-heading {
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #eed3d7;
+}
+
+.panel-danger>.panel-heading+.panel-collapse .panel-body {
+ border-top-color: #eed3d7;
+}
+
+.panel-danger>.panel-footer+.panel-collapse .panel-body {
+ border-bottom-color: #eed3d7;
+}
+
+.panel-info {
+ border-color: #bce8f1;
+}
+
+.panel-info>.panel-heading {
+ color: #3a87ad;
+ background-color: #d9edf7;
+ border-color: #bce8f1;
+}
+
+.panel-info>.panel-heading+.panel-collapse .panel-body {
+ border-top-color: #bce8f1;
+}
+
+.panel-info>.panel-footer+.panel-collapse .panel-body {
+ border-bottom-color: #bce8f1;
+}
+
+.well {
+ min-height: 20px;
+ padding: 19px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border: 1px solid #e3e3e3;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+}
+
+.well blockquote {
+ border-color: #ddd;
+ border-color: rgba(0, 0, 0, 0.15);
+}
+
+.well-lg {
+ padding: 24px;
+ border-radius: 6px;
+}
+
+.well-sm {
+ padding: 9px;
+ border-radius: 3px;
+}
+
+.close {
+ float: right;
+ font-size: 21px;
+ font-weight: bold;
+ line-height: 1;
+ color: #000000;
+ text-shadow: 0 1px 0 #ffffff;
+ opacity: 0.2;
+ filter: alpha(opacity=20);
+}
+
+.close:hover, .close:focus {
+ color: #000000;
+ text-decoration: none;
+ cursor: pointer;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+}
+
+button.close {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+}
+
+.caret {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ margin-left: 2px;
+ vertical-align: middle;
+ border-top: 4px solid #000000;
+ border-right: 4px solid transparent;
+ border-left: 4px solid transparent;
+ border-bottom: 0 dotted;
+ content: "";
+}
+
+.dropdown {
+ position: relative;
+}
+
+.dropdown-toggle:focus {
+ outline: 0;
+}
+
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ list-style: none;
+ font-size: 14px;
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 4px;
+ -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ background-clip: padding-box;
+}
+
+.dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
+}
+
+.dropdown-menu .divider {
+ height: 1px;
+ margin: 9px 0;
+ overflow: hidden;
+ background-color: #e5e5e5;
+}
+
+.dropdown-menu>li>a {
+ display: block;
+ padding: 3px 20px;
+ clear: both;
+ font-weight: normal;
+ line-height: 1.428571429;
+ color: #333333;
+ white-space: nowrap;
+}
+
+.dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus {
+ text-decoration: none;
+ color: #ffffff;
+ background-color: #428bca;
+}
+
+.dropdown-menu>.active>a, .dropdown-menu>.active>a:hover, .dropdown-menu>.active>a:focus {
+ color: #ffffff;
+ text-decoration: none;
+ outline: 0;
+ background-color: #428bca;
+}
+
+.dropdown-menu>.disabled>a, .dropdown-menu>.disabled>a:hover,
+.dropdown-menu>.disabled>a:focus {
+ color: #999999;
+}
+
+.dropdown-menu>.disabled>a:hover, .dropdown-menu>.disabled>a:focus {
+ text-decoration: none;
+ background-color: transparent;
+ background-image: none;
+ filter: progid: DXImageTransform.Microsoft.gradient(enabled = false);
+ cursor: not-allowed;
+}
+
+.open>.dropdown-menu {
+ display: block;
+}
+
+.open>a {
+ outline: 0;
+}
+
+.dropdown-header {
+ display: block;
+ padding: 3px 20px;
+ font-size: 12px;
+ line-height: 1.428571429;
+ color: #999999;
+}
+
+.dropdown-backdrop {
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ top: 0;
+ z-index: 990;
+}
+
+.pull-right>.dropdown-menu {
+ right: 0;
+ left: auto;
+}
+
+.dropup .caret, .navbar-fixed-bottom .dropdown .caret {
+ border-top: 0 dotted;
+ border-bottom: 4px solid #000000;
+ content: "";
+}
+
+.dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu {
+ top: auto;
+ bottom: 100%;
+ margin-bottom: 1px;
+}
+
+ at media (min-width:980px){.navbar-right .dropdown-menu {
+ right: 0;
+ left: auto;
+ }
+}
+
+.tooltip {
+ position: absolute;
+ z-index: 1030;
+ display: block;
+ visibility: visible;
+ font-size: 12px;
+ line-height: 1.4;
+ opacity: 0;
+ filter: alpha(opacity=0);
+}
+
+.tooltip.in {
+ opacity: 0.9;
+ filter: alpha(opacity=90);
+}
+
+.tooltip.top {
+ margin-top: -3px;
+ padding: 5px 0;
+}
+
+.tooltip.right {
+ margin-left: 3px;
+ padding: 0 5px;
+}
+
+.tooltip.bottom {
+ margin-top: 3px;
+ padding: 5px 0;
+}
+
+.tooltip.left {
+ margin-left: -3px;
+ padding: 0 5px;
+}
+
+.tooltip-inner {
+ max-width: 200px;
+ padding: 3px 8px;
+ color: #ffffff;
+ text-align: center;
+ text-decoration: none;
+ background-color: #000000;
+ border-radius: 4px;
+}
+
+.tooltip-arrow {
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+
+.tooltip.top .tooltip-arrow {
+ bottom: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000000;
+}
+
+.tooltip.top-left .tooltip-arrow {
+ bottom: 0;
+ left: 5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000000;
+}
+
+.tooltip.top-right .tooltip-arrow {
+ bottom: 0;
+ right: 5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000000;
+}
+
+.tooltip.right .tooltip-arrow {
+ top: 50%;
+ left: 0;
+ margin-top: -5px;
+ border-width: 5px 5px 5px 0;
+ border-right-color: #000000;
+}
+
+.tooltip.left .tooltip-arrow {
+ top: 50%;
+ right: 0;
+ margin-top: -5px;
+ border-width: 5px 0 5px 5px;
+ border-left-color: #000000;
+}
+
+.tooltip.bottom .tooltip-arrow {
+ top: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000000;
+}
+
+.tooltip.bottom-left .tooltip-arrow {
+ top: 0;
+ left: 5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000000;
+}
+
+.tooltip.bottom-right .tooltip-arrow {
+ top: 0;
+ right: 5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000000;
+}
+
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1010;
+ display: none;
+ max-width: 276px;
+ padding: 1px;
+ text-align: left;
+ background-color: #ffffff;
+ background-clip: padding-box;
+ border: 1px solid #cccccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ white-space: normal;
+}
+
+.popover.top {
+ margin-top: -10px;
+}
+
+.popover.right {
+ margin-left: 10px;
+}
+
+.popover.bottom {
+ margin-top: 10px;
+}
+
+.popover.left {
+ margin-left: -10px;
+}
+
+.popover-title {
+ margin: 0;
+ padding: 8px 14px;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 18px;
+ background-color: #f7f7f7;
+ border-bottom: 1px solid #ebebeb;
+ border-radius: 5px 5px 0 0;
+}
+
+.popover-content {
+ padding: 9px 14px;
+}
+
+.popover .arrow, .popover .arrow:after {
+ position: absolute;
+ display: block;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+
+.popover .arrow {
+ border-width: 11px;
+}
+
+.popover .arrow:after {
+ border-width: 10px;
+ content: "";
+}
+
+.popover.top .arrow {
+ left: 50%;
+ margin-left: -11px;
+ border-bottom-width: 0;
+ border-top-color: #999999;
+ border-top-color: rgba(0, 0, 0, 0.25);
+ bottom: -11px;
+}
+
+.popover.top .arrow:after {
+ content: " ";
+ bottom: 1px;
+ margin-left: -10px;
+ border-bottom-width: 0;
+ border-top-color: #ffffff;
+}
+
+.popover.right .arrow {
+ top: 50%;
+ left: -11px;
+ margin-top: -11px;
+ border-left-width: 0;
+ border-right-color: #999999;
+ border-right-color: rgba(0, 0, 0, 0.25);
+}
+
+.popover.right .arrow:after {
+ content: " ";
+ left: 1px;
+ bottom: -10px;
+ border-left-width: 0;
+ border-right-color: #ffffff;
+}
+
+.popover.bottom .arrow {
+ left: 50%;
+ margin-left: -11px;
+ border-top-width: 0;
+ border-bottom-color: #999999;
+ border-bottom-color: rgba(0, 0, 0, 0.25);
+ top: -11px;
+}
+
+.popover.bottom .arrow:after {
+ content: " ";
+ top: 1px;
+ margin-left: -10px;
+ border-top-width: 0;
+ border-bottom-color: #ffffff;
+}
+
+.popover.left .arrow {
+ top: 50%;
+ right: -11px;
+ margin-top: -11px;
+ border-right-width: 0;
+ border-left-color: #999999;
+ border-left-color: rgba(0, 0, 0, 0.25);
+}
+
+.popover.left .arrow:after {
+ content: " ";
+ right: 1px;
+ border-right-width: 0;
+ border-left-color: #ffffff;
+ bottom: -10px;
+}
+
+.modal-open {
+ overflow: hidden;
+}
+
+body.modal-open, .modal-open .navbar-fixed-top, .modal-open .navbar-fixed-bottom {
+ margin-right: 15px;
+}
+
+.modal {
+ display: none;
+ overflow: auto;
+ overflow-y: scroll;
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1040;
+}
+
+.modal.fade .modal-dialog {
+ -webkit-transform: translate(0, -25%);
+ -ms-transform: translate(0, -25%);
+ transform: translate(0, -25%);
+ -webkit-transition: -webkit-transform 0.3s ease-out;
+ -moz-transition: -moz-transform 0.3s ease-out;
+ -o-transition: -o-transform 0.3s ease-out;
+ transition: transform 0.3s ease-out;
+}
+
+.modal.in .modal-dialog {
+ -webkit-transform: translate(0, 0);
+ -ms-transform: translate(0, 0);
+ transform: translate(0, 0);
+}
+
+.modal-dialog {
+ margin-left: auto;
+ margin-right: auto;
+ width: auto;
+ padding: 10px;
+ z-index: 1050;
+}
+
+.modal-content {
+ position: relative;
+ background-color: #ffffff;
+ border: 1px solid #999999;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 6px;
+ -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
+ box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
+ background-clip: padding-box;
+ outline: none;
+}
+
+.modal-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1030;
+ background-color: #000000;
+}
+
+.modal-backdrop.fade {
+ opacity: 0;
+ filter: alpha(opacity=0);
+}
+
+.modal-backdrop.in {
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+}
+
+.modal-header {
+ padding: 15px;
+ border-bottom: 1px solid #e5e5e5;
+ min-height: 16.428571429px;
+}
+
+.modal-header .close {
+ margin-top: -2px;
+}
+
+.modal-title {
+ margin: 0;
+ line-height: 1.428571429;
+}
+
+.modal-body {
+ position: relative;
+ padding: 20px;
+}
+
+.modal-footer {
+ margin-top: 15px;
+ padding: 19px 20px 20px;
+ text-align: right;
+ border-top: 1px solid #e5e5e5;
+}
+
+.modal-footer:before, .modal-footer:after {
+ content: " ";
+ display: table;
+}
+
+.modal-footer:after {
+ clear: both;
+}
+
+.modal-footer:before, .modal-footer:after {
+ content: " ";
+ display: table;
+}
+
+.modal-footer:after {
+ clear: both;
+}
+
+.modal-footer .btn+.btn {
+ margin-left: 5px;
+ margin-bottom: 0;
+}
+
+.modal-footer .btn-group .btn+.btn {
+ margin-left: -1px;
+}
+
+.modal-footer .btn-block+.btn-block {
+ margin-left: 0;
+}
+
+ at media screen and (min-width:980px) {
+ .modal-dialog {
+ left: 50%;
+ right: auto;
+ width: 600px;
+ padding-top: 30px;
+ padding-bottom: 30px;
+ }
+
+ .modal-content {
+ -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
+ }
+}
+
+.carousel {
+ position: relative;
+}
+
+.carousel-inner {
+ position: relative;
+ overflow: hidden;
+ width: 100%;
+}
+
+.carousel-inner>.item {
+ display: none;
+ position: relative;
+ -webkit-transition: 0.6s ease-in-out left;
+ transition: 0.6s ease-in-out left;
+}
+
+.carousel-inner>.item>img, .carousel-inner>.item>a>img {
+ display: block;
+ max-width: 100%;
+ height: auto;
+ line-height: 1;
+}
+
+.carousel-inner>.active, .carousel-inner>.next, .carousel-inner>.prev {
+ display: block;
+}
+
+.carousel-inner>.active {
+ left: 0;
+}
+
+.carousel-inner>.next, .carousel-inner>.prev {
+ position: absolute;
+ top: 0;
+ width: 100%;
+}
+
+.carousel-inner>.next {
+ left: 100%;
+}
+
+.carousel-inner>.prev {
+ left: -100%;
+}
+
+.carousel-inner>.next.left, .carousel-inner>.prev.right {
+ left: 0;
+}
+
+.carousel-inner>.active.left {
+ left: -100%;
+}
+
+.carousel-inner>.active.right {
+ left: 100%;
+}
+
+.carousel-control {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 15%;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+ font-size: 20px;
+ color: #ffffff;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
+}
+
+.carousel-control.left {
+ background-image: -webkit-gradient(linear, 0% top, 100% top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001)));
+ background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0%), color-stop(rgba(0, 0, 0, 0.0001) 100%));
+ background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);
+ background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);
+}
+
+.carousel-control.right {
+ left: auto;
+ right: 0;
+ background-image: -webkit-gradient(linear, 0% top, 100% top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5)));
+ background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0%), color-stop(rgba(0, 0, 0, 0.5) 100%));
+ background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);
+ background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);
+ background-repeat: repeat-x;
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);
+}
+
+.carousel-control:hover, .carousel-control:focus {
+ color: #ffffff;
+ text-decoration: none;
+ opacity: 0.9;
+ filter: alpha(opacity=90);
+}
+
+.carousel-control .icon-prev, .carousel-control .icon-next, .carousel-control .glyphicon-chevron-left,
+.carousel-control .glyphicon-chevron-right {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ z-index: 5;
+ display: inline-block;
+}
+
+.carousel-control .icon-prev, .carousel-control .icon-next {
+ width: 20px;
+ height: 20px;
+ margin-top: -10px;
+ margin-left: -10px;
+ font-family: serif;
+}
+
+.carousel-control .icon-prev:before {
+ content: '\2039';
+}
+
+.carousel-control .icon-next:before {
+ content: '\203a';
+}
+
+.carousel-indicators {
+ position: absolute;
+ bottom: 10px;
+ left: 50%;
+ z-index: 15;
+ width: 60%;
+ margin-left: -30%;
+ padding-left: 0;
+ list-style: none;
+ text-align: center;
+}
+
+.carousel-indicators li {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ margin: 1px;
+ text-indent: -999px;
+ border: 1px solid #ffffff;
+ border-radius: 10px;
+ cursor: pointer;
+}
+
+.carousel-indicators .active {
+ margin: 0;
+ width: 12px;
+ height: 12px;
+ background-color: #ffffff;
+}
+
+.carousel-caption {
+ position: absolute;
+ left: 15%;
+ right: 15%;
+ bottom: 20px;
+ z-index: 10;
+ padding-top: 20px;
+ padding-bottom: 20px;
+ color: #ffffff;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
+}
+
+.carousel-caption .btn {
+ text-shadow: none;
+}
+
+ at media screen and (min-width:980px) {
+ .carousel-control .icon-prev, .carousel-control .icon-next {
+ width: 30px;
+ height: 30px;
+ margin-top: -15px;
+ margin-left: -15px;
+ font-size: 30px;
+ }
+
+ .carousel-caption {
+ left: 20%;
+ right: 20%;
+ padding-bottom: 30px;
+ }
+
+ .carousel-indicators {
+ bottom: 20px;
+ }
+}
+
+.clearfix:before, .clearfix:after {
+ content: " ";
+ display: table;
+}
+
+.clearfix:after {
+ clear: both;
+}
+
+.pull-right {
+ float: right !important;
+}
+
+.pull-left {
+ float: left !important;
+}
+
+.hide {
+ display: none !important;
+}
+
+.show {
+ display: block !important;
+}
+
+.invisible {
+ visibility: hidden;
+}
+
+.text-hide {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+
+.affix {
+ position: fixed;
+}
+
+ at -ms-viewport {
+ width: device-width;
+}
+
+ at media screen and (max-width:400px) {
+ @-ms-viewport {
+ width: 320px;
+ }
+}
+
+.hidden {
+ display: none !important;
+ visibility: hidden !important;
+}
+
+.visible-xs {
+ display: none !important;
+}
+
+tr.visible-xs {
+ display: none !important;
+}
+
+th.visible-xs, td.visible-xs {
+ display: none !important;
+}
+
+ at media (max-width:979px){.visible-xs {
+ display: block !important;
+ }
+
+ tr.visible-xs {
+ display: table-row !important;
+ }
+
+ th.visible-xs, td.visible-xs {
+ display: table-cell !important;
+ }
+}
+
+ at media (min-width:980px) and (max-width:991px){.visible-xs.visible-sm {
+ display: block !important;
+ }
+
+ tr.visible-xs.visible-sm {
+ display: table-row !important;
+ }
+
+ th.visible-xs.visible-sm, td.visible-xs.visible-sm {
+ display: table-cell !important;
+ }
+}
+
+ at media (min-width:992px) and (max-width:1199px){.visible-xs.visible-md {
+ display: block !important;
+ }
+
+ tr.visible-xs.visible-md {
+ display: table-row !important;
+ }
+
+ th.visible-xs.visible-md, td.visible-xs.visible-md {
+ display: table-cell !important;
+ }
+}
+
+ at media (min-width:1200px){.visible-xs.visible-lg {
+ display: block !important;
+ }
+
+ tr.visible-xs.visible-lg {
+ display: table-row !important;
+ }
+
+ th.visible-xs.visible-lg, td.visible-xs.visible-lg {
+ display: table-cell !important;
+ }
+}
+
+.visible-sm {
+ display: none !important;
+}
+
+tr.visible-sm {
+ display: none !important;
+}
+
+th.visible-sm, td.visible-sm {
+ display: none !important;
+}
+
+ at media (max-width:979px){.visible-sm.visible-xs {
+ display: block !important;
+ }
+
+ tr.visible-sm.visible-xs {
+ display: table-row !important;
+ }
+
+ th.visible-sm.visible-xs, td.visible-sm.visible-xs {
+ display: table-cell !important;
+ }
+}
+
+ at media (min-width:980px) and (max-width:991px){.visible-sm {
+ display: block !important;
+ }
+
+ tr.visible-sm {
+ display: table-row !important;
+ }
+
+ th.visible-sm, td.visible-sm {
+ display: table-cell !important;
+ }
+}
+
+ at media (min-width:992px) and (max-width:1199px){.visible-sm.visible-md {
+ display: block !important;
+ }
+
+ tr.visible-sm.visible-md {
+ display: table-row !important;
+ }
+
+ th.visible-sm.visible-md, td.visible-sm.visible-md {
+ display: table-cell !important;
+ }
+}
+
+ at media (min-width:1200px){.visible-sm.visible-lg {
+ display: block !important;
+ }
+
+ tr.visible-sm.visible-lg {
+ display: table-row !important;
+ }
+
+ th.visible-sm.visible-lg, td.visible-sm.visible-lg {
+ display: table-cell !important;
+ }
+}
+
+.visible-md {
+ display: none !important;
+}
+
+tr.visible-md {
+ display: none !important;
+}
+
+th.visible-md, td.visible-md {
+ display: none !important;
+}
+
+ at media (max-width:979px){.visible-md.visible-xs {
+ display: block !important;
+ }
+
+ tr.visible-md.visible-xs {
+ display: table-row !important;
+ }
+
+ th.visible-md.visible-xs, td.visible-md.visible-xs {
+ display: table-cell !important;
+ }
+}
+
+ at media (min-width:980px) and (max-width:991px){.visible-md.visible-sm {
+ display: block !important;
+ }
+
+ tr.visible-md.visible-sm {
+ display: table-row !important;
+ }
+
+ th.visible-md.visible-sm, td.visible-md.visible-sm {
+ display: table-cell !important;
+ }
+}
+
+ at media (min-width:992px) and (max-width:1199px){.visible-md {
+ display: block !important;
+ }
+
+ tr.visible-md {
+ display: table-row !important;
+ }
+
+ th.visible-md, td.visible-md {
+ display: table-cell !important;
+ }
+}
+
+ at media (min-width:1200px){.visible-md.visible-lg {
+ display: block !important;
+ }
+
+ tr.visible-md.visible-lg {
+ display: table-row !important;
+ }
+
+ th.visible-md.visible-lg, td.visible-md.visible-lg {
+ display: table-cell !important;
+ }
+}
+
+.visible-lg {
+ display: none !important;
+}
+
+tr.visible-lg {
+ display: none !important;
+}
+
+th.visible-lg, td.visible-lg {
+ display: none !important;
+}
+
+ at media (max-width:979px){.visible-lg.visible-xs {
+ display: block !important;
+ }
+
+ tr.visible-lg.visible-xs {
+ display: table-row !important;
+ }
+
+ th.visible-lg.visible-xs, td.visible-lg.visible-xs {
+ display: table-cell !important;
+ }
+}
+
+ at media (min-width:980px) and (max-width:991px){.visible-lg.visible-sm {
+ display: block !important;
+ }
+
+ tr.visible-lg.visible-sm {
+ display: table-row !important;
+ }
+
+ th.visible-lg.visible-sm, td.visible-lg.visible-sm {
+ display: table-cell !important;
+ }
+}
+
+ at media (min-width:992px) and (max-width:1199px){.visible-lg.visible-md {
+ display: block !important;
+ }
+
+ tr.visible-lg.visible-md {
+ display: table-row !important;
+ }
+
+ th.visible-lg.visible-md, td.visible-lg.visible-md {
+ display: table-cell !important;
+ }
+}
+
+ at media (min-width:1200px){.visible-lg {
+ display: block !important;
+ }
+
+ tr.visible-lg {
+ display: table-row !important;
+ }
+
+ th.visible-lg, td.visible-lg {
+ display: table-cell !important;
+ }
+}
+
+.hidden-xs {
+ display: block !important;
+}
+
+tr.hidden-xs {
+ display: table-row !important;
+}
+
+th.hidden-xs, td.hidden-xs {
+ display: table-cell !important;
+}
+
+ at media (max-width:979px){.hidden-xs {
+ display: none !important;
+ }
+
+ tr.hidden-xs {
+ display: none !important;
+ }
+
+ th.hidden-xs, td.hidden-xs {
+ display: none !important;
+ }
+}
+
+ at media (min-width:980px) and (max-width:991px){.hidden-xs.hidden-sm {
+ display: none !important;
+ }
+
+ tr.hidden-xs.hidden-sm {
+ display: none !important;
+ }
+
+ th.hidden-xs.hidden-sm, td.hidden-xs.hidden-sm {
+ display: none !important;
+ }
+}
+
+ at media (min-width:992px) and (max-width:1199px){.hidden-xs.hidden-md {
+ display: none !important;
+ }
+
+ tr.hidden-xs.hidden-md {
+ display: none !important;
+ }
+
+ th.hidden-xs.hidden-md, td.hidden-xs.hidden-md {
+ display: none !important;
+ }
+}
+
+ at media (min-width:1200px){.hidden-xs.hidden-lg {
+ display: none !important;
+ }
+
+ tr.hidden-xs.hidden-lg {
+ display: none !important;
+ }
+
+ th.hidden-xs.hidden-lg, td.hidden-xs.hidden-lg {
+ display: none !important;
+ }
+}
+
+.hidden-sm {
+ display: block !important;
+}
+
+tr.hidden-sm {
+ display: table-row !important;
+}
+
+th.hidden-sm, td.hidden-sm {
+ display: table-cell !important;
+}
+
+ at media (max-width:979px){.hidden-sm.hidden-xs {
+ display: none !important;
+ }
+
+ tr.hidden-sm.hidden-xs {
+ display: none !important;
+ }
+
+ th.hidden-sm.hidden-xs, td.hidden-sm.hidden-xs {
+ display: none !important;
+ }
+}
+
+ at media (min-width:980px) and (max-width:991px){.hidden-sm {
+ display: none !important;
+ }
+
+ tr.hidden-sm {
+ display: none !important;
+ }
+
+ th.hidden-sm, td.hidden-sm {
+ display: none !important;
+ }
+}
+
+ at media (min-width:992px) and (max-width:1199px){.hidden-sm.hidden-md {
+ display: none !important;
+ }
+
+ tr.hidden-sm.hidden-md {
+ display: none !important;
+ }
+
+ th.hidden-sm.hidden-md, td.hidden-sm.hidden-md {
+ display: none !important;
+ }
+}
+
+ at media (min-width:1200px){.hidden-sm.hidden-lg {
+ display: none !important;
+ }
+
+ tr.hidden-sm.hidden-lg {
+ display: none !important;
+ }
+
+ th.hidden-sm.hidden-lg, td.hidden-sm.hidden-lg {
+ display: none !important;
+ }
+}
+
+.hidden-md {
+ display: block !important;
+}
+
+tr.hidden-md {
+ display: table-row !important;
+}
+
+th.hidden-md, td.hidden-md {
+ display: table-cell !important;
+}
+
+ at media (max-width:979px){.hidden-md.hidden-xs {
+ display: none !important;
+ }
+
+ tr.hidden-md.hidden-xs {
+ display: none !important;
+ }
+
+ th.hidden-md.hidden-xs, td.hidden-md.hidden-xs {
+ display: none !important;
+ }
+}
+
+ at media (min-width:980px) and (max-width:991px){.hidden-md.hidden-sm {
+ display: none !important;
+ }
+
+ tr.hidden-md.hidden-sm {
+ display: none !important;
+ }
+
+ th.hidden-md.hidden-sm, td.hidden-md.hidden-sm {
+ display: none !important;
+ }
+}
+
+ at media (min-width:992px) and (max-width:1199px){.hidden-md {
+ display: none !important;
+ }
+
+ tr.hidden-md {
+ display: none !important;
+ }
+
+ th.hidden-md, td.hidden-md {
+ display: none !important;
+ }
+}
+
+ at media (min-width:1200px){.hidden-md.hidden-lg {
+ display: none !important;
+ }
+
+ tr.hidden-md.hidden-lg {
+ display: none !important;
+ }
+
+ th.hidden-md.hidden-lg, td.hidden-md.hidden-lg {
+ display: none !important;
+ }
+}
+
+.hidden-lg {
+ display: block !important;
+}
+
+tr.hidden-lg {
+ display: table-row !important;
+}
+
+th.hidden-lg, td.hidden-lg {
+ display: table-cell !important;
+}
+
+ at media (max-width:979px){.hidden-lg.hidden-xs {
+ display: none !important;
+ }
+
+ tr.hidden-lg.hidden-xs {
+ display: none !important;
+ }
+
+ th.hidden-lg.hidden-xs, td.hidden-lg.hidden-xs {
+ display: none !important;
+ }
+}
+
+ at media (min-width:980px) and (max-width:991px){.hidden-lg.hidden-sm {
+ display: none !important;
+ }
+
+ tr.hidden-lg.hidden-sm {
+ display: none !important;
+ }
+
+ th.hidden-lg.hidden-sm, td.hidden-lg.hidden-sm {
+ display: none !important;
+ }
+}
+
+ at media (min-width:992px) and (max-width:1199px){.hidden-lg.hidden-md {
+ display: none !important;
+ }
+
+ tr.hidden-lg.hidden-md {
+ display: none !important;
+ }
+
+ th.hidden-lg.hidden-md, td.hidden-lg.hidden-md {
+ display: none !important;
+ }
+}
+
+ at media (min-width:1200px){.hidden-lg {
+ display: none !important;
+ }
+
+ tr.hidden-lg {
+ display: none !important;
+ }
+
+ th.hidden-lg, td.hidden-lg {
+ display: none !important;
+ }
+}
+
+.visible-print {
+ display: none !important;
+}
+
+tr.visible-print {
+ display: none !important;
+}
+
+th.visible-print, td.visible-print {
+ display: none !important;
+}
+
+ at media print {
+ .visible-print {
+ display: block !important;
+ }
+
+ tr.visible-print {
+ display: table-row !important;
+ }
+
+ th.visible-print, td.visible-print {
+ display: table-cell !important;
+ }
+
+ .hidden-print {
+ display: none !important;
+ }
+
+ tr.hidden-print {
+ display: none !important;
+ }
+
+ th.hidden-print, td.hidden-print {
+ display: none !important;
+ }
+}
+
+.fade {
+ opacity: 0;
+ -webkit-transition: opacity 0.15s linear;
+ transition: opacity 0.15s linear;
+}
+
+.fade.in {
+ opacity: 1;
+}
+
+.collapse {
+ display: none;
+}
+
+.collapse.in {
+ display: block;
+}
+
+.collapsing {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ -webkit-transition: height 0.35s ease;
+ transition: height 0.35s ease;
+}
diff --git a/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/fonts/glyphicons-halflings-regular.eot b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/fonts/glyphicons-halflings-regular.eot
new file mode 100644
index 0000000..87eaa43
Binary files /dev/null and b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/fonts/glyphicons-halflings-regular.eot differ
diff --git a/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/fonts/glyphicons-halflings-regular.svg b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/fonts/glyphicons-halflings-regular.svg
new file mode 100644
index 0000000..5fee068
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/fonts/glyphicons-halflings-regular.svg
@@ -0,0 +1,228 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
+<font-face units-per-em="1200" ascent="960" descent="-240" />
+<missing-glyph horiz-adv-x="500" />
+<glyph />
+<glyph />
+<glyph unicode=" " />
+<glyph unicode="*" d="M1100 500h-259l183 -183l-141 -141l-183 183v-259h-200v259l-183 -183l-141 141l183 183h-259v200h259l-183 183l141 141l183 -183v259h200v-259l183 183l141 -141l-183 -183h259v-200z" />
+<glyph unicode="+" d="M1100 400h-400v-400h-300v400h-400v300h400v400h300v-400h400v-300z" />
+<glyph unicode=" " />
+<glyph unicode=" " horiz-adv-x="652" />
+<glyph unicode=" " horiz-adv-x="1304" />
+<glyph unicode=" " horiz-adv-x="652" />
+<glyph unicode=" " horiz-adv-x="1304" />
+<glyph unicode=" " horiz-adv-x="434" />
+<glyph unicode=" " horiz-adv-x="326" />
+<glyph unicode=" " horiz-adv-x="217" />
+<glyph unicode=" " horiz-adv-x="217" />
+<glyph unicode=" " horiz-adv-x="163" />
+<glyph unicode=" " horiz-adv-x="260" />
+<glyph unicode=" " horiz-adv-x="72" />
+<glyph unicode=" " horiz-adv-x="260" />
+<glyph unicode=" " horiz-adv-x="326" />
+<glyph unicode="€" d="M800 500h-300q9 -74 33 -132t52.5 -91t62 -54.5t59 -29t46.5 -7.5q29 0 66 13t75 37t63.5 67.5t25.5 96.5h174q-31 -172 -128 -278q-107 -117 -274 -117q-205 0 -324 158q-36 46 -69 131.5t-45 205.5h-217l100 100h113q0 47 5 100h-218l100 100h135q37 167 112 257 q117 141 297 141q242 0 354 -189q60 -103 66 -209h-181q0 55 -25.5 99t-63.5 68t-75 36.5t-67 12.5q-24 0 -52.5 -10t-62.5 -32t-65.5 -67t-50.5 -107h379l-100 -100h-300q-6 -46 -6 -100h406z" />
+<glyph unicode="−" d="M1100 700h-900v-300h900v300z" />
+<glyph unicode="☁" d="M178 300h750q120 0 205 86t85 208q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5q0 -80 56.5 -137t135.5 -57z" />
+<glyph unicode="✉" d="M1200 1100h-1200l600 -603zM300 600l-300 -300v600zM1200 900v-600l-300 300zM800 500l400 -400h-1200l400 400l200 -200z" />
+<glyph unicode="✏" d="M1101 889l99 92q13 13 13 32.5t-13 33.5l-153 153q-15 13 -33 13t-33 -13l-94 -97zM401 189l614 614l-214 214l-614 -614zM-13 -13l333 112l-223 223z" />
+<glyph unicode="" horiz-adv-x="500" d="M0 0z" />
+<glyph unicode="" d="M700 100h300v-100h-800v100h300v550l-500 550h1200l-500 -550v-550z" />
+<glyph unicode="" d="M1000 934v-521q-64 16 -138 -7q-79 -26 -122.5 -83t-25.5 -111q17 -55 85.5 -75.5t147.5 4.5q70 23 111.5 63.5t41.5 95.5v881q0 10 -7 15.5t-17 2.5l-752 -193q-10 -3 -17 -12.5t-7 -19.5v-689q-64 17 -138 -7q-79 -25 -122.5 -82t-25.5 -112t86 -75.5t147 5.5 q65 21 109 69t44 90v606z" />
+<glyph unicode="" d="M913 432l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342t142 342t342 142t342 -142t142 -342q0 -142 -78 -261zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233z" />
+<glyph unicode="" d="M649 949q48 69 109.5 105t121.5 38t118.5 -20.5t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-149.5 152.5t-126.5 127.5t-94 124.5t-33.5 117.5q0 64 28 123t73 100.5t104.5 64t119 20.5 t120 -38.5t104.5 -104.5z" />
+<glyph unicode="" d="M791 522l145 -449l-384 275l-382 -275l146 447l-388 280h479l146 400h2l146 -400h472zM168 71l2 1z" />
+<glyph unicode="" d="M791 522l145 -449l-384 275l-382 -275l146 447l-388 280h479l146 400h2l146 -400h472zM747 331l-74 229l193 140h-235l-77 211l-78 -211h-239l196 -142l-73 -226l192 140zM168 71l2 1z" />
+<glyph unicode="" d="M1200 143v-143h-1200v143l400 257v100q-37 0 -68.5 74.5t-31.5 125.5v200q0 124 88 212t212 88t212 -88t88 -212v-200q0 -51 -31.5 -125.5t-68.5 -74.5v-100z" />
+<glyph unicode="" d="M1200 1100v-1100h-1200v1100h1200zM200 1000h-100v-100h100v100zM900 1000h-600v-400h600v400zM1100 1000h-100v-100h100v100zM200 800h-100v-100h100v100zM1100 800h-100v-100h100v100zM200 600h-100v-100h100v100zM1100 600h-100v-100h100v100zM900 500h-600v-400h600 v400zM200 400h-100v-100h100v100zM1100 400h-100v-100h100v100zM200 200h-100v-100h100v100zM1100 200h-100v-100h100v100z" />
+<glyph unicode="" d="M500 1050v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5zM1100 1050v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h400 q21 0 35.5 -14.5t14.5 -35.5zM500 450v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5zM1100 450v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-40 [...]
+<glyph unicode="" d="M300 1050v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5zM700 1050v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h200 q21 0 35.5 -14.5t14.5 -35.5zM1100 1050v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5zM300 650v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-20 [...]
+<glyph unicode="" d="M300 1050v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5zM1200 1050v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h700 q21 0 35.5 -14.5t14.5 -35.5zM300 450v200q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-200q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5zM1200 650v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-70 [...]
+<glyph unicode="" d="M448 34l818 820l-212 212l-607 -607l-206 207l-212 -212z" />
+<glyph unicode="" d="M882 106l-282 282l-282 -282l-212 212l282 282l-282 282l212 212l282 -282l282 282l212 -212l-282 -282l282 -282z" />
+<glyph unicode="" d="M913 432l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342t142 342t342 142t342 -142t142 -342q0 -142 -78 -261zM507 363q137 0 233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5t-234 -97t-97 -233 t97 -233t234 -97zM600 800h100v-200h-100v-100h-200v100h-100v200h100v100h200v-100z" />
+<glyph unicode="" d="M913 432l300 -299q7 -7 7 -18t-7 -18l-109 -109q-8 -8 -18 -8t-18 8l-300 299q-120 -77 -261 -77q-200 0 -342 142t-142 342t142 342t342 142t342 -142t142 -342q0 -141 -78 -262zM176 694q0 -136 97 -233t234 -97t233.5 97t96.5 233t-96.5 233t-233.5 97t-234 -97 t-97 -233zM300 801v-200h400v200h-400z" />
+<glyph unicode="" d="M700 750v400q0 21 -14.5 35.5t-35.5 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-400q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5zM800 975v166q167 -62 272 -210t105 -331q0 -118 -45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123 t-123 184t-45.5 224.5q0 183 105 331t272 210v-166q-103 -55 -165 -155t-62 -220q0 -177 125 -302t302 -125t302 125t125 302q0 120 -62 220t-165 155z" />
+<glyph unicode="" d="M1200 1h-200v1200h200v-1200zM900 1h-200v800h200v-800zM600 1h-200v500h200v-500zM300 301h-200v-300h200v300z" />
+<glyph unicode="" d="M488 183l38 -151q40 -5 74 -5q27 0 74 5l38 151l6 2q46 13 93 39l5 3l134 -81q56 44 104 105l-80 134l3 5q24 44 39 93l1 6l152 38q5 40 5 74q0 28 -5 73l-152 38l-1 6q-16 51 -39 93l-3 5l80 134q-44 58 -104 105l-134 -81l-5 3q-45 25 -93 39l-6 1l-38 152q-40 5 -74 5 q-27 0 -74 -5l-38 -152l-5 -1q-50 -14 -94 -39l-5 -3l-133 81q-59 -47 -105 -105l80 -134l-3 -5q-25 -47 -38 -93l-2 -6l-151 -38q-6 -48 -6 -73q0 -33 6 -74l151 -38l2 -6q14 -49 38 -93l3 -5l-80 -134q45 -59 105 -105l133 81 [...]
+<glyph unicode="" d="M900 1100h275q10 0 17.5 -7.5t7.5 -17.5v-50q0 -11 -7 -18t-18 -7h-1050q-11 0 -18 7t-7 18v50q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5v-100zM800 1100v100h-300v-100h300zM200 900h900v-800q0 -41 -29.5 -71 t-70.5 -30h-700q-41 0 -70.5 30t-29.5 71v800zM300 100h100v700h-100v-700zM500 100h100v700h-100v-700zM700 100h100v700h-100v-700zM900 100h100v700h-100v-700z" />
+<glyph unicode="" d="M1301 601h-200v-600h-300v400h-300v-400h-300v600h-200l656 644z" />
+<glyph unicode="" d="M600 700h400v-675q0 -11 -7 -18t-18 -7h-850q-11 0 -18 7t-7 18v1150q0 11 7 18t18 7h475v-500zM1000 800h-300v300z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM600 600h200 v-100h-300v400h100v-300z" />
+<glyph unicode="" d="M721 400h-242l-40 -400h-539l431 1200h209l-21 -300h162l-20 300h208l431 -1200h-538zM712 500l-27 300h-170l-27 -300h224z" />
+<glyph unicode="" d="M1100 400v-400h-1100v400h490l-290 300h200v500h300v-500h200l-290 -300h490zM988 300h-175v-100h175v100z" />
+<glyph unicode="" d="M600 1199q122 0 233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233t47.5 233t127.5 191t191 127.5t233 47.5zM600 1012q-170 0 -291 -121t-121 -291t121 -291t291 -121t291 121 t121 291t-121 291t-291 121zM700 600h150l-250 -300l-250 300h150v300h200v-300z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM850 600h-150 v-300h-200v300h-150l250 300z" />
+<glyph unicode="" d="M0 500l200 700h800q199 -700 200 -700v-475q0 -11 -7 -18t-18 -7h-1150q-11 0 -18 7t-7 18v475zM903 1000h-606l-97 -500h200l50 -200h300l50 200h200z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5zM797 598 l-297 -201v401z" />
+<glyph unicode="" d="M1177 600h-150q0 -177 -125 -302t-302 -125t-302 125t-125 302t125 302t302 125q136 0 246 -81l-146 -146h400v400l-145 -145q-157 122 -355 122q-118 0 -224.5 -45.5t-184 -123t-123 -184t-45.5 -224.5t45.5 -224.5t123 -184t184 -123t224.5 -45.5t224.5 45.5t184 123 t123 184t45.5 224.5z" />
+<glyph unicode="" d="M700 800l147 147q-112 80 -247 80q-177 0 -302 -125t-125 -302h-150q0 118 45.5 224.5t123 184t184 123t224.5 45.5q198 0 355 -122l145 145v-400h-400zM500 400l-147 -147q112 -80 247 -80q177 0 302 125t125 302h150q0 -118 -45.5 -224.5t-123 -184t-184 -123 t-224.5 -45.5q-198 0 -355 122l-145 -145v400h400z" />
+<glyph unicode="" d="M100 1200v-1200h1100v1200h-1100zM1100 100h-900v900h900v-900zM400 800h-100v100h100v-100zM1000 800h-500v100h500v-100zM400 600h-100v100h100v-100zM1000 600h-500v100h500v-100zM400 400h-100v100h100v-100zM1000 400h-500v100h500v-100zM400 200h-100v100h100v-100 zM1000 300h-500v-100h500v100z" />
+<glyph unicode="" d="M200 0h-100v1100h100v-1100zM1100 600v500q-40 -81 -101.5 -115.5t-127.5 -29.5t-138 25t-139.5 40t-125.5 25t-103 -29.5t-65 -115.5v-500q60 60 127.5 84t127.5 17.5t122 -23t119 -30t110 -11t103 42t91 120.5z" />
+<glyph unicode="" d="M1200 275v300q0 116 -49.5 227t-131 192.5t-192.5 131t-227 49.5t-227 -49.5t-192.5 -131t-131 -192.5t-49.5 -227v-300q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 127 70.5 231.5t184.5 161.5t245 57t245 -57t184.5 -161.5t70.5 -231.5v-300q0 -11 7 -18t18 -7h50 q11 0 18 7t7 18zM400 480v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14zM1000 480v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14z" />
+<glyph unicode="" d="M0 800v-400h300l300 -200v800l-300 -200h-300zM971 600l141 -141l-71 -71l-141 141l-141 -141l-71 71l141 141l-141 141l71 71l141 -141l141 141l71 -71z" />
+<glyph unicode="" d="M0 800v-400h300l300 -200v800l-300 -200h-300zM700 857l69 53q111 -135 111 -310q0 -169 -106 -302l-67 54q86 110 86 248q0 146 -93 257z" />
+<glyph unicode="" d="M974 186l6 8q142 178 142 405q0 230 -144 408l-6 8l-83 -64l7 -8q123 -151 123 -344q0 -189 -119 -339l-7 -8zM300 801l300 200v-800l-300 200h-300v400h300zM702 858l69 53q111 -135 111 -310q0 -170 -106 -303l-67 55q86 110 86 248q0 145 -93 257z" />
+<glyph unicode="" d="M100 700h400v100h100v100h-100v300h-500v-600h100v100zM1200 700v500h-600v-200h100v-300h200v-300h300v200h-200v100h200zM100 1100h300v-300h-300v300zM800 800v300h300v-300h-300zM200 900h100v100h-100v-100zM900 1000h100v-100h-100v100zM300 600h-100v-100h-200 v-500h500v500h-200v100zM900 200v-100h-200v100h-100v100h100v200h-200v100h300v-300h200v-100h-100zM400 400v-300h-300v300h300zM300 200h-100v100h100v-100zM1100 300h100v-100h-100v100zM600 100h100v-100h-100v100zM1200 100v [...]
+<glyph unicode="" d="M100 1200h-100v-1000h100v1000zM300 200h-100v1000h100v-1000zM700 200h-200v1000h200v-1000zM900 200h-100v1000h100v-1000zM1200 1200v-1000h-200v1000h200zM400 100v-100h-300v100h300zM500 91h100v-91h-100v91zM700 91h100v-91h-100v91zM1100 91v-91h-200v91h200z " />
+<glyph unicode="" d="M1200 500l-500 -500l-699 700v475q0 10 7.5 17.5t17.5 7.5h474zM320 882q29 29 29 71t-29 71q-30 30 -71.5 30t-71.5 -30q-29 -29 -29 -71t29 -71q30 -30 71.5 -30t71.5 30z" />
+<glyph unicode="" d="M1201 500l-500 -500l-699 700v475q0 11 7 18t18 7h474zM1501 500l-500 -500l-50 50l450 450l-700 700h100zM320 882q30 29 30 71t-30 71q-29 30 -71 30t-71 -30q-30 -29 -30 -71t30 -71q29 -30 71 -30t71 30z" />
+<glyph unicode="" d="M1200 1200v-1000l-100 -100v1000h-750l-100 -100h750v-1000h-900v1025l175 175h925z" />
+<glyph unicode="" d="M947 829l-94 346q-2 11 -10 18t-18 7h-450q-10 0 -18 -7t-10 -18l-94 -346l40 -124h592zM1200 800v-700h-200v200h-800v-200h-200v700h200l100 -200h600l100 200h200zM881 176l38 -152q2 -10 -3.5 -17t-15.5 -7h-600q-10 0 -15.5 7t-3.5 17l38 152q2 10 11.5 17t19.5 7 h500q10 0 19.5 -7t11.5 -17z" />
+<glyph unicode="" d="M1200 0v66q-34 1 -74 43q-18 19 -33 42t-21 37l-6 13l-385 998h-93l-399 -1006q-24 -48 -52 -75q-12 -12 -33 -25t-36 -20l-15 -7v-66h365v66q-41 0 -72 11t-49 38t1 71l92 234h391l82 -222q16 -45 -5.5 -88.5t-74.5 -43.5v-66h417zM416 521l178 457l46 -140l116 -317 h-340z" />
+<glyph unicode="" d="M100 1199h471q120 0 213 -88t93 -228q0 -55 -11.5 -101.5t-28 -74t-33.5 -47.5t-28 -28l-12 -7q8 -3 21.5 -9t48 -31.5t60.5 -58t47.5 -91.5t21.5 -129q0 -84 -59 -156.5t-142 -111t-162 -38.5h-500v89q41 7 70.5 32.5t29.5 65.5v827q0 28 -1 39.5t-5.5 26t-15.5 21 t-29 14t-49 14.5v70zM400 1079v-379h139q76 0 130 61.5t54 138.5q0 82 -84 130.5t-239 48.5zM400 200h161q89 0 153 48.5t64 132.5q0 90 -62.5 154.5t-156.5 64.5h-159v-400z" />
+<glyph unicode="" d="M877 1200l2 -57q-33 -8 -62 -25.5t-46 -37t-29.5 -38t-17.5 -30.5l-5 -12l-128 -825q-10 -52 14 -82t95 -36v-57h-500v57q77 7 134.5 40.5t65.5 80.5l173 849q10 56 -10 74t-91 37q-6 1 -10.5 2.5t-9.5 2.5v57h425z" />
+<glyph unicode="" d="M1150 1200h150v-300h-50q0 29 -8 48.5t-18.5 30t-33.5 15t-39.5 5.5t-50.5 1h-200v-850l100 -50v-100h-400v100l100 50v850h-200q-34 0 -50.5 -1t-40 -5.5t-33.5 -15t-18.5 -30t-8.5 -48.5h-49v300h150h700zM100 1000v-800h75l-125 -167l-125 167h75v800h-75l125 167 l125 -167h-75z" />
+<glyph unicode="" d="M950 1201h150v-300h-50q0 29 -8 48.5t-18 30t-33.5 15t-40 5.5t-50.5 1h-200v-650l100 -50v-100h-400v100l100 50v650h-200q-34 0 -50.5 -1t-39.5 -5.5t-33.5 -15t-18.5 -30t-8 -48.5h-50v300h150h700zM200 101h800v75l167 -125l-167 -125v75h-800v-75l-167 125l167 125 v-75z" />
+<glyph unicode="" d="M700 950v100q0 21 -14.5 35.5t-35.5 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h600q21 0 35.5 15t14.5 35zM1100 650v100q0 21 -14.5 35.5t-35.5 14.5h-1000q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h1000 q21 0 35.5 15t14.5 35zM900 350v100q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35zM1200 50v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -3 [...]
+<glyph unicode="" d="M1000 950v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35zM1200 650v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h1100 q21 0 35.5 15t14.5 35zM1000 350v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35zM1200 50v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 [...]
+<glyph unicode="" d="M500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-600q-21 0 -35.5 15t-14.5 35zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1000q-21 0 -35.5 15 t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 [...]
+<glyph unicode="" d="M0 950v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15 t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 - [...]
+<glyph unicode="" d="M0 950v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM300 950v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM300 650v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 [...]
+<glyph unicode="" d="M400 1100h-100v-1100h100v1100zM700 950v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35zM1100 650v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15 h500q20 0 35 15t15 35zM100 425v75h-201v100h201v75l166 -125zM900 350v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35zM1200 50v100q0 21 -15 35.5t-35 [...]
+<glyph unicode="" d="M201 950v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35zM801 1100h100v-1100h-100v1100zM601 650v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15 h500q20 0 35 15t15 35zM1101 425v75h200v100h-200v75l-167 -125zM401 350v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35zM701 50v100q0 21 -15 35.5t-35 [...]
+<glyph unicode="" d="M900 925v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53v650q0 31 22 53t53 22h750q31 0 53 -22t22 -53zM1200 300l-300 300l300 300v-600z" />
+<glyph unicode="" d="M1200 1056v-1012q0 -18 -12.5 -31t-31.5 -13h-1112q-18 0 -31 13t-13 31v1012q0 18 13 31t31 13h1112q19 0 31.5 -13t12.5 -31zM1100 1000h-1000v-737l247 182l298 -131l-74 156l293 318l236 -288v500zM476 750q0 -56 -39 -95t-95 -39t-95 39t-39 95t39 95t95 39t95 -39 t39 -95z" />
+<glyph unicode="" d="M600 1213q123 0 227 -63t164.5 -169.5t60.5 -229.5t-73 -272q-73 -114 -166.5 -237t-150.5 -189l-57 -66q-10 9 -27 26t-66.5 70.5t-96 109t-104 135.5t-100.5 155q-63 139 -63 262q0 124 60.5 231.5t165 172t226.5 64.5zM599 514q107 0 182.5 75.5t75.5 182.5t-75.5 182 t-182.5 75t-182 -75.5t-75 -181.5q0 -107 75.5 -182.5t181.5 -75.5z" />
+<glyph unicode="" d="M600 1199q122 0 233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233t47.5 233t127.5 191t191 127.5t233 47.5zM600 173v854q-176 0 -301.5 -125t-125.5 -302t125.5 -302t301.5 -125z " />
+<glyph unicode="" d="M554 1295q21 -71 57.5 -142.5t76 -130.5t83 -118.5t82 -117t70 -116t50 -125.5t18.5 -136q0 -89 -39 -165.5t-102 -126.5t-140 -79.5t-156 -33.5q-114 6 -211.5 53t-161.5 138.5t-64 210.5q0 94 34 186t88.5 172.5t112 159t115 177t87.5 194.5zM455 296q-7 6 -18 17 t-34 48t-33 77q-15 73 -14 143.5t10 122.5l9 51q-92 -110 -119.5 -185t-12.5 -156q14 -82 59.5 -136t136.5 -80z" />
+<glyph unicode="" d="M1108 902l113 113l-21 85l-92 28l-113 -113zM1100 625v-225q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5v300q0 165 117.5 282.5t282.5 117.5q366 -6 397 -14l-186 -186h-311q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5 t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v125zM436 341l161 50l412 412l-114 113l-405 -405z" />
+<glyph unicode="" d="M1100 453v-53q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5v300q0 165 117.5 282.5t282.5 117.5h261l2 -80q-133 -32 -218 -120h-145q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5z M813 431l360 324l-359 318v-216q-7 0 -19 -1t-48 -8t-69.5 -18.5t-76.5 -37t-76.5 -59t-62 -88t-39.5 -121.5q30 38 81.5 64t103 35.5t99 14t77.5 3.5l29 -1v-209z" />
+<glyph unicode="" d="M1100 569v-169q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5v300q0 165 117.5 282.5t282.5 117.5h300q60 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69z M625 348l566 567l-136 137l-430 -431l-147 147l-136 -136z" />
+<glyph unicode="" d="M900 303v198h-200v-200h195l-295 -300l-300 300h200v200h-200v-198l-300 300l300 296v-198h200v200h-200l300 300l295 -300h-195v-200h200v198l300 -296z" />
+<glyph unicode="" d="M900 0l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-1100z" />
+<glyph unicode="" d="M1200 0l-500 488v-488l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-487l500 487v-1100z" />
+<glyph unicode="" d="M1200 0l-500 488v-488l-564 550l564 550v-487l500 487v-1100z" />
+<glyph unicode="" d="M1100 550l-900 550v-1100z" />
+<glyph unicode="" d="M500 150v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5zM900 150v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800q0 -21 14.5 -35.5t35.5 -14.5h200 q21 0 35.5 14.5t14.5 35.5z" />
+<glyph unicode="" d="M1100 150v800q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-800q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35z" />
+<glyph unicode="" d="M500 0v488l-500 -488v1100l500 -487v487l564 -550z" />
+<glyph unicode="" d="M1050 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v488l-500 -488v1100l500 -487v487l500 -487v437q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M850 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v1100l500 -487v437q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M650 1064l-550 -564h1100zM1200 350v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="" d="M777 7l240 240l-353 353l353 353l-240 240l-592 -594z" />
+<glyph unicode="" d="M513 -46l-241 240l353 353l-353 353l241 240l572 -571l21 -22l-1 -1v-1z" />
+<glyph unicode="" d="M600 1197q162 0 299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5t80 299.5t217.5 217.5t299.5 80zM500 900v-200h-200v-200h200v-200h200v200h200v200h-200v200h-200z" />
+<glyph unicode="" d="M600 1197q162 0 299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5t80 299.5t217.5 217.5t299.5 80zM300 700v-200h600v200h-600z" />
+<glyph unicode="" d="M600 1197q162 0 299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5t80 299.5t217.5 217.5t299.5 80zM247 741l141 -141l-142 -141l213 -213l141 142l141 -142l213 213l-142 141l142 141l-213 212l-141 -141 l-141 142z" />
+<glyph unicode="" d="M600 1197q162 0 299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5t80 299.5t217.5 217.5t299.5 80zM546 623l-102 102l-174 -174l276 -277l411 411l-175 174z" />
+<glyph unicode="" d="M600 1197q162 0 299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5t80 299.5t217.5 217.5t299.5 80zM500 500h200q5 3 14 8t31.5 25.5t39.5 45.5t31 69t14 94q0 51 -17.5 89t-42 58t-58.5 32t-58.5 15t-51.5 3 q-105 0 -172 -56t-67 -183h144q4 0 11.5 -1t11 -1t6.5 3t3 9t1 11t3.5 8.5t3.5 6t5.5 4t6.5 2.5t9 1.5t9 0.5h11.5h12.5q19 0 30 -10t11 -26q0 -22 -4 -28t-27 -22q-5 -1 -12.5 -3t-27 -13.5t-34 -27t-26.5 -46t-11 -68.5zM500 400 [...]
+<glyph unicode="" d="M600 1197q162 0 299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5t80 299.5t217.5 217.5t299.5 80zM500 900v-100h200v100h-200zM400 700v-100h100v-200h-100v-100h400v100h-100v300h-300z" />
+<glyph unicode="" d="M1200 700v-200h-203q-25 -102 -116.5 -186t-180.5 -117v-197h-200v197q-140 27 -208 102.5t-98 200.5h-194v200h194q15 60 36 104.5t55.5 86t88 69t126.5 40.5v200h200v-200q54 -20 113 -60t112.5 -105.5t71.5 -134.5h203zM700 500v-206q149 48 201 206h-201v200h200 q-25 74 -76 127.5t-124 76.5v-204h-200v203q-75 -24 -130 -77.5t-79 -125.5h209v-200h-210q24 -73 79.5 -127.5t130.5 -78.5v206h200z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM844 735 l-135 -135l135 -135l-109 -109l-135 135l-135 -135l-109 109l135 135l-135 135l109 109l135 -135l135 135z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM896 654 l-346 -345l-228 228l141 141l87 -87l204 205z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM248 385l568 567q-100 62 -216 62q-171 0 -292.5 -121.5t-121.5 -292.5q0 -115 62 -215zM955 809l-564 -564q97 -59 209 -59q171 0 292.5 121.5 t121.5 292.5q0 112 -59 209z" />
+<glyph unicode="" d="M1200 400h-600v-301l-600 448l600 453v-300h600v-300z" />
+<glyph unicode="" d="M600 400h-600v300h600v300l600 -453l-600 -448v301z" />
+<glyph unicode="" d="M1098 600h-298v-600h-300v600h-296l450 600z" />
+<glyph unicode="" d="M998 600l-449 -600l-445 600h296v600h300v-600h298z" />
+<glyph unicode="" d="M600 199v301q-95 -2 -183 -20t-170 -52t-147 -92.5t-100 -135.5q6 132 41 238.5t103.5 193t184 138t271.5 59.5v271l600 -453z" />
+<glyph unicode="" d="M1200 1200h-400l129 -129l-294 -294l142 -142l294 294l129 -129v400zM565 423l-294 -294l129 -129h-400v400l129 -129l294 294z" />
+<glyph unicode="" d="M871 730l129 -130h-400v400l129 -129l295 295l142 -141zM200 600h400v-400l-129 130l-295 -295l-142 141l295 295z" />
+<glyph unicode="" d="M600 1177q118 0 224.5 -45.5t184 -123t123 -184t45.5 -224.5t-45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5t45.5 224.5t123 184t184 123t224.5 45.5zM686 549l58 302q4 20 -8 34.5t-33 14.5h-207q-20 0 -32 -14.5t-8 -34.5 l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5zM700 400h-200v-100h200v100z" />
+<glyph unicode="" d="M1200 900h-111v6t-1 15t-3 18l-34 172q-11 39 -41.5 63t-69.5 24q-32 0 -61 -17l-239 -144q-22 -13 -40 -35q-19 24 -40 36l-238 144q-33 18 -62 18q-39 0 -69.5 -23t-40.5 -61l-35 -177q-2 -8 -3 -18t-1 -15v-6h-111v-100h100v-200h400v300h200v-300h400v200h100v100z M731 900l202 197q5 -12 12 -32.5t23 -64t25 -72t7 -28.5h-269zM481 900h-281q-3 0 14 48t35 96l18 47zM100 0h400v400h-400v-400zM700 400h400v-400h-400v400z" />
+<glyph unicode="" d="M0 121l216 193q-9 53 -13 83t-5.5 94t9 113t38.5 114t74 124q47 60 99.5 102.5t103 68t127.5 48t145.5 37.5t184.5 43.5t220 58.5q0 -189 -22 -343t-59 -258t-89 -181.5t-108.5 -120t-122 -68t-125.5 -30t-121.5 -1.5t-107.5 12.5t-87.5 17t-56.5 7.5l-99 -55l-201 -202 v143zM692 611q70 38 118.5 69.5t102 79t99 111.5t86.5 148q22 50 24 60t-6 19q-7 5 -17 5t-26.5 -14.5t-33.5 -39.5q-35 -51 -113.5 -108.5t-139.5 -89.5l-61 -32q-369 -197 -458 -401q-48 -111 -28.5 -117.5t86.5 76.5q55 66 36 [...]
+<glyph unicode="" d="M1261 600l-26 -40q-6 -10 -20 -30t-49 -63.5t-74.5 -85.5t-97 -90t-116.5 -83.5t-132.5 -59t-145.5 -23.5t-145.5 23.5t-132.5 59t-116.5 83.5t-97 90t-74.5 85.5t-49 63.5t-20 30l-26 40l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5 t145.5 -23.5t132.5 -59t116.5 -83.5t97 -90t74.5 -85.5t49 -63.5t20 -30zM600 240q64 0 123.5 20t100.5 45.5t85.5 71.5t66.5 75.5t58 81.5t47 66q-1 1 -28.5 37.5t-42 55t-43.5 53t-57.5 63.5t-58.5 54q49 -74 49 -163q0 -124 -88 - [...]
+<glyph unicode="" d="M906 1200l-314 -1200h-148l37 143q-82 21 -165 71.5t-140 102t-109.5 112t-72 88.5t-29.5 43l-26 40l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5q61 0 121 -17l37 142h148zM1261 600l-26 -40q-7 -12 -25.5 -38t-63.5 -79.5t-95.5 -102.5 t-124 -100t-146.5 -79l38 145q22 15 44.5 34t46 44t40.5 44t41 50.5t33.5 43.5t33 44t24.5 34q-97 127 -140 175l39 146q67 -54 131.5 -125.5t87.5 -103.5t36 -52zM513 264l37 141q-107 18 -178.5 101.5t-71.5 193.5q0 85 46 158 [...]
+<glyph unicode="" d="M-47 0h1294q37 0 50.5 35.5t-7.5 67.5l-642 1056q-20 33 -48 36t-48 -29l-642 -1066q-21 -32 -7.5 -66t50.5 -34zM700 200v100h-200v-100h-345l445 723l445 -723h-345zM700 700h-200v-100l100 -300l100 300v100z" />
+<glyph unicode="" d="M800 711l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -91 100 -113v-64q0 -21 -13 -29t-32 1l-94 78h-222l-94 -78q-19 -9 -32 -1t-13 29v64q0 22 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5v41q0 20 11 44.5t26 38.5 l363 325v339q0 62 44 106t106 44t106 -44t44 -106v-339z" />
+<glyph unicode="" d="M941 800l-600 -600h-341v200h259l600 600h241v198l300 -295l-300 -300v197h-159zM381 678l141 142l-181 180h-341v-200h259zM1100 598l300 -295l-300 -300v197h-241l-181 181l141 142l122 -123h159v198z" />
+<glyph unicode="" d="M100 1100h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5z" />
+<glyph unicode="" d="M400 900h-300v300h300v-300zM1100 900h-300v300h300v-300zM1100 800v-200q0 -42 -3 -83t-15 -104t-31.5 -116t-58 -109.5t-89 -96.5t-129 -65.5t-174.5 -25.5t-174.5 25.5t-129 65.5t-89 96.5t-58 109.5t-31.5 116t-15 104t-3 83v200h300v-250q0 -113 6 -145 q17 -92 102 -117q39 -11 92 -11q37 0 66.5 5.5t50 15.5t36 24t24 31.5t14 37.5t7 42t2.5 45t0 47v25v250h300z" />
+<glyph unicode="" d="M902 184l226 227l-578 579l-580 -579l227 -227l352 353z" />
+<glyph unicode="" d="M650 218l578 579l-226 227l-353 -353l-352 353l-227 -227z" />
+<glyph unicode="" d="M1198 400v600h-796l215 -200h381v-400h-198l299 -283l299 283h-200zM-198 700l299 283l300 -283h-203v-400h385l215 -200h-800v600h-196z" />
+<glyph unicode="" d="M1050 1200h94q20 0 35 -14.5t15 -35.5t-15 -35.5t-35 -14.5h-54l-201 -961q-2 -4 -6 -10.5t-19 -17.5t-33 -11h-31v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-300v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-50q-21 0 -35.5 15t-14.5 35 q0 21 14.5 35.5t35.5 14.5h535l48 200h-633q-32 0 -54.5 21t-27.5 43l-100 475q-5 24 10 42q14 19 39 19h896l38 162q5 17 18.5 27.5t30.5 10.5z" />
+<glyph unicode="" d="M1200 1000v-100h-1200v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500zM0 800h1200v-800h-1200v800z" />
+<glyph unicode="" d="M201 800l-200 -400v600h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-200h-1000zM1501 700l-300 -700h-1200l300 700h1200z" />
+<glyph unicode="" d="M302 300h198v600h-198l298 300l298 -300h-198v-600h198l-298 -300z" />
+<glyph unicode="" d="M900 303v197h-600v-197l-300 297l300 298v-198h600v198l300 -298z" />
+<glyph unicode="" d="M31 400l172 739q5 22 23 41.5t38 19.5h672q19 0 37.5 -22.5t23.5 -45.5l172 -732h-1138zM100 300h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM900 200h-100v-100h100v100z M1100 200h-100v-100h100v100z" />
+<glyph unicode="" d="M1100 200v850q0 21 14.5 35.5t35.5 14.5q20 0 35 -14.5t15 -35.5v-850q0 -20 -15 -35t-35 -15q-21 0 -35.5 15t-14.5 35zM325 800l675 250v-850l-675 200h-38l47 -276q2 -12 -3 -17.5t-11 -6t-21 -0.5h-8h-83q-20 0 -34.5 14t-18.5 35q-56 337 -56 351v250v5 q0 13 0.5 18.5t2.5 13t8 10.5t15 3h200zM-101 600v50q0 24 25 49t50 38l25 13v-250l-11 5.5t-24 14t-30 21.5t-24 27.5t-11 31.5z" />
+<glyph unicode="" d="M445 1180l-45 -233l-224 78l78 -225l-233 -44l179 -156l-179 -155l233 -45l-78 -224l224 78l45 -233l155 179l155 -179l45 233l224 -78l-78 224l234 45l-180 155l180 156l-234 44l78 225l-224 -78l-45 233l-155 -180z" />
+<glyph unicode="" d="M700 1200h-50q-27 0 -51 -20t-38 -48l-96 -198l-145 -196q-20 -26 -20 -63v-400q0 -75 100 -75h61q123 -100 139 -100h250q46 0 83 57l238 344q29 31 29 74v100q0 44 -30.5 84.5t-69.5 40.5h-328q28 118 28 125v150q0 44 -30.5 84.5t-69.5 40.5zM700 925l-50 -225h450 v-125l-250 -375h-214l-136 100h-100v375l150 212l100 213h50v-175zM0 800v-600h200v600h-200z" />
+<glyph unicode="" d="M700 0h-50q-27 0 -51 20t-38 48l-96 198l-145 196q-20 26 -20 63v400q0 75 100 75h61q123 100 139 100h250q46 0 83 -57l238 -344q29 -31 29 -74v-100q0 -44 -30.5 -84.5t-69.5 -40.5h-328q28 -118 28 -125v-150q0 -44 -30.5 -84.5t-69.5 -40.5zM200 400h-200v600h200 v-600zM700 275l-50 225h450v125l-250 375h-214l-136 -100h-100v-375l150 -212l100 -213h50v175z" />
+<glyph unicode="" d="M364 873l362 230q14 6 25 6q17 0 29 -12l109 -112q14 -14 14 -34q0 -18 -11 -32l-85 -121h302q85 0 138.5 -38t53.5 -110t-54.5 -111t-138.5 -39h-107l-130 -339q-7 -22 -20.5 -41.5t-28.5 -19.5h-341q-7 0 -90 81t-83 94v525q0 17 14 35.5t28 28.5zM408 792v-503 l100 -89h293l131 339q6 21 19.5 41t28.5 20h203q16 0 25 15t9 36q0 20 -9 34.5t-25 14.5h-457h-6.5h-7.5t-6.5 0.5t-6 1t-5 1.5t-5.5 2.5t-4 4t-4 5.5q-5 12 -5 20q0 14 10 27l147 183l-86 83zM208 200h-200v600h200v-600z" />
+<glyph unicode="" d="M475 1104l365 -230q7 -4 16.5 -10.5t26 -26t16.5 -36.5v-526q0 -13 -85.5 -93.5t-93.5 -80.5h-342q-15 0 -28.5 20t-19.5 41l-131 339h-106q-84 0 -139 39t-55 111t54 110t139 37h302l-85 121q-11 16 -11 32q0 21 14 34l109 113q13 12 29 12q11 0 25 -6zM370 946 l145 -184q10 -11 10 -26q0 -11 -5 -20q-1 -3 -3.5 -5.5l-4 -4t-5 -2.5t-5.5 -1.5t-6.5 -1t-6.5 -0.5h-7.5h-6.5h-476v-100h222q15 0 28.5 -20.5t19.5 -40.5l131 -339h293l106 89v502l-342 237zM1199 201h-200v600h200v-600z" />
+<glyph unicode="" d="M1100 473v342q0 15 -20 28.5t-41 19.5l-339 131v106q0 84 -39 139t-111 55t-110 -53.5t-38 -138.5v-302l-121 84q-15 12 -33.5 11.5t-32.5 -13.5l-112 -110q-22 -22 -6 -53l230 -363q4 -6 10.5 -15.5t26 -25t36.5 -15.5h525q13 0 94 83t81 90zM911 400h-503l-236 339 l83 86l183 -146q22 -18 47 -5q3 1 5.5 3.5l4 4t2.5 5t1.5 5.5t1 6.5t0.5 6v7.5v7v456q0 22 25 31t50 -0.5t25 -30.5v-202q0 -16 20 -29.5t41 -19.5l339 -130v-294zM1000 200v-200h-600v200h600z" />
+<glyph unicode="" d="M305 1104v200h600v-200h-600zM605 310l339 131q20 6 40.5 19.5t20.5 28.5v342q0 7 -81 90t-94 83h-525q-17 0 -35.5 -14t-28.5 -28l-10 -15l-230 -362q-15 -31 7 -53l112 -110q13 -13 32 -13.5t34 10.5l121 85l-1 -302q0 -84 38.5 -138t110.5 -54t111 55t39 139v106z M905 804v-294l-340 -130q-20 -6 -40 -20t-20 -29v-202q0 -22 -25 -31t-50 0t-25 31v456v14.5t-1.5 11.5t-5 12t-9.5 7q-24 13 -46 -5l-184 -146l-83 86l237 339h503z" />
+<glyph unicode="" d="M603 1195q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5zM598 701h-298v-201h300l-2 -194l402 294l-402 298v-197z" />
+<glyph unicode="" d="M597 1195q122 0 232.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-218 -217.5t-300 -80t-299.5 80t-217.5 217.5t-80 299.5q0 122 47.5 232.5t127.5 190.5t190.5 127.5t231.5 47.5zM200 600l400 -294v194h302v201h-300v197z" />
+<glyph unicode="" d="M603 1195q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5zM300 600h200v-300h200v300h200l-300 400z" />
+<glyph unicode="" d="M603 1195q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5zM500 900v-300h-200l300 -400l300 400h-200v300h-200z" />
+<glyph unicode="" d="M603 1195q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5zM627 1101q-15 -12 -36.5 -21t-34.5 -12t-44 -8t-39 -6 q-15 -3 -45.5 0.5t-45.5 -2.5q-21 -7 -52 -26.5t-34 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -90.5t-29.5 -79.5q-8 -33 5.5 -92.5t7.5 -87.5q0 -9 17 -44t16 -60q12 0 23 -5.5t23 -15t20 -13.5q24 -12 108 -42q22 -8 53 -31.5t59 [...]
+<glyph unicode="" horiz-adv-x="1220" d="M100 1196h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 1096h-200v-100h200v100zM100 796h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 696h-500v-100h500v100zM100 396h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70 [...]
+<glyph unicode="" d="M1100 1200v-100h-1000v100h1000zM150 1000h900l-350 -500v-300l-200 -200v500z" />
+<glyph unicode="" d="M329 729l142 142l-200 200l129 129h-400v-400l129 129zM1200 1200v-400l-129 129l-200 -200l-142 142l200 200l-129 129h400zM271 129l129 -129h-400v400l129 -129l200 200l142 -142zM1071 271l129 129v-400h-400l129 129l-200 200l142 142z" />
+<glyph unicode="" d="M596 1192q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM596 1010q-171 0 -292.5 -121.5t-121.5 -292.5q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5zM455 905 q22 0 38 -16t16 -39t-16 -39t-38 -16q-23 0 -39 16.5t-16 38.5t16 38.5t39 16.5zM708 821l1 1q-9 14 -9 28q0 22 16 38.5t39 16.5q22 0 38 -16t16 -39t-16 -39t-38 -16q-14 0 -29 10l-55 -145q17 -22 17 -51q0 -36 -25.5 -61.5t- [...]
+<glyph unicode="" d="M649 54l-16 22q-90 125 -293 323q-71 70 -104.5 105.5t-77 89.5t-61 99t-17.5 91q0 131 98.5 229.5t230.5 98.5q143 0 241 -129q103 129 246 129q129 0 226 -98.5t97 -229.5q0 -46 -17.5 -91t-61 -99t-77 -89.5t-104.5 -105.5q-203 -198 -293 -323zM844 524l12 12 q64 62 97.5 97t64.5 79t31 72q0 71 -48 119t-105 48q-74 0 -132 -82l-118 -171l-114 174q-51 79 -123 79q-60 0 -109.5 -49t-49.5 -118q0 -27 30.5 -70t61.5 -75.5t95 -94.5l22 -22q93 -90 190 -201q82 92 195 203z" />
+<glyph unicode="" d="M476 406l19 -17l105 105l-212 212l389 389l247 -247l-95 -96l18 -18q46 -46 77 -99l29 29q35 35 62.5 88t27.5 96q0 93 -66 159l-141 141q-66 66 -159 66q-95 0 -159 -66l-283 -283q-66 -64 -66 -159q0 -93 66 -159zM123 193l141 -141q66 -66 159 -66q95 0 159 66 l283 283q66 66 66 159t-66 159l-141 141q-12 12 -19 17l-105 -105l212 -212l-389 -389l-247 248l95 95l-18 18q-46 45 -75 101l-55 -55q-66 -66 -66 -159q0 -94 66 -160z" />
+<glyph unicode="" d="M200 100v953q0 21 30 46t81 48t129 38t163 15t162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5zM900 1000h-600v-700h600v700zM600 46q43 0 73.5 30.5t30.5 73.5t-30.5 73.5t-73.5 30.5t-73.5 -30.5t-30.5 -73.5 t30.5 -73.5t73.5 -30.5z" />
+<glyph unicode="" d="M700 1029v-307l64 -14q34 -7 64 -16.5t70 -31.5t67.5 -52t47.5 -80.5t20 -112.5q0 -139 -89 -224t-244 -96v-77h-100v78q-152 17 -237 104q-40 40 -52.5 93.5t-15.5 139.5h139q5 -77 48.5 -126.5t117.5 -64.5v335l-27 7q-46 14 -79 26.5t-72 36t-62.5 52t-40 72.5 t-16.5 99q0 92 44 159.5t109 101t144 40.5v78h100v-79q38 -4 72.5 -13.5t75.5 -31.5t71 -53.5t51.5 -84t24.5 -118.5h-159q-8 72 -35 109.5t-101 50.5zM600 755v274q-61 -8 -97.5 -37.5t-36.5 -102.5q0 -29 8 -51t16.5 -34t29.5 -22.5t [...]
+<glyph unicode="" d="M866 300l50 -147q-41 -25 -80.5 -36.5t-59 -13t-61.5 -1.5q-23 0 -128 33t-155 29q-39 -4 -82 -17t-66 -25l-24 -11l-55 145l16.5 11t15.5 10t13.5 9.5t14.5 12t14.5 14t17.5 18.5q48 55 54 126.5t-30 142.5h-221v100h166q-24 49 -44 104q-10 26 -14.5 55.5t-3 72.5 t25 90t68.5 87q97 88 263 88q129 0 230 -89t101 -208h-153q0 52 -34 89.5t-74 51.5t-76 14q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -11 2.5 -24.5t5.5 -24t9.5 -26.5t10.5 -25t14 -27.5t14 -25.5t15.5 -27t13.5 -24h242v-100 [...]
+<glyph unicode="" d="M300 0l298 300h-198v900h-200v-900h-198zM900 1200l298 -300h-198v-900h-200v900h-198z" />
+<glyph unicode="" d="M400 300h198l-298 -300l-298 300h198v900h200v-900zM1000 1200v-500h-100v100h-100v-100h-100v500h300zM901 1100h-100v-200h100v200zM700 500h300v-200h-99v-100h-100v100h99v100h-200v100zM800 100h200v-100h-300v200h100v-100z" />
+<glyph unicode="" d="M400 300h198l-298 -300l-298 300h198v900h200v-900zM1000 1200v-200h-99v-100h-100v100h99v100h-200v100h300zM800 800h200v-100h-300v200h100v-100zM700 500h300v-500h-100v100h-100v-100h-100v500zM801 200h100v200h-100v-200z" />
+<glyph unicode="" d="M300 0l298 300h-198v900h-200v-900h-198zM900 1100h-100v100h200v-500h-100v400zM1100 500v-500h-100v100h-200v400h300zM1001 400h-100v-200h100v200z" />
+<glyph unicode="" d="M300 0l298 300h-198v900h-200v-900h-198zM1100 1200v-500h-100v100h-200v400h300zM1001 1100h-100v-200h100v200zM900 400h-100v100h200v-500h-100v400z" />
+<glyph unicode="" d="M300 0l298 300h-198v900h-200v-900h-198zM900 1000h-200v200h200v-200zM1000 700h-300v200h300v-200zM1100 400h-400v200h400v-200zM1200 100h-500v200h500v-200z" />
+<glyph unicode="" d="M300 0l298 300h-198v900h-200v-900h-198zM1200 1000h-500v200h500v-200zM1100 700h-400v200h400v-200zM1000 400h-300v200h300v-200zM900 100h-200v200h200v-200z" />
+<glyph unicode="" d="M400 1100h300q162 0 281 -118.5t119 -281.5v-300q0 -165 -118.5 -282.5t-281.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5v300q0 165 117.5 282.5t282.5 117.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5 t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5z" />
+<glyph unicode="" d="M700 0h-300q-163 0 -281.5 117.5t-118.5 282.5v300q0 163 119 281.5t281 118.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5 t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5zM400 800v-500l333 250z" />
+<glyph unicode="" d="M0 400v300q0 163 117.5 281.5t282.5 118.5h300q163 0 281.5 -119t118.5 -281v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM900 300v500q0 41 -29.5 70.5t-70.5 29.5h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5 t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5zM800 700h-500l250 -333z" />
+<glyph unicode="" d="M1100 700v-300q0 -162 -118.5 -281t-281.5 -119h-300q-165 0 -282.5 118.5t-117.5 281.5v300q0 165 117.5 282.5t282.5 117.5h300q165 0 282.5 -117.5t117.5 -282.5zM900 300v500q0 41 -29.5 70.5t-70.5 29.5h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5 t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5zM550 733l-250 -333h500z" />
+<glyph unicode="" d="M500 1100h400q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-400v200h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-500v200zM700 550l-400 -350v200h-300v300h300v200z" />
+<glyph unicode="" d="M403 2l9 -1q13 0 26 16l538 630q15 19 6 36q-8 18 -32 16h-300q1 4 78 219.5t79 227.5q2 17 -6 27l-8 8h-9q-16 0 -25 -15q-4 -5 -98.5 -111.5t-228 -257t-209.5 -238.5q-17 -19 -7 -40q10 -19 32 -19h302q-155 -438 -160 -458q-5 -21 4 -32z" />
+<glyph unicode="" d="M800 200h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h500v185q-14 4 -114 7.5t-193 5.5l-93 2q-165 0 -282.5 -117.5t-117.5 -282.5v-300q0 -165 117.5 -282.5t282.5 -117.5h300q47 0 100 15v185zM900 200v200h-300v300h300v200l400 -350z" />
+<glyph unicode="" d="M1200 700l-149 149l-342 -353l-213 213l353 342l-149 149h500v-500zM1022 571l-122 -123v-148q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h156l118 122l-74 78h-100q-165 0 -282.5 -117.5t-117.5 -282.5v-300 q0 -165 117.5 -282.5t282.5 -117.5h300q163 0 281.5 117.5t118.5 282.5v98z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM600 794 q80 0 137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137t57 137t137 57z" />
+<glyph unicode="" d="M700 800v400h-300v-400h-300l445 -500l450 500h-295zM25 300h1048q11 0 19 -7.5t8 -17.5v-275h-1100v275q0 11 7 18t18 7zM1000 200h-100v-50h100v50z" />
+<glyph unicode="" d="M400 700v-300h300v300h295l-445 500l-450 -500h300zM25 300h1048q11 0 19 -7.5t8 -17.5v-275h-1100v275q0 11 7 18t18 7zM1000 200h-100v-50h100v50z" />
+<glyph unicode="" d="M405 400l596 596l-154 155l-442 -442l-150 151l-155 -155zM25 300h1048q11 0 19 -7.5t8 -17.5v-275h-1100v275q0 11 7 18t18 7zM1000 200h-100v-50h100v50z" />
+<glyph unicode="" d="M409 1103l-97 97l-212 -212l97 -98zM650 861l-149 149l-212 -212l149 -149l-238 -248h700v699zM25 300h1048q11 0 19 -7.5t8 -17.5v-275h-1100v275q0 11 7 18t18 7zM1000 200h-100v-50h100v50z" />
+<glyph unicode="" d="M539 950l-149 -149l212 -212l149 148l248 -237v700h-699zM297 709l-97 -97l212 -212l98 97zM25 300h1048q11 0 19 -7.5t8 -17.5v-275h-1100v275q0 11 7 18t18 7zM1000 200h-100v-50h100v50z" />
+<glyph unicode="" d="M1200 1199v-1079l-475 272l-310 -393v416h-392zM1166 1148l-672 -712v-226z" />
+<glyph unicode="" d="M1100 1000v-850q0 -21 -15 -35.5t-35 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1200h-100v-200h100v200z" />
+<glyph unicode="" d="M578 500h-378v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-218l-276 -275l-120 120zM700 1200h-100v-200h100v200zM1300 538l-475 -476l-244 244l123 123l120 -120l353 352z" />
+<glyph unicode="" d="M529 500h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-269l-103 -103l-170 170zM700 1200h-100v-200h100v200zM1167 6l-170 170l-170 -170l-127 127l170 170l-170 170l127 127l170 -170l170 170l127 -128 l-170 -169l170 -170z" />
+<glyph unicode="" d="M700 500h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-300h-400v-200zM700 1000h-100v200h100v-200zM1000 600h-200v-300h-200l300 -300l300 300h-200v300z" />
+<glyph unicode="" d="M602 500h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-402l-200 200zM700 1000h-100v200h100v-200zM1000 300h200l-300 300l-300 -300h200v-300h200v300z" />
+<glyph unicode="" d="M1200 900v150q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-150h1200zM0 800v-550q0 -21 14.5 -35.5t35.5 -14.5h1100q21 0 35.5 14.5t14.5 35.5v550h-1200zM100 500h400v-200h-400v200z" />
+<glyph unicode="" d="M500 1000h400v198l300 -298l-300 -298v198h-400v200zM100 800v200h100v-200h-100zM400 800h-100v200h100v-200zM700 300h-400v-198l-300 298l300 298v-198h400v-200zM800 500h100v-200h-100v200zM1000 500v-200h100v200h-100z" />
+<glyph unicode="" d="M1200 50v1106q0 31 -18 40.5t-44 -7.5l-276 -117q-25 -16 -43.5 -50.5t-18.5 -65.5v-359q0 -29 10.5 -55.5t25 -43t29 -28.5t25.5 -18l10 -5v-397q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5zM550 1200l50 -100v-400l-100 -203v-447q0 -21 -14.5 -35.5 t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447l-100 203v400l50 100l50 -100v-300h100v300l50 100l50 -100v-300h100v300z" />
+<glyph unicode="" d="M1100 106v888q0 22 25 34.5t50 13.5l25 2v56h-400v-56q75 0 87.5 -6t12.5 -44v-394h-500v394q0 38 12.5 44t87.5 6v56h-400v-56q4 0 11 -0.5t24 -3t30 -7t24 -15t11 -24.5v-888q0 -22 -25 -34.5t-50 -13.5l-25 -2v-56h400v56q-75 0 -87.5 6t-12.5 44v394h500v-394 q0 -38 -12.5 -44t-87.5 -6v-56h400v56q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5z" />
+<glyph unicode="" d="M675 1000l-100 100h-375l-100 -100h400l200 -200v-98l295 98h105v200h-425zM500 300v500q0 41 -29.5 70.5t-70.5 29.5h-300q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h300q41 0 70.5 29.5t29.5 70.5zM100 800h300v-200h-300v200zM700 565l400 133 v-163l-400 -133v163zM100 500h300v-200h-300v200zM805 300l295 98v-298h-425l-100 -100h-375l-100 100h400l200 200h105z" />
+<glyph unicode="" d="M179 1169l-162 -162q-1 -11 -0.5 -32.5t16 -90t46.5 -140t104 -177.5t175 -208q103 -103 207.5 -176t180 -103.5t137 -47t92.5 -16.5l31 1l163 162q16 17 13 40.5t-22 37.5l-192 136q-19 14 -45 12t-42 -19l-119 -118q-143 103 -267 227q-126 126 -227 268l118 118 q17 17 20 41.5t-11 44.5l-139 194q-14 19 -36.5 22t-40.5 -14z" />
+<glyph unicode="" d="M1200 712v200q-6 8 -19 20.5t-63 45t-112 57t-171 45t-235 20.5q-92 0 -175 -10.5t-141.5 -27t-108.5 -36.5t-81.5 -40t-53.5 -36.5t-31 -27.5l-9 -10v-200q0 -21 14.5 -33.5t34.5 -8.5l202 33q20 4 34.5 21t14.5 38v146q141 24 300 24t300 -24v-146q0 -21 14.5 -38 t34.5 -21l202 -33q20 -4 34.5 8.5t14.5 33.5zM800 650l365 -303q14 -14 24.5 -39.5t10.5 -45.5v-212q0 -21 -15 -35.5t-35 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v212q0 20 10.5 45.5t24.5 39.5l365 303v50q0 4 1 10.5t12 22.5t30 [...]
+<glyph unicode="" d="M175 200h950l-125 150v250l100 100v400h-100v-200h-100v200h-200v-200h-100v200h-200v-200h-100v200h-100v-400l100 -100v-250zM1200 100v-100h-1100v100h1100z" />
+<glyph unicode="" d="M600 1100h100q41 0 70.5 -29.5t29.5 -70.5v-1000h-300v1000q0 41 29.5 70.5t70.5 29.5zM1000 800h100q41 0 70.5 -29.5t29.5 -70.5v-700h-300v700q0 41 29.5 70.5t70.5 29.5zM400 0v400q0 41 -29.5 70.5t-70.5 29.5h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-400h300z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM200 800v-300h200v-100h-200v-100h300v300h-200v100h200v100h-300zM800 800h-200v-500h200v100h100v300h-100 v100zM800 700v-300h-100v300h100z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM400 600h-100v200h-100v-500h100v200h100v-200h100v500h-100v-200zM800 800h-200v-500h200v100h100v300h-100 v100zM800 700v-300h-100v300h100z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM200 800v-500h300v100h-200v300h200v100h-300zM600 800v-500h300v100h-200v300h200v100h-300z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM500 700l-300 -150l300 -150v300zM600 400l300 150l-300 150v-300z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM900 800v-500h-700v500h700zM300 400h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130v-300zM800 700h-130 q-38 0 -66.5 -43t-28.5 -108t27 -107t68 -42h130v300z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM200 800v-300h200v-100h-200v-100h300v300h-200v100h200v100h-300zM800 300h100v500h-200v-100h100v-400z M601 300h100v100h-100v-100z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM300 700v100h-100v-500h300v400h-200zM800 300h100v500h-200v-100h100v-400zM401 400h-100v200h100v-200z M601 300h100v100h-100v-100z" />
+<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM1000 900h-900v-700h900v700zM400 700h-200v100h300v-300h-99v-100h-100v100h99v200zM800 700h-100v100h200v-500h-100v400zM201 400h100v-100 h-100v100zM701 300h-100v100h100v-100z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM800 700h-300 v-200h300v-100h-300l-100 100v200l100 100h300v-100z" />
+<glyph unicode="" d="M596 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM596 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM800 700v-100 h-100v100h-200v-100h200v-100h-200v-100h-100v400h300zM800 400h-100v100h100v-100z" />
+<glyph unicode="" d="M800 300h128q120 0 205 86t85 208q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5q0 -80 56.5 -137t135.5 -57h222v300h400v-300zM700 200h200l-300 -300 l-300 300h200v300h200v-300z" />
+<glyph unicode="" d="M600 714l403 -403q94 26 154.5 104t60.5 178q0 121 -85 207.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5q0 -80 56.5 -137t135.5 -57h8zM700 -100h-200v300h-200l300 300 l300 -300h-200v-300z" />
+<glyph unicode="" d="M700 200h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170l-270 -300h400v-155l-75 -45h350l-75 45v155z" />
+<glyph unicode="" d="M700 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -12t1 -11q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5 q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350z" />
+<glyph unicode="💼" d="M800 1000h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5h200q41 0 70.5 -29.5t29.5 -70.5v-100zM500 1000h200v100h-200v-100zM1200 400v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v200h1200z" />
+<glyph unicode="📅" d="M1100 900v150q0 21 -14.5 35.5t-35.5 14.5h-150v100h-100v-100h-500v100h-100v-100h-150q-21 0 -35.5 -14.5t-14.5 -35.5v-150h1100zM0 800v-750q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v750h-1100zM100 600h100v-100h-100v100zM300 600h100v-100h-100v100z M500 600h100v-100h-100v100zM700 600h100v-100h-100v100zM900 600h100v-100h-100v100zM100 400h100v-100h-100v100zM300 400h100v-100h-100v100zM500 400h100v-100h-100v100zM700 400h100v-100h-100v100zM900 400h100v-100h-100 [...]
+<glyph unicode="📌" d="M902 1185l283 -282q15 -15 15 -36t-15 -35q-14 -15 -35 -15t-35 15l-36 35l-279 -267v-300l-212 210l-208 -207l-380 -303l303 380l207 208l-210 212h300l267 279l-35 36q-15 14 -15 35t15 35q14 15 35 15t35 -15z" />
+<glyph unicode="📎" d="M518 119l69 -60l517 511q67 67 95 157t11 183q-16 87 -67 154t-130 103q-69 33 -152 33q-107 0 -197 -55q-40 -24 -111 -95l-512 -512q-68 -68 -81 -163t35 -173q35 -57 94 -89t129 -32q63 0 119 28q33 16 65 40.5t52.5 45.5t59.5 64q40 44 57 61l394 394q35 35 47 84 t-3 96q-27 87 -117 104q-20 2 -29 2q-46 0 -79.5 -17t-67.5 -51l-388 -396l-7 -7l69 -67l377 373q20 22 39 38q23 23 50 23q38 0 53 -36q16 -39 -20 -75l-547 -547q-52 -52 -125 -52q-55 0 -100 33t-54 96q-5 35 2.5 66t31.5 63t4 [...]
+<glyph unicode="📷" d="M1200 200v600q0 41 -29.5 70.5t-70.5 29.5h-150q-4 8 -11.5 21.5t-33 48t-53 61t-69 48t-83.5 21.5h-200q-41 0 -82 -20.5t-70 -50t-52 -59t-34 -50.5l-12 -20h-150q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5z M1000 700h-100v100h100v-100zM844 500q0 -100 -72 -172t-172 -72t-172 72t-72 172t72 172t172 72t172 -72t72 -172zM706 500q0 44 -31 75t-75 31t-75 -31t-31 -75t31 -75t75 -31t75 31t31 75z" />
+<glyph unicode="🔒" d="M900 800h100q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-900q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5h100v200q0 82 59 141t141 59h300q82 0 141 -59t59 -141v-200zM400 800h300v150q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-150z" />
+<glyph unicode="🔔" d="M1062 400h17q20 0 33.5 -14.5t13.5 -35.5q0 -20 -13 -40t-31 -27q-22 -9 -63 -23t-167.5 -37t-251.5 -23t-245.5 20.5t-178.5 41.5l-58 20q-18 7 -31 27.5t-13 40.5q0 21 13.5 35.5t33.5 14.5h17l118 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3 32t29 13h94 q20 0 29 -10.5t3 -29.5l-18 -37q83 -19 144 -82.5t76 -140.5l63 -327zM600 104q-54 0 -103 6q12 -49 40 -79.5t63 -30.5t63 30.5t39 79.5q-48 -6 -102 -6z" />
+<glyph unicode="🔖" d="M200 0l450 444l450 -443v1150q0 20 -14.5 35t-35.5 15h-800q-21 0 -35.5 -15t-14.5 -35v-1151z" />
+<glyph unicode="🔥" d="M400 755q2 -12 8 -41.5t8 -43t6 -39.5t3.5 -39.5t-1 -33.5t-6 -31.5t-13.5 -24t-21 -20.5t-31 -12q-38 -10 -67 13t-40.5 61.5t-15 81.5t10.5 75q-52 -46 -83.5 -101t-39 -107t-7.5 -85t5 -63q9 -56 44 -119.5t105 -108.5q31 -21 64 -16t62 23.5t57 49.5t48 61.5t35 60.5 q32 66 39 184.5t-13 157.5q79 -80 122 -164t26 -184q-5 -33 -20.5 -69.5t-37.5 -80.5q-10 -19 -14.5 -29t-12 -26t-9 -23.5t-3 -19t2.5 -15.5t11 -9.5t19.5 -5t30.5 2.5t42 8q57 20 91 34t87.5 44.5t87 64t65.5 88.5t47 122q38 [...]
+<glyph unicode="🔧" d="M948 778l251 126q13 -175 -151 -267q-123 -70 -253 -23l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5t15 37.5l600 599q-33 101 6 201.5t135 154.5q164 92 306 -9l-259 -138z" />
+</font>
+</defs></svg>
\ No newline at end of file
diff --git a/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/fonts/glyphicons-halflings-regular.ttf b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/fonts/glyphicons-halflings-regular.ttf
new file mode 100644
index 0000000..be784dc
Binary files /dev/null and b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/fonts/glyphicons-halflings-regular.ttf differ
diff --git a/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/fonts/glyphicons-halflings-regular.woff b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/fonts/glyphicons-halflings-regular.woff
new file mode 100644
index 0000000..2cc3e48
Binary files /dev/null and b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/fonts/glyphicons-halflings-regular.woff differ
diff --git a/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/js/bootstrap.js b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/js/bootstrap.js
new file mode 100644
index 0000000..f2020c8
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/js/bootstrap.js
@@ -0,0 +1,1999 @@
+/**
+* bootstrap.js v3.0.0 by @fat and @mdo
+* Copyright 2013 Twitter Inc.
+* http://www.apache.org/licenses/LICENSE-2.0
+*/
+if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
+
+/* ========================================================================
+ * Bootstrap: transition.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#transitions
+ * ========================================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
+ // ============================================================
+
+ function transitionEnd() {
+ var el = document.createElement('bootstrap')
+
+ var transEndEventNames = {
+ 'WebkitTransition' : 'webkitTransitionEnd'
+ , 'MozTransition' : 'transitionend'
+ , 'OTransition' : 'oTransitionEnd otransitionend'
+ , 'transition' : 'transitionend'
+ }
+
+ for (var name in transEndEventNames) {
+ if (el.style[name] !== undefined) {
+ return { end: transEndEventNames[name] }
+ }
+ }
+ }
+
+ // http://blog.alexmaccaw.com/css-transitions
+ $.fn.emulateTransitionEnd = function (duration) {
+ var called = false, $el = this
+ $(this).one($.support.transition.end, function () { called = true })
+ var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
+ setTimeout(callback, duration)
+ return this
+ }
+
+ $(function () {
+ $.support.transition = transitionEnd()
+ })
+
+}(window.$jqTheme || window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: alert.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#alerts
+ * ========================================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // ALERT CLASS DEFINITION
+ // ======================
+
+ var dismiss = '[data-dismiss="alert"]'
+ var Alert = function (el) {
+ $(el).on('click', dismiss, this.close)
+ }
+
+ Alert.prototype.close = function (e) {
+ var $this = $(this)
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+ }
+
+ var $parent = $(selector)
+
+ if (e) e.preventDefault()
+
+ if (!$parent.length) {
+ $parent = $this.hasClass('alert') ? $this : $this.parent()
+ }
+
+ $parent.trigger(e = $.Event('close.bs.alert'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent.removeClass('in')
+
+ function removeElement() {
+ $parent.trigger('closed.bs.alert').remove()
+ }
+
+ $.support.transition && $parent.hasClass('fade') ?
+ $parent
+ .one($.support.transition.end, removeElement)
+ .emulateTransitionEnd(150) :
+ removeElement()
+ }
+
+
+ // ALERT PLUGIN DEFINITION
+ // =======================
+
+ var old = $.fn.alert
+
+ $.fn.alert = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.alert')
+
+ if (!data) $this.data('bs.alert', (data = new Alert(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.alert.Constructor = Alert
+
+
+ // ALERT NO CONFLICT
+ // =================
+
+ $.fn.alert.noConflict = function () {
+ $.fn.alert = old
+ return this
+ }
+
+
+ // ALERT DATA-API
+ // ==============
+
+ $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
+
+}(window.$jqTheme || window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: button.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#buttons
+ * ========================================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // BUTTON PUBLIC CLASS DEFINITION
+ // ==============================
+
+ var Button = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, Button.DEFAULTS, options)
+ }
+
+ Button.DEFAULTS = {
+ loadingText: 'loading...'
+ }
+
+ Button.prototype.setState = function (state) {
+ var d = 'disabled'
+ var $el = this.$element
+ var val = $el.is('input') ? 'val' : 'html'
+ var data = $el.data()
+
+ state = state + 'Text'
+
+ if (!data.resetText) $el.data('resetText', $el[val]())
+
+ $el[val](data[state] || this.options[state])
+
+ // push to event loop to allow forms to submit
+ setTimeout(function () {
+ state == 'loadingText' ?
+ $el.addClass(d).attr(d, d) :
+ $el.removeClass(d).removeAttr(d);
+ }, 0)
+ }
+
+ Button.prototype.toggle = function () {
+ var $parent = this.$element.closest('[data-toggle="buttons"]')
+
+ if ($parent.length) {
+ var $input = this.$element.find('input')
+ .prop('checked', !this.$element.hasClass('active'))
+ .trigger('change')
+ if ($input.prop('type') === 'radio') $parent.find('.active').removeClass('active')
+ }
+
+ this.$element.toggleClass('active')
+ }
+
+
+ // BUTTON PLUGIN DEFINITION
+ // ========================
+
+ var old = $.fn.button
+
+ $.fn.button = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.button')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.button', (data = new Button(this, options)))
+
+ if (option == 'toggle') data.toggle()
+ else if (option) data.setState(option)
+ })
+ }
+
+ $.fn.button.Constructor = Button
+
+
+ // BUTTON NO CONFLICT
+ // ==================
+
+ $.fn.button.noConflict = function () {
+ $.fn.button = old
+ return this
+ }
+
+
+ // BUTTON DATA-API
+ // ===============
+
+ $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {
+ var $btn = $(e.target)
+ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+ $btn.button('toggle')
+ e.preventDefault()
+ })
+
+}(window.$jqTheme || window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: carousel.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#carousel
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // CAROUSEL CLASS DEFINITION
+ // =========================
+
+ var Carousel = function (element, options) {
+ this.$element = $(element)
+ this.$indicators = this.$element.find('.carousel-indicators')
+ this.options = options
+ this.paused =
+ this.sliding =
+ this.interval =
+ this.$active =
+ this.$items = null
+
+ this.options.pause == 'hover' && this.$element
+ .on('mouseenter', $.proxy(this.pause, this))
+ .on('mouseleave', $.proxy(this.cycle, this))
+ }
+
+ Carousel.DEFAULTS = {
+ interval: 5000
+ , pause: 'hover'
+ , wrap: true
+ }
+
+ Carousel.prototype.cycle = function (e) {
+ e || (this.paused = false)
+
+ this.interval && clearInterval(this.interval)
+
+ this.options.interval
+ && !this.paused
+ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+
+ return this
+ }
+
+ Carousel.prototype.getActiveIndex = function () {
+ this.$active = this.$element.find('.item.active')
+ this.$items = this.$active.parent().children()
+
+ return this.$items.index(this.$active)
+ }
+
+ Carousel.prototype.to = function (pos) {
+ var that = this
+ var activeIndex = this.getActiveIndex()
+
+ if (pos > (this.$items.length - 1) || pos < 0) return
+
+ if (this.sliding) return this.$element.one('slid', function () { that.to(pos) })
+ if (activeIndex == pos) return this.pause().cycle()
+
+ return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
+ }
+
+ Carousel.prototype.pause = function (e) {
+ e || (this.paused = true)
+
+ if (this.$element.find('.next, .prev').length && $.support.transition.end) {
+ this.$element.trigger($.support.transition.end)
+ this.cycle(true)
+ }
+
+ this.interval = clearInterval(this.interval)
+
+ return this
+ }
+
+ Carousel.prototype.next = function () {
+ if (this.sliding) return
+ return this.slide('next')
+ }
+
+ Carousel.prototype.prev = function () {
+ if (this.sliding) return
+ return this.slide('prev')
+ }
+
+ Carousel.prototype.slide = function (type, next) {
+ var $active = this.$element.find('.item.active')
+ var $next = next || $active[type]()
+ var isCycling = this.interval
+ var direction = type == 'next' ? 'left' : 'right'
+ var fallback = type == 'next' ? 'first' : 'last'
+ var that = this
+
+ if (!$next.length) {
+ if (!this.options.wrap) return
+ $next = this.$element.find('.item')[fallback]()
+ }
+
+ this.sliding = true
+
+ isCycling && this.pause()
+
+ var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction })
+
+ if ($next.hasClass('active')) return
+
+ if (this.$indicators.length) {
+ this.$indicators.find('.active').removeClass('active')
+ this.$element.one('slid', function () {
+ var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
+ $nextIndicator && $nextIndicator.addClass('active')
+ })
+ }
+
+ if ($.support.transition && this.$element.hasClass('slide')) {
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+ $next.addClass(type)
+ $next[0].offsetWidth // force reflow
+ $active.addClass(direction)
+ $next.addClass(direction)
+ $active
+ .one($.support.transition.end, function () {
+ $next.removeClass([type, direction].join(' ')).addClass('active')
+ $active.removeClass(['active', direction].join(' '))
+ that.sliding = false
+ setTimeout(function () { that.$element.trigger('slid') }, 0)
+ })
+ .emulateTransitionEnd(600)
+ } else {
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+ $active.removeClass('active')
+ $next.addClass('active')
+ this.sliding = false
+ this.$element.trigger('slid')
+ }
+
+ isCycling && this.cycle()
+
+ return this
+ }
+
+
+ // CAROUSEL PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.carousel
+
+ $.fn.carousel = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.carousel')
+ var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
+ var action = typeof option == 'string' ? option : options.slide
+
+ if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
+ if (typeof option == 'number') data.to(option)
+ else if (action) data[action]()
+ else if (options.interval) data.pause().cycle()
+ })
+ }
+
+ $.fn.carousel.Constructor = Carousel
+
+
+ // CAROUSEL NO CONFLICT
+ // ====================
+
+ $.fn.carousel.noConflict = function () {
+ $.fn.carousel = old
+ return this
+ }
+
+
+ // CAROUSEL DATA-API
+ // =================
+
+ $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
+ var $this = $(this), href
+ var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ var options = $.extend({}, $target.data(), $this.data())
+ var slideIndex = $this.attr('data-slide-to')
+ if (slideIndex) options.interval = false
+
+ $target.carousel(options)
+
+ if (slideIndex = $this.attr('data-slide-to')) {
+ $target.data('bs.carousel').to(slideIndex)
+ }
+
+ e.preventDefault()
+ })
+
+ $(window).on('load', function () {
+ $('[data-ride="carousel"]').each(function () {
+ var $carousel = $(this)
+ $carousel.carousel($carousel.data())
+ })
+ })
+
+}(window.$jqTheme || window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: collapse.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#collapse
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // COLLAPSE PUBLIC CLASS DEFINITION
+ // ================================
+
+ var Collapse = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, Collapse.DEFAULTS, options)
+ this.transitioning = null
+
+ if (this.options.parent) this.$parent = $(this.options.parent)
+ if (this.options.toggle) this.toggle()
+ }
+
+ Collapse.DEFAULTS = {
+ toggle: true
+ }
+
+ Collapse.prototype.dimension = function () {
+ var hasWidth = this.$element.hasClass('width')
+ return hasWidth ? 'width' : 'height'
+ }
+
+ Collapse.prototype.show = function () {
+ if (this.transitioning || this.$element.hasClass('in')) return
+
+ var startEvent = $.Event('show.bs.collapse')
+ this.$element.trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
+
+ var actives = this.$parent && this.$parent.find('> .panel > .in')
+
+ if (actives && actives.length) {
+ var hasData = actives.data('bs.collapse')
+ if (hasData && hasData.transitioning) return
+ actives.collapse('hide')
+ hasData || actives.data('bs.collapse', null)
+ }
+
+ var dimension = this.dimension()
+
+ this.$element
+ .removeClass('collapse')
+ .addClass('collapsing')
+ [dimension](0)
+
+ this.transitioning = 1
+
+ var complete = function () {
+ this.$element
+ .removeClass('collapsing')
+ .addClass('in')
+ [dimension]('auto')
+ this.transitioning = 0
+ this.$element.trigger('shown.bs.collapse')
+ }
+
+ if (!$.support.transition) return complete.call(this)
+
+ var scrollSize = $.camelCase(['scroll', dimension].join('-'))
+
+ this.$element
+ .one($.support.transition.end, $.proxy(complete, this))
+ .emulateTransitionEnd(350)
+ [dimension](this.$element[0][scrollSize])
+ }
+
+ Collapse.prototype.hide = function () {
+ if (this.transitioning || !this.$element.hasClass('in')) return
+
+ var startEvent = $.Event('hide.bs.collapse')
+ this.$element.trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
+
+ var dimension = this.dimension()
+
+ this.$element
+ [dimension](this.$element[dimension]())
+ [0].offsetHeight
+
+ this.$element
+ .addClass('collapsing')
+ .removeClass('collapse')
+ .removeClass('in')
+
+ this.transitioning = 1
+
+ var complete = function () {
+ this.transitioning = 0
+ this.$element
+ .trigger('hidden.bs.collapse')
+ .removeClass('collapsing')
+ .addClass('collapse')
+ }
+
+ if (!$.support.transition) return complete.call(this)
+
+ this.$element
+ [dimension](0)
+ .one($.support.transition.end, $.proxy(complete, this))
+ .emulateTransitionEnd(350)
+ }
+
+ Collapse.prototype.toggle = function () {
+ this[this.$element.hasClass('in') ? 'hide' : 'show']()
+ }
+
+
+ // COLLAPSE PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.collapse
+
+ $.fn.collapse = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.collapse')
+ var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+ if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.collapse.Constructor = Collapse
+
+
+ // COLLAPSE NO CONFLICT
+ // ====================
+
+ $.fn.collapse.noConflict = function () {
+ $.fn.collapse = old
+ return this
+ }
+
+
+ // COLLAPSE DATA-API
+ // =================
+
+ $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) {
+ var $this = $(this), href
+ var target = $this.attr('data-target')
+ || e.preventDefault()
+ || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
+ var $target = $(target)
+ var data = $target.data('bs.collapse')
+ var option = data ? 'toggle' : $this.data()
+ var parent = $this.attr('data-parent')
+ var $parent = parent && $(parent)
+
+ if (!data || !data.transitioning) {
+ if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed')
+ $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
+ }
+
+ $target.collapse(option)
+ })
+
+}(window.$jqTheme || window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#dropdowns
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle=dropdown]'
+ var Dropdown = function (element) {
+ var $el = $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we we use a backdrop because click events don't delegate
+ $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ $parent.trigger(e = $.Event('show.bs.dropdown'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown')
+
+ $this.focus()
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ var $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+ if (!$items.length) return
+
+ var index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index=0
+
+ $items.eq(index).focus()
+ }
+
+ function clearMenus() {
+ $(backdrop).remove()
+ $(toggle).each(function (e) {
+ var $parent = getParent($(this))
+ if (!$parent.hasClass('open')) return
+ $parent.trigger(e = $.Event('hide.bs.dropdown'))
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('open').trigger('hidden.bs.dropdown')
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('dropdown')
+
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.$jqTheme || window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: modal.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#modals
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // MODAL CLASS DEFINITION
+ // ======================
+
+ var Modal = function (element, options) {
+ this.options = options
+ this.$element = $(element)
+ this.$backdrop =
+ this.isShown = null
+
+ if (this.options.remote) this.$element.load(this.options.remote)
+ }
+
+ Modal.DEFAULTS = {
+ backdrop: true
+ , keyboard: true
+ , show: true
+ }
+
+ Modal.prototype.toggle = function (_relatedTarget) {
+ return this[!this.isShown ? 'show' : 'hide'](_relatedTarget)
+ }
+
+ Modal.prototype.show = function (_relatedTarget) {
+ var that = this
+ var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
+
+ this.$element.trigger(e)
+
+ if (this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = true
+
+ this.escape()
+
+ this.$element.on('click.dismiss.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
+
+ this.backdrop(function () {
+ var transition = $.support.transition && that.$element.hasClass('fade')
+
+ if (!that.$element.parent().length) {
+ that.$element.appendTo(document.body) // don't move modals dom position
+ }
+
+ that.$element.show()
+
+ if (transition) {
+ that.$element[0].offsetWidth // force reflow
+ }
+
+ that.$element
+ .addClass('in')
+ .attr('aria-hidden', false)
+
+ that.enforceFocus()
+
+ var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
+
+ transition ?
+ that.$element.find('.modal-dialog') // wait for modal to slide in
+ .one($.support.transition.end, function () {
+ that.$element.focus().trigger(e)
+ })
+ .emulateTransitionEnd(300) :
+ that.$element.focus().trigger(e)
+ })
+ }
+
+ Modal.prototype.hide = function (e) {
+ if (e) e.preventDefault()
+
+ e = $.Event('hide.bs.modal')
+
+ this.$element.trigger(e)
+
+ if (!this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = false
+
+ this.escape()
+
+ $(document).off('focusin.bs.modal')
+
+ this.$element
+ .removeClass('in')
+ .attr('aria-hidden', true)
+ .off('click.dismiss.modal')
+
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.$element
+ .one($.support.transition.end, $.proxy(this.hideModal, this))
+ .emulateTransitionEnd(300) :
+ this.hideModal()
+ }
+
+ Modal.prototype.enforceFocus = function () {
+ $(document)
+ .off('focusin.bs.modal') // guard against infinite focus loop
+ .on('focusin.bs.modal', $.proxy(function (e) {
+ if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
+ this.$element.focus()
+ }
+ }, this))
+ }
+
+ Modal.prototype.escape = function () {
+ if (this.isShown && this.options.keyboard) {
+ this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
+ e.which == 27 && this.hide()
+ }, this))
+ } else if (!this.isShown) {
+ this.$element.off('keyup.dismiss.bs.modal')
+ }
+ }
+
+ Modal.prototype.hideModal = function () {
+ var that = this
+ this.$element.hide()
+ this.backdrop(function () {
+ that.removeBackdrop()
+ that.$element.trigger('hidden.bs.modal')
+ })
+ }
+
+ Modal.prototype.removeBackdrop = function () {
+ this.$backdrop && this.$backdrop.remove()
+ this.$backdrop = null
+ }
+
+ Modal.prototype.backdrop = function (callback) {
+ var that = this
+ var animate = this.$element.hasClass('fade') ? 'fade' : ''
+
+ if (this.isShown && this.options.backdrop) {
+ var doAnimate = $.support.transition && animate
+
+ this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
+ .appendTo(document.body)
+
+ this.$element.on('click.dismiss.modal', $.proxy(function (e) {
+ if (e.target !== e.currentTarget) return
+ this.options.backdrop == 'static'
+ ? this.$element[0].focus.call(this.$element[0])
+ : this.hide.call(this)
+ }, this))
+
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+
+ this.$backdrop.addClass('in')
+
+ if (!callback) return
+
+ doAnimate ?
+ this.$backdrop
+ .one($.support.transition.end, callback)
+ .emulateTransitionEnd(150) :
+ callback()
+
+ } else if (!this.isShown && this.$backdrop) {
+ this.$backdrop.removeClass('in')
+
+ $.support.transition && this.$element.hasClass('fade')?
+ this.$backdrop
+ .one($.support.transition.end, callback)
+ .emulateTransitionEnd(150) :
+ callback()
+
+ } else if (callback) {
+ callback()
+ }
+ }
+
+
+ // MODAL PLUGIN DEFINITION
+ // =======================
+
+ var old = $.fn.modal
+
+ $.fn.modal = function (option, _relatedTarget) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.modal')
+ var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+ if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
+ if (typeof option == 'string') data[option](_relatedTarget)
+ else if (options.show) data.show(_relatedTarget)
+ })
+ }
+
+ $.fn.modal.Constructor = Modal
+
+
+ // MODAL NO CONFLICT
+ // =================
+
+ $.fn.modal.noConflict = function () {
+ $.fn.modal = old
+ return this
+ }
+
+
+ // MODAL DATA-API
+ // ==============
+
+ $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
+ var $this = $(this)
+ var href = $this.attr('href')
+ var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
+ var option = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
+
+ e.preventDefault()
+
+ $target
+ .modal(option, this)
+ .one('hide', function () {
+ $this.is(':visible') && $this.focus()
+ })
+ })
+
+ $(document)
+ .on('show.bs.modal', '.modal', function () { $(document.body).addClass('modal-open') })
+ .on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })
+
+}(window.$jqTheme || window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: tooltip.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#tooltip
+ * Inspired by the original jQuery.tipsy by Jason Frame
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // TOOLTIP PUBLIC CLASS DEFINITION
+ // ===============================
+
+ var Tooltip = function (element, options) {
+ this.type =
+ this.options =
+ this.enabled =
+ this.timeout =
+ this.hoverState =
+ this.$element = null
+
+ this.init('tooltip', element, options)
+ }
+
+ Tooltip.DEFAULTS = {
+ animation: true
+ , placement: 'top'
+ , selector: false
+ , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
+ , trigger: 'hover focus'
+ , title: ''
+ , delay: 0
+ , html: false
+ , container: false
+ }
+
+ Tooltip.prototype.init = function (type, element, options) {
+ this.enabled = true
+ this.type = type
+ this.$element = $(element)
+ this.options = this.getOptions(options)
+
+ var triggers = this.options.trigger.split(' ')
+
+ for (var i = triggers.length; i--;) {
+ var trigger = triggers[i]
+
+ if (trigger == 'click') {
+ this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
+ } else if (trigger != 'manual') {
+ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
+ var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
+
+ this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
+ this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+ }
+ }
+
+ this.options.selector ?
+ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+ this.fixTitle()
+ }
+
+ Tooltip.prototype.getDefaults = function () {
+ return Tooltip.DEFAULTS
+ }
+
+ Tooltip.prototype.getOptions = function (options) {
+ options = $.extend({}, this.getDefaults(), this.$element.data(), options)
+
+ if (options.delay && typeof options.delay == 'number') {
+ options.delay = {
+ show: options.delay
+ , hide: options.delay
+ }
+ }
+
+ return options
+ }
+
+ Tooltip.prototype.getDelegateOptions = function () {
+ var options = {}
+ var defaults = this.getDefaults()
+
+ this._options && $.each(this._options, function (key, value) {
+ if (defaults[key] != value) options[key] = value
+ })
+
+ return options
+ }
+
+ Tooltip.prototype.enter = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
+
+ clearTimeout(self.timeout)
+
+ self.hoverState = 'in'
+
+ if (!self.options.delay || !self.options.delay.show) return self.show()
+
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'in') self.show()
+ }, self.options.delay.show)
+ }
+
+ Tooltip.prototype.leave = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
+
+ clearTimeout(self.timeout)
+
+ self.hoverState = 'out'
+
+ if (!self.options.delay || !self.options.delay.hide) return self.hide()
+
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'out') self.hide()
+ }, self.options.delay.hide)
+ }
+
+ Tooltip.prototype.show = function () {
+ var e = $.Event('show.bs.'+ this.type)
+
+ if (this.hasContent() && this.enabled) {
+ this.$element.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ var $tip = this.tip()
+
+ this.setContent()
+
+ if (this.options.animation) $tip.addClass('fade')
+
+ var placement = typeof this.options.placement == 'function' ?
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
+ this.options.placement
+
+ var autoToken = /\s?auto?\s?/i
+ var autoPlace = autoToken.test(placement)
+ if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
+
+ $tip
+ .detach()
+ .css({ top: 0, left: 0, display: 'block' })
+ .addClass(placement)
+
+ this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+
+ var pos = this.getPosition()
+ var actualWidth = $tip[0].offsetWidth
+ var actualHeight = $tip[0].offsetHeight
+
+ if (autoPlace) {
+ var $parent = this.$element.parent()
+
+ var orgPlacement = placement
+ var docScroll = document.documentElement.scrollTop || document.body.scrollTop
+ var parentWidth = this.options.container == 'body' ? window.innerWidth : $parent.outerWidth()
+ var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight()
+ var parentLeft = this.options.container == 'body' ? 0 : $parent.offset().left
+
+ placement = placement == 'bottom' && pos.top + pos.height + actualHeight - docScroll > parentHeight ? 'top' :
+ placement == 'top' && pos.top - docScroll - actualHeight < 0 ? 'bottom' :
+ placement == 'right' && pos.right + actualWidth > parentWidth ? 'left' :
+ placement == 'left' && pos.left - actualWidth < parentLeft ? 'right' :
+ placement
+
+ $tip
+ .removeClass(orgPlacement)
+ .addClass(placement)
+ }
+
+ var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
+
+ this.applyPlacement(calculatedOffset, placement)
+ this.$element.trigger('shown.bs.' + this.type)
+ }
+ }
+
+ Tooltip.prototype.applyPlacement = function(offset, placement) {
+ var replace
+ var $tip = this.tip()
+ var width = $tip[0].offsetWidth
+ var height = $tip[0].offsetHeight
+
+ // manually read margins because getBoundingClientRect includes difference
+ var marginTop = parseInt($tip.css('margin-top'), 10)
+ var marginLeft = parseInt($tip.css('margin-left'), 10)
+
+ // we must check for NaN for ie 8/9
+ if (isNaN(marginTop)) marginTop = 0
+ if (isNaN(marginLeft)) marginLeft = 0
+
+ offset.top = offset.top + marginTop
+ offset.left = offset.left + marginLeft
+
+ $tip
+ .offset(offset)
+ .addClass('in')
+
+ // check to see if placing tip in new offset caused the tip to resize itself
+ var actualWidth = $tip[0].offsetWidth
+ var actualHeight = $tip[0].offsetHeight
+
+ if (placement == 'top' && actualHeight != height) {
+ replace = true
+ offset.top = offset.top + height - actualHeight
+ }
+
+ if (/bottom|top/.test(placement)) {
+ var delta = 0
+
+ if (offset.left < 0) {
+ delta = offset.left * -2
+ offset.left = 0
+
+ $tip.offset(offset)
+
+ actualWidth = $tip[0].offsetWidth
+ actualHeight = $tip[0].offsetHeight
+ }
+
+ this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
+ } else {
+ this.replaceArrow(actualHeight - height, actualHeight, 'top')
+ }
+
+ if (replace) $tip.offset(offset)
+ }
+
+ Tooltip.prototype.replaceArrow = function(delta, dimension, position) {
+ this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
+ }
+
+ Tooltip.prototype.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
+
+ $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
+ $tip.removeClass('fade in top bottom left right')
+ }
+
+ Tooltip.prototype.hide = function () {
+ var that = this
+ var $tip = this.tip()
+ var e = $.Event('hide.bs.' + this.type)
+
+ function complete() {
+ if (that.hoverState != 'in') $tip.detach()
+ }
+
+ this.$element.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ $tip.removeClass('in')
+
+ $.support.transition && this.$tip.hasClass('fade') ?
+ $tip
+ .one($.support.transition.end, complete)
+ .emulateTransitionEnd(150) :
+ complete()
+
+ this.$element.trigger('hidden.bs.' + this.type)
+
+ return this
+ }
+
+ Tooltip.prototype.fixTitle = function () {
+ var $e = this.$element
+ if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
+ $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
+ }
+ }
+
+ Tooltip.prototype.hasContent = function () {
+ return this.getTitle()
+ }
+
+ Tooltip.prototype.getPosition = function () {
+ var el = this.$element[0]
+ return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
+ width: el.offsetWidth
+ , height: el.offsetHeight
+ }, this.$element.offset())
+ }
+
+ Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
+ return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
+ placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
+ placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
+ /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
+ }
+
+ Tooltip.prototype.getTitle = function () {
+ var title
+ var $e = this.$element
+ var o = this.options
+
+ title = $e.attr('data-original-title')
+ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
+
+ return title
+ }
+
+ Tooltip.prototype.tip = function () {
+ return this.$tip = this.$tip || $(this.options.template)
+ }
+
+ Tooltip.prototype.arrow = function () {
+ return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
+ }
+
+ Tooltip.prototype.validate = function () {
+ if (!this.$element[0].parentNode) {
+ this.hide()
+ this.$element = null
+ this.options = null
+ }
+ }
+
+ Tooltip.prototype.enable = function () {
+ this.enabled = true
+ }
+
+ Tooltip.prototype.disable = function () {
+ this.enabled = false
+ }
+
+ Tooltip.prototype.toggleEnabled = function () {
+ this.enabled = !this.enabled
+ }
+
+ Tooltip.prototype.toggle = function (e) {
+ var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
+ self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
+ }
+
+ Tooltip.prototype.destroy = function () {
+ this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
+ }
+
+
+ // TOOLTIP PLUGIN DEFINITION
+ // =========================
+
+ var old = $.fn.tooltip
+
+ $.fn.tooltip = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.tooltip')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.tooltip.Constructor = Tooltip
+
+
+ // TOOLTIP NO CONFLICT
+ // ===================
+
+ $.fn.tooltip.noConflict = function () {
+ $.fn.tooltip = old
+ return this
+ }
+
+}(window.$jqTheme || window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: popover.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#popovers
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // POPOVER PUBLIC CLASS DEFINITION
+ // ===============================
+
+ var Popover = function (element, options) {
+ this.init('popover', element, options)
+ }
+
+ if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
+
+ Popover.DEFAULTS = $.extend({} , $.fn.tooltip.Constructor.DEFAULTS, {
+ placement: 'right'
+ , trigger: 'click'
+ , content: ''
+ , template: '<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
+ })
+
+
+ // NOTE: POPOVER EXTENDS tooltip.js
+ // ================================
+
+ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
+
+ Popover.prototype.constructor = Popover
+
+ Popover.prototype.getDefaults = function () {
+ return Popover.DEFAULTS
+ }
+
+ Popover.prototype.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
+ var content = this.getContent()
+
+ $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
+ $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)
+
+ $tip.removeClass('fade top bottom left right in')
+
+ // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
+ // this manually by checking the contents.
+ if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
+ }
+
+ Popover.prototype.hasContent = function () {
+ return this.getTitle() || this.getContent()
+ }
+
+ Popover.prototype.getContent = function () {
+ var $e = this.$element
+ var o = this.options
+
+ return $e.attr('data-content')
+ || (typeof o.content == 'function' ?
+ o.content.call($e[0]) :
+ o.content)
+ }
+
+ Popover.prototype.arrow = function () {
+ return this.$arrow = this.$arrow || this.tip().find('.arrow')
+ }
+
+ Popover.prototype.tip = function () {
+ if (!this.$tip) this.$tip = $(this.options.template)
+ return this.$tip
+ }
+
+
+ // POPOVER PLUGIN DEFINITION
+ // =========================
+
+ var old = $.fn.popover
+
+ $.fn.popover = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.popover')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.popover.Constructor = Popover
+
+
+ // POPOVER NO CONFLICT
+ // ===================
+
+ $.fn.popover.noConflict = function () {
+ $.fn.popover = old
+ return this
+ }
+
+}(window.$jqTheme || window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: scrollspy.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#scrollspy
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // SCROLLSPY CLASS DEFINITION
+ // ==========================
+
+ function ScrollSpy(element, options) {
+ var href
+ var process = $.proxy(this.process, this)
+
+ this.$element = $(element).is('body') ? $(window) : $(element)
+ this.$body = $('body')
+ this.$scrollElement = this.$element.on('scroll.bs.scroll-spy.data-api', process)
+ this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
+ this.selector = (this.options.target
+ || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ || '') + ' .nav li > a'
+ this.offsets = $([])
+ this.targets = $([])
+ this.activeTarget = null
+
+ this.refresh()
+ this.process()
+ }
+
+ ScrollSpy.DEFAULTS = {
+ offset: 10
+ }
+
+ ScrollSpy.prototype.refresh = function () {
+ var offsetMethod = this.$element[0] == window ? 'offset' : 'position'
+
+ this.offsets = $([])
+ this.targets = $([])
+
+ var self = this
+ var $targets = this.$body
+ .find(this.selector)
+ .map(function () {
+ var $el = $(this)
+ var href = $el.data('target') || $el.attr('href')
+ var $href = /^#\w/.test(href) && $(href)
+
+ return ($href
+ && $href.length
+ && [[ $href[offsetMethod]().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]]) || null
+ })
+ .sort(function (a, b) { return a[0] - b[0] })
+ .each(function () {
+ self.offsets.push(this[0])
+ self.targets.push(this[1])
+ })
+ }
+
+ ScrollSpy.prototype.process = function () {
+ var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
+ var scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
+ var maxScroll = scrollHeight - this.$scrollElement.height()
+ var offsets = this.offsets
+ var targets = this.targets
+ var activeTarget = this.activeTarget
+ var i
+
+ if (scrollTop >= maxScroll) {
+ return activeTarget != (i = targets.last()[0]) && this.activate(i)
+ }
+
+ for (i = offsets.length; i--;) {
+ activeTarget != targets[i]
+ && scrollTop >= offsets[i]
+ && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
+ && this.activate( targets[i] )
+ }
+ }
+
+ ScrollSpy.prototype.activate = function (target) {
+ this.activeTarget = target
+
+ $(this.selector)
+ .parents('.active')
+ .removeClass('active')
+
+ var selector = this.selector
+ + '[data-target="' + target + '"],'
+ + this.selector + '[href="' + target + '"]'
+
+ var active = $(selector)
+ .parents('li')
+ .addClass('active')
+
+ if (active.parent('.dropdown-menu').length) {
+ active = active
+ .closest('li.dropdown')
+ .addClass('active')
+ }
+
+ active.trigger('activate')
+ }
+
+
+ // SCROLLSPY PLUGIN DEFINITION
+ // ===========================
+
+ var old = $.fn.scrollspy
+
+ $.fn.scrollspy = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.scrollspy')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.scrollspy.Constructor = ScrollSpy
+
+
+ // SCROLLSPY NO CONFLICT
+ // =====================
+
+ $.fn.scrollspy.noConflict = function () {
+ $.fn.scrollspy = old
+ return this
+ }
+
+
+ // SCROLLSPY DATA-API
+ // ==================
+
+ $(window).on('load', function () {
+ $('[data-spy="scroll"]').each(function () {
+ var $spy = $(this)
+ $spy.scrollspy($spy.data())
+ })
+ })
+
+}(window.$jqTheme || window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: tab.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#tabs
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // TAB CLASS DEFINITION
+ // ====================
+
+ var Tab = function (element) {
+ this.element = $(element)
+ }
+
+ Tab.prototype.show = function () {
+ var $this = this.element
+ var $ul = $this.closest('ul:not(.dropdown-menu)')
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ if ($this.parent('li').hasClass('active')) return
+
+ var previous = $ul.find('.active:last a')[0]
+ var e = $.Event('show.bs.tab', {
+ relatedTarget: previous
+ })
+
+ $this.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ var $target = $(selector)
+
+ this.activate($this.parent('li'), $ul)
+ this.activate($target, $target.parent(), function () {
+ $this.trigger({
+ type: 'shown.bs.tab'
+ , relatedTarget: previous
+ })
+ })
+ }
+
+ Tab.prototype.activate = function (element, container, callback) {
+ var $active = container.find('> .active')
+ var transition = callback
+ && $.support.transition
+ && $active.hasClass('fade')
+
+ function next() {
+ $active
+ .removeClass('active')
+ .find('> .dropdown-menu > .active')
+ .removeClass('active')
+
+ element.addClass('active')
+
+ if (transition) {
+ element[0].offsetWidth // reflow for transition
+ element.addClass('in')
+ } else {
+ element.removeClass('fade')
+ }
+
+ if (element.parent('.dropdown-menu')) {
+ element.closest('li.dropdown').addClass('active')
+ }
+
+ callback && callback()
+ }
+
+ transition ?
+ $active
+ .one($.support.transition.end, next)
+ .emulateTransitionEnd(150) :
+ next()
+
+ $active.removeClass('in')
+ }
+
+
+ // TAB PLUGIN DEFINITION
+ // =====================
+
+ var old = $.fn.tab
+
+ $.fn.tab = function ( option ) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.tab')
+
+ if (!data) $this.data('bs.tab', (data = new Tab(this)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.tab.Constructor = Tab
+
+
+ // TAB NO CONFLICT
+ // ===============
+
+ $.fn.tab.noConflict = function () {
+ $.fn.tab = old
+ return this
+ }
+
+
+ // TAB DATA-API
+ // ============
+
+ $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
+ e.preventDefault()
+ $(this).tab('show')
+ })
+
+}(window.$jqTheme || window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: affix.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#affix
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // AFFIX CLASS DEFINITION
+ // ======================
+
+ var Affix = function (element, options) {
+ this.options = $.extend({}, Affix.DEFAULTS, options)
+ this.$window = $(window)
+ .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
+ .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
+
+ this.$element = $(element)
+ this.affixed =
+ this.unpin = null
+
+ this.checkPosition()
+ }
+
+ Affix.RESET = 'affix affix-top affix-bottom'
+
+ Affix.DEFAULTS = {
+ offset: 0
+ }
+
+ Affix.prototype.checkPositionWithEventLoop = function () {
+ setTimeout($.proxy(this.checkPosition, this), 1)
+ }
+
+ Affix.prototype.checkPosition = function () {
+ if (!this.$element.is(':visible')) return
+
+ var scrollHeight = $(document).height()
+ var scrollTop = this.$window.scrollTop()
+ var position = this.$element.offset()
+ var offset = this.options.offset
+ var offsetTop = offset.top
+ var offsetBottom = offset.bottom
+
+ if (typeof offset != 'object') offsetBottom = offsetTop = offset
+ if (typeof offsetTop == 'function') offsetTop = offset.top()
+ if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
+
+ var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false :
+ offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
+ offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false
+
+ if (this.affixed === affix) return
+ if (this.unpin) this.$element.css('top', '')
+
+ this.affixed = affix
+ this.unpin = affix == 'bottom' ? position.top - scrollTop : null
+
+ this.$element.removeClass(Affix.RESET).addClass('affix' + (affix ? '-' + affix : ''))
+
+ if (affix == 'bottom') {
+ this.$element.offset({ top: document.body.offsetHeight - offsetBottom - this.$element.height() })
+ }
+ }
+
+
+ // AFFIX PLUGIN DEFINITION
+ // =======================
+
+ var old = $.fn.affix
+
+ $.fn.affix = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.affix')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.affix.Constructor = Affix
+
+
+ // AFFIX NO CONFLICT
+ // =================
+
+ $.fn.affix.noConflict = function () {
+ $.fn.affix = old
+ return this
+ }
+
+
+ // AFFIX DATA-API
+ // ==============
+
+ $(window).on('load', function () {
+ $('[data-spy="affix"]').each(function () {
+ var $spy = $(this)
+ var data = $spy.data()
+
+ data.offset = data.offset || {}
+
+ if (data.offsetBottom) data.offset.bottom = data.offsetBottom
+ if (data.offsetTop) data.offset.top = data.offsetTop
+
+ $spy.affix(data)
+ })
+ })
+
+}(window.$jqTheme || window.jQuery);
diff --git a/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/js/bootstrap.min.js b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/js/bootstrap.min.js
new file mode 100644
index 0000000..0890964
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/static/bootstrap-3.0.0/js/bootstrap.min.js
@@ -0,0 +1,6 @@
+/**
+* bootstrap.js v3.0.0 by @fat and @mdo
+* Copyright 2013 Twitter Inc.
+* http://www.apache.org/licenses/LICENSE-2.0
+*/
+if(!jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.tran [...]
diff --git a/dev/docs/source/_themes/bootstrap/static/bootstrap-sphinx.css_t b/dev/docs/source/_themes/bootstrap/static/bootstrap-sphinx.css_t
new file mode 100644
index 0000000..5b1ac7d
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/static/bootstrap-sphinx.css_t
@@ -0,0 +1,227 @@
+/*
+* bootstrap-sphinx.css
+* ~~~~~~~~~~~~~~~~~~~~
+*
+* Sphinx stylesheet -- Twitter Bootstrap theme.
+*/
+
+.navbar-inverse .brand {
+ color: #FFF;
+}
+
+.page-top {
+ top: 0px;
+}
+
+{% if theme_navbar_fixed_top == 'true' %}
+body {
+{% if theme_bootswatch_theme %}
+ padding-top: 50px;
+{% else %}
+ padding-top: 40px;
+{% endif %}
+}
+.page-top {
+{% if theme_bootswatch_theme %}
+ top: 50px;
+{% else %}
+ top: 40px;
+{% endif %}
+}
+
+.navbar-inner {
+ padding-left: 12px !important;
+ padding-right: 12px !important;
+}
+{% endif %}
+
+{%- block sidebarlogo %}
+ {%- if logo %}
+ at media (min-width: 980px) {
+ .navbar h3 a, .navbar .brand {
+ background: transparent url("{{ logo }}") no-repeat 22px 3px;
+ padding-left: 62px;
+ }
+}
+ {%- endif %}
+{%- endblock %}
+
+table {
+ border: 0;
+}
+
+.highlighttable .code pre {
+ font-size: 12px;
+}
+
+.highlighttable .linenos pre {
+ word-break: normal;
+ font-size: 12px;
+}
+
+div.highlight {
+ background: none;
+}
+
+table.field-list {
+ width: auto;
+}
+
+.footer {
+ width: 100%;
+ border-top: 1px solid #ccc;
+ padding-top: 10px;
+}
+
+.bs-sidenav form, .bs-sidenav #sourcelink {
+ padding: 5px 20px;
+}
+
+{% if theme_bootstrap_version == "3" %}
+
+/* The code below is based on the bootstrap website sidebar */
+
+.bs-sidenav.affix {
+ position: static;
+}
+
+/* First level of nav */
+.bs-sidenav {
+ margin-top: 30px;
+ margin-bottom: 30px;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ text-shadow: 0 1px 0 #fff;
+ background-color: #f7f5fa;
+ border-radius: 5px;
+}
+
+/* All levels of nav */
+.bs-sidenav .nav > li > a {
+ display: block;
+ color: #716b7a;
+ padding: 5px 20px;
+}
+.bs-sidenav .nav > li > a:hover,
+.bs-sidenav .nav > li > a:focus {
+ text-decoration: none;
+ background-color: #e5e3e9;
+ border-right: 1px solid #dbd8e0;
+}
+.bs-sidenav .nav > .active > a,
+.bs-sidenav .nav > .active:hover > a,
+.bs-sidenav .nav > .active:focus > a {
+ font-weight: bold;
+ color: #563d7c;
+ background-color: transparent;
+ border-right: 1px solid #563d7c;
+}
+
+.bs-sidenav .nav .nav > li > a {
+ padding-top: 3px;
+ padding-bottom: 3px;
+ padding-left: 30px;
+ font-size: 90%;
+}
+
+.bs-sidenav .nav .nav .nav > li > a {
+ padding-top: 3px;
+ padding-bottom: 3px;
+ padding-left: 40px;
+ font-size: 90%;
+}
+
+.bs-sidenav .nav .nav .nav .nav > li > a {
+ padding-top: 3px;
+ padding-bottom: 3px;
+ padding-left: 50px;
+ font-size: 90%;
+}
+
+/* Show and affix the side nav when space allows it */
+ at media screen and (min-width: 992px) {
+ .bs-sidenav .nav > .active > ul {
+ display: block;
+ }
+ /* Widen the fixed sidenav */
+ .bs-sidenav.affix,
+ .bs-sidenav.affix-bottom {
+ width: 213px;
+ }
+ .bs-sidenav.affix {
+ position: fixed; /* Undo the static from mobile first approach */
+ top: 80px;
+ }
+ .bs-sidenav.affix-bottom {
+ position: absolute; /* Undo the static from mobile first approach */
+ }
+ .bs-sidenav.affix-bottom .bs-sidenav,
+ .bs-sidenav.affix .bs-sidenav {
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+}
+ at media screen and (min-width: 1200px) {
+ /* Widen the fixed sidenav again */
+ .bs-sidenav.affix-bottom,
+ .bs-sidenav.affix {
+ width: 263px;
+ }
+}
+
+
+{% else %}
+
+.bs-sidenav {
+ width: 228px;
+ margin: 30px 0 0;
+ padding: 10px 0 0 5px;
+}
+
+.bs-sidenav ul{
+ list-style-type: none;
+ padding-left: 25px;
+ margin-left: 0; /* bootstrap 2 compatability. */
+}
+
+ at media (min-width: 1200px) {
+ .bs-sidenav {
+ width: 258px;
+ }
+}
+
+/* Desktop
+------------------------- */
+ at media (max-width: 980px) {
+ .bs-sidenav {
+ width: 218px;
+ margin-top: 30px;
+ margin-right: 0;
+ }
+}
+
+/* Tablet to desktop
+------------------------- */
+ at media (min-width: 768px) and (max-width: 979px) {
+ .bs-sidenav {
+ width: 166px;
+ margin-top: 20px;
+ }
+}
+
+/* Tablet
+------------------------- */
+ at media (max-width: 767px) {
+ .navbar-version {
+ padding-left: 5px;
+ }
+ .bs-sidenav {
+ width: auto;
+ margin-bottom: 20px;
+ }
+ .bs-sidenav.affix {
+ position: static;
+ top: 0;
+ }
+}
+{% endif %}
diff --git a/dev/docs/source/_themes/bootstrap/static/bootstrap-sphinx.js_t b/dev/docs/source/_themes/bootstrap/static/bootstrap-sphinx.js_t
new file mode 100644
index 0000000..4f22cbd
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/static/bootstrap-sphinx.js_t
@@ -0,0 +1,160 @@
+(function ($) {
+ /**
+ * Patch TOC list.
+ *
+ * Will mutate the underlying span to have a correct ul for nav.
+ *
+ * @param $span: Span containing nested UL's to mutate.
+ * @param minLevel: Starting level for nested lists. (1: global, 2: local).
+ */
+ var patchToc = function ($ul, minLevel) {
+ var findA,
+ patchTables,
+ $localLi;
+
+ // Find all a "internal" tags, traversing recursively.
+ findA = function ($elem, level) {
+ level = level || 0;
+ var $items = $elem.find("> li > a.internal, > ul, > li > ul");
+
+ // Iterate everything in order.
+ $items.each(function (index, item) {
+ var $item = $(item),
+ tag = item.tagName.toLowerCase(),
+ $childrenLi = $item.children('li'),
+ $parentLi = $($item.parent('li'), $item.parent().parent('li'));
+
+ // Add dropdowns if more children and above minimum level.
+ if (tag === 'ul' && level >= minLevel && $childrenLi.length > 0) {
+ $parentLi
+ .addClass('dropdown-submenu')
+ .children('a').first().attr('tabindex', -1);
+
+ $item.addClass('dropdown-menu');
+ }
+
+ findA($item, level + 1);
+ });
+ };
+
+ findA($ul);
+ };
+
+ /**
+ * Patch all tables to remove ``docutils`` class and add Bootstrap base
+ * ``table`` class.
+ */
+ patchTables = function () {
+ $("table.docutils")
+ .removeClass("docutils")
+ .addClass("table")
+ .attr("border", 0);
+ };
+
+ $(window).load(function () {
+ /*
+ * Scroll the window to avoid the topnav bar
+ * https://github.com/twitter/bootstrap/issues/1768
+ */
+ if ($("#navbar.navbar-fixed-top").length > 0) {
+ var navHeight = $("#navbar").height(),
+ shiftWindow = function() { scrollBy(0, -navHeight - 10); };
+
+ if (location.hash) {
+ setTimeout(shiftWindow, 1);
+ }
+
+ window.addEventListener("hashchange", shiftWindow);
+ }
+ });
+
+ $(document).ready(function () {
+ // Add styling, structure to TOC's.
+ $(".dropdown-menu").each(function () {
+ $(this).find("ul").each(function (index, item){
+ var $item = $(item);
+ $item.addClass('unstyled');
+ });
+ });
+
+ // Global TOC.
+ if ($("ul.globaltoc li").length) {
+ patchToc($("ul.globaltoc"), 1);
+ } else {
+ // Remove Global TOC.
+ $(".globaltoc-container").remove();
+ }
+
+ // Local TOC.
+ $(".bs-sidenav ul").addClass("nav nav-list");
+ $(".bs-sidenav > ul > li > a").addClass("nav-header");
+
+ {% if theme_bootstrap_version == "3" %}
+ // back to top
+ setTimeout(function () {
+ var $sideBar = $('.bs-sidenav');
+
+ $sideBar.affix({
+ offset: {
+ top: function () {
+ var offsetTop = $sideBar.offset().top;
+ var sideBarMargin = parseInt($sideBar.children(0).css('margin-top'), 10);
+ var navOuterHeight = $('#navbar').height();
+
+ return (this.top = offsetTop - navOuterHeight - sideBarMargin);
+ }
+ , bottom: function () {
+ // add 25 because the footer height doesn't seem to be enough
+ return (this.bottom = $('.footer').outerHeight(true) + 25);
+ }
+ }
+ });
+ }, 100);
+ {% endif %}
+
+ // Local TOC.
+ patchToc($("ul.localtoc"), 2);
+
+ // Mutate sub-lists (for bs-2.3.0).
+ $(".dropdown-menu ul").not(".dropdown-menu").each(function () {
+ var $ul = $(this),
+ $parent = $ul.parent(),
+ tag = $parent[0].tagName.toLowerCase(),
+ $kids = $ul.children().detach();
+
+ // Replace list with items if submenu header.
+ if (tag === "ul") {
+ $ul.replaceWith($kids);
+ } else if (tag === "li") {
+ // Insert into previous list.
+ $parent.after($kids);
+ $ul.remove();
+ }
+ });
+
+ // Add divider in page TOC.
+ $localLi = $("ul.localtoc li");
+ if ($localLi.length > 2) {
+ $localLi.first().after('<li class="divider"></li>');
+ }
+
+ // Enable dropdown.
+ $('.dropdown-toggle').dropdown();
+
+ // Patch tables.
+ patchTables();
+
+ // Add Note, Warning styles. (BS v2,3 compatible).
+ $('div.note').addClass('alert alert-info');
+ $('div.warning').addClass('alert alert-danger alert-error');
+
+ // Inline code styles to Bootstrap style.
+ $('tt.docutils.literal').not(".xref").each(function (i, e) {
+ // ignore references
+ if (!$(e).parent().hasClass("reference")) {
+ $(e).replaceWith(function () {
+ return $("<code />").text($(this).text());
+ });
+ }});
+ });
+}($jqTheme || window.jQuery));
diff --git a/dev/docs/source/_themes/bootstrap/static/js/jquery-1.9.1.js b/dev/docs/source/_themes/bootstrap/static/js/jquery-1.9.1.js
new file mode 100644
index 0000000..e2c203f
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/static/js/jquery-1.9.1.js
@@ -0,0 +1,9597 @@
+/*!
+ * jQuery JavaScript Library v1.9.1
+ * http://jquery.com/
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ *
+ * Copyright 2005, 2012 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2013-2-4
+ */
+(function( window, undefined ) {
+
+// Can't do this because several apps including ASP.NET trace
+// the stack via arguments.caller.callee and Firefox dies if
+// you try to trace through "use strict" call chains. (#13335)
+// Support: Firefox 18+
+//"use strict";
+var
+ // The deferred used on DOM ready
+ readyList,
+
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // Support: IE<9
+ // For `typeof node.method` instead of `node.method !== undefined`
+ core_strundefined = typeof undefined,
+
+ // Use the correct document accordingly with window argument (sandbox)
+ document = window.document,
+ location = window.location,
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // [[Class]] -> type pairs
+ class2type = {},
+
+ // List of deleted data cache ids, so we can reuse them
+ core_deletedIds = [],
+
+ core_version = "1.9.1",
+
+ // Save a reference to some core methods
+ core_concat = core_deletedIds.concat,
+ core_push = core_deletedIds.push,
+ core_slice = core_deletedIds.slice,
+ core_indexOf = core_deletedIds.indexOf,
+ core_toString = class2type.toString,
+ core_hasOwn = class2type.hasOwnProperty,
+ core_trim = core_version.trim,
+
+ // Define a local copy of jQuery
+ jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
+
+ // Used for matching numbers
+ core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
+
+ // Used for splitting on whitespace
+ core_rnotwhite = /\S+/g,
+
+ // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
+ rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
+
+ // A simple way to check for HTML strings
+ // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+ // Strict HTML recognition (#11290: must start with <)
+ rquickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
+
+ // JSON RegExp
+ rvalidchars = /^[\],:{}\s]*$/,
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
+ rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,
+
+ // Matches dashed string for camelizing
+ rmsPrefix = /^-ms-/,
+ rdashAlpha = /-([\da-z])/gi,
+
+ // Used by jQuery.camelCase as callback to replace()
+ fcamelCase = function( all, letter ) {
+ return letter.toUpperCase();
+ },
+
+ // The ready event handler
+ completed = function( event ) {
+
+ // readyState === "complete" is good enough for us to call the dom ready in oldIE
+ if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
+ detach();
+ jQuery.ready();
+ }
+ },
+ // Clean-up method for dom ready events
+ detach = function() {
+ if ( document.addEventListener ) {
+ document.removeEventListener( "DOMContentLoaded", completed, false );
+ window.removeEventListener( "load", completed, false );
+
+ } else {
+ document.detachEvent( "onreadystatechange", completed );
+ window.detachEvent( "onload", completed );
+ }
+ };
+
+jQuery.fn = jQuery.prototype = {
+ // The current version of jQuery being used
+ jquery: core_version,
+
+ constructor: jQuery,
+ init: function( selector, context, rootjQuery ) {
+ var match, elem;
+
+ // HANDLE: $(""), $(null), $(undefined), $(false)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+ // Assume that strings that start and end with <> are HTML and skip the regex check
+ match = [ null, selector, null ];
+
+ } else {
+ match = rquickExpr.exec( selector );
+ }
+
+ // Match html or make sure no context is specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
+
+ // scripts is true for back-compat
+ jQuery.merge( this, jQuery.parseHTML(
+ match[1],
+ context && context.nodeType ? context.ownerDocument || context : document,
+ true
+ ) );
+
+ // HANDLE: $(html, props)
+ if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+ for ( match in context ) {
+ // Properties of context are called as methods if possible
+ if ( jQuery.isFunction( this[ match ] ) ) {
+ this[ match ]( context[ match ] );
+
+ // ...and otherwise set as attributes
+ } else {
+ this.attr( match, context[ match ] );
+ }
+ }
+ }
+
+ return this;
+
+ // HANDLE: $(#id)
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return ( context || rootjQuery ).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(DOMElement)
+ } else if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if ( selector.selector !== undefined ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return core_slice.call( this );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems ) {
+
+ // Build a new jQuery matched element set
+ var ret = jQuery.merge( this.constructor(), elems );
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+ ret.context = this.context;
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Add the callback
+ jQuery.ready.promise().done( fn );
+
+ return this;
+ },
+
+ slice: function() {
+ return this.pushStack( core_slice.apply( this, arguments ) );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ eq: function( i ) {
+ var len = this.length,
+ j = +i + ( i < 0 ? len : 0 );
+ return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: core_push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var src, copyIsArray, copy, name, options, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+
+ // Abort if there are pending holds or we're already ready
+ if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+ return;
+ }
+
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.resolveWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger("ready").off("ready");
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type(obj) === "array";
+ },
+
+ isWindow: function( obj ) {
+ return obj != null && obj == obj.window;
+ },
+
+ isNumeric: function( obj ) {
+ return !isNaN( parseFloat(obj) ) && isFinite( obj );
+ },
+
+ type: function( obj ) {
+ if ( obj == null ) {
+ return String( obj );
+ }
+ return typeof obj === "object" || typeof obj === "function" ?
+ class2type[ core_toString.call(obj) ] || "object" :
+ typeof obj;
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ try {
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !core_hasOwn.call(obj, "constructor") &&
+ !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+ } catch ( e ) {
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || core_hasOwn.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ var name;
+ for ( name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw new Error( msg );
+ },
+
+ // data: string of html
+ // context (optional): If specified, the fragment will be created in this context, defaults to document
+ // keepScripts (optional): If true, will include scripts passed in the html string
+ parseHTML: function( data, context, keepScripts ) {
+ if ( !data || typeof data !== "string" ) {
+ return null;
+ }
+ if ( typeof context === "boolean" ) {
+ keepScripts = context;
+ context = false;
+ }
+ context = context || document;
+
+ var parsed = rsingleTag.exec( data ),
+ scripts = !keepScripts && [];
+
+ // Single tag
+ if ( parsed ) {
+ return [ context.createElement( parsed[1] ) ];
+ }
+
+ parsed = jQuery.buildFragment( [ data ], context, scripts );
+ if ( scripts ) {
+ jQuery( scripts ).remove();
+ }
+ return jQuery.merge( [], parsed.childNodes );
+ },
+
+ parseJSON: function( data ) {
+ // Attempt to parse using the native JSON parser first
+ if ( window.JSON && window.JSON.parse ) {
+ return window.JSON.parse( data );
+ }
+
+ if ( data === null ) {
+ return data;
+ }
+
+ if ( typeof data === "string" ) {
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ if ( data ) {
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+ .replace( rvalidtokens, "]" )
+ .replace( rvalidbraces, "")) ) {
+
+ return ( new Function( "return " + data ) )();
+ }
+ }
+ }
+
+ jQuery.error( "Invalid JSON: " + data );
+ },
+
+ // Cross-browser xml parsing
+ parseXML: function( data ) {
+ var xml, tmp;
+ if ( !data || typeof data !== "string" ) {
+ return null;
+ }
+ try {
+ if ( window.DOMParser ) { // Standard
+ tmp = new DOMParser();
+ xml = tmp.parseFromString( data , "text/xml" );
+ } else { // IE
+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
+ xml.async = "false";
+ xml.loadXML( data );
+ }
+ } catch( e ) {
+ xml = undefined;
+ }
+ if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
+ jQuery.error( "Invalid XML: " + data );
+ }
+ return xml;
+ },
+
+ noop: function() {},
+
+ // Evaluates a script in a global context
+ // Workarounds based on findings by Jim Driscoll
+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+ globalEval: function( data ) {
+ if ( data && jQuery.trim( data ) ) {
+ // We use execScript on Internet Explorer
+ // We use an anonymous function so that context is window
+ // rather than jQuery in Firefox
+ ( window.execScript || function( data ) {
+ window[ "eval" ].call( window, data );
+ } )( data );
+ }
+ },
+
+ // Convert dashed to camelCase; used by the css and data modules
+ // Microsoft forgot to hump their vendor prefix (#9572)
+ camelCase: function( string ) {
+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+ },
+
+ // args is for internal usage only
+ each: function( obj, callback, args ) {
+ var value,
+ i = 0,
+ length = obj.length,
+ isArray = isArraylike( obj );
+
+ if ( args ) {
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback.apply( obj[ i ], args );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( i in obj ) {
+ value = callback.apply( obj[ i ], args );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback.call( obj[ i ], i, obj[ i ] );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( i in obj ) {
+ value = callback.call( obj[ i ], i, obj[ i ] );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return obj;
+ },
+
+ // Use native String.trim function wherever possible
+ trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
+ function( text ) {
+ return text == null ?
+ "" :
+ core_trim.call( text );
+ } :
+
+ // Otherwise use our own trimming functionality
+ function( text ) {
+ return text == null ?
+ "" :
+ ( text + "" ).replace( rtrim, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( arr, results ) {
+ var ret = results || [];
+
+ if ( arr != null ) {
+ if ( isArraylike( Object(arr) ) ) {
+ jQuery.merge( ret,
+ typeof arr === "string" ?
+ [ arr ] : arr
+ );
+ } else {
+ core_push.call( ret, arr );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, arr, i ) {
+ var len;
+
+ if ( arr ) {
+ if ( core_indexOf ) {
+ return core_indexOf.call( arr, elem, i );
+ }
+
+ len = arr.length;
+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+ for ( ; i < len; i++ ) {
+ // Skip accessing in sparse arrays
+ if ( i in arr && arr[ i ] === elem ) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var l = second.length,
+ i = first.length,
+ j = 0;
+
+ if ( typeof l === "number" ) {
+ for ( ; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var retVal,
+ ret = [],
+ i = 0,
+ length = elems.length;
+ inv = !!inv;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( ; i < length; i++ ) {
+ retVal = !!callback( elems[ i ], i );
+ if ( inv !== retVal ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var value,
+ i = 0,
+ length = elems.length,
+ isArray = isArraylike( elems ),
+ ret = [];
+
+ // Go through the array, translating each of the items to their
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( i in elems ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return core_concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ var args, proxy, tmp;
+
+ if ( typeof context === "string" ) {
+ tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ args = core_slice.call( arguments, 2 );
+ proxy = function() {
+ return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ // Multifunctional method to get and set values of a collection
+ // The value/s can optionally be executed if it's a function
+ access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
+ var i = 0,
+ length = elems.length,
+ bulk = key == null;
+
+ // Sets many values
+ if ( jQuery.type( key ) === "object" ) {
+ chainable = true;
+ for ( i in key ) {
+ jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
+ }
+
+ // Sets one value
+ } else if ( value !== undefined ) {
+ chainable = true;
+
+ if ( !jQuery.isFunction( value ) ) {
+ raw = true;
+ }
+
+ if ( bulk ) {
+ // Bulk operations run against the entire set
+ if ( raw ) {
+ fn.call( elems, value );
+ fn = null;
+
+ // ...except when executing function values
+ } else {
+ bulk = fn;
+ fn = function( elem, key, value ) {
+ return bulk.call( jQuery( elem ), value );
+ };
+ }
+ }
+
+ if ( fn ) {
+ for ( ; i < length; i++ ) {
+ fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
+ }
+ }
+ }
+
+ return chainable ?
+ elems :
+
+ // Gets
+ bulk ?
+ fn.call( elems ) :
+ length ? fn( elems[0], key ) : emptyGet;
+ },
+
+ now: function() {
+ return ( new Date() ).getTime();
+ }
+});
+
+jQuery.ready.promise = function( obj ) {
+ if ( !readyList ) {
+
+ readyList = jQuery.Deferred();
+
+ // Catch cases where $(document).ready() is called after the browser event has already occurred.
+ // we once tried to use readyState "interactive" here, but it caused issues like the one
+ // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ setTimeout( jQuery.ready );
+
+ // Standards-based browsers support DOMContentLoaded
+ } else if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", completed, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", completed, false );
+
+ // If IE event model is used
+ } else {
+ // Ensure firing before onload, maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", completed );
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", completed );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var top = false;
+
+ try {
+ top = window.frameElement == null && document.documentElement;
+ } catch(e) {}
+
+ if ( top && top.doScroll ) {
+ (function doScrollCheck() {
+ if ( !jQuery.isReady ) {
+
+ try {
+ // Use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ top.doScroll("left");
+ } catch(e) {
+ return setTimeout( doScrollCheck, 50 );
+ }
+
+ // detach all dom ready events
+ detach();
+
+ // and execute any waiting functions
+ jQuery.ready();
+ }
+ })();
+ }
+ }
+ }
+ return readyList.promise( obj );
+};
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+function isArraylike( obj ) {
+ var length = obj.length,
+ type = jQuery.type( obj );
+
+ if ( jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ if ( obj.nodeType === 1 && length ) {
+ return true;
+ }
+
+ return type === "array" || type !== "function" &&
+ ( length === 0 ||
+ typeof length === "number" && length > 0 && ( length - 1 ) in obj );
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+// String to Object options format cache
+var optionsCache = {};
+
+// Convert String-formatted options into Object-formatted ones and store in cache
+function createOptions( options ) {
+ var object = optionsCache[ options ] = {};
+ jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
+ object[ flag ] = true;
+ });
+ return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ * options: an optional list of space-separated options that will change how
+ * the callback list behaves or a more traditional option object
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible options:
+ *
+ * once: will ensure the callback list can only be fired once (like a Deferred)
+ *
+ * memory: will keep track of previous values and will call any callback added
+ * after the list has been fired right away with the latest "memorized"
+ * values (like a Deferred)
+ *
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
+ *
+ * stopOnFalse: interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( options ) {
+
+ // Convert options from String-formatted to Object-formatted if needed
+ // (we check in cache first)
+ options = typeof options === "string" ?
+ ( optionsCache[ options ] || createOptions( options ) ) :
+ jQuery.extend( {}, options );
+
+ var // Flag to know if list is currently firing
+ firing,
+ // Last fire value (for non-forgettable lists)
+ memory,
+ // Flag to know if list was already fired
+ fired,
+ // End of the loop when firing
+ firingLength,
+ // Index of currently firing callback (modified by remove if needed)
+ firingIndex,
+ // First callback to fire (used internally by add and fireWith)
+ firingStart,
+ // Actual callback list
+ list = [],
+ // Stack of fire calls for repeatable lists
+ stack = !options.once && [],
+ // Fire callbacks
+ fire = function( data ) {
+ memory = options.memory && data;
+ fired = true;
+ firingIndex = firingStart || 0;
+ firingStart = 0;
+ firingLength = list.length;
+ firing = true;
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+ if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+ memory = false; // To prevent further calls using add
+ break;
+ }
+ }
+ firing = false;
+ if ( list ) {
+ if ( stack ) {
+ if ( stack.length ) {
+ fire( stack.shift() );
+ }
+ } else if ( memory ) {
+ list = [];
+ } else {
+ self.disable();
+ }
+ }
+ },
+ // Actual Callbacks object
+ self = {
+ // Add a callback or a collection of callbacks to the list
+ add: function() {
+ if ( list ) {
+ // First, we save the current length
+ var start = list.length;
+ (function add( args ) {
+ jQuery.each( args, function( _, arg ) {
+ var type = jQuery.type( arg );
+ if ( type === "function" ) {
+ if ( !options.unique || !self.has( arg ) ) {
+ list.push( arg );
+ }
+ } else if ( arg && arg.length && type !== "string" ) {
+ // Inspect recursively
+ add( arg );
+ }
+ });
+ })( arguments );
+ // Do we need to add the callbacks to the
+ // current firing batch?
+ if ( firing ) {
+ firingLength = list.length;
+ // With memory, if we're not firing then
+ // we should call right away
+ } else if ( memory ) {
+ firingStart = start;
+ fire( memory );
+ }
+ }
+ return this;
+ },
+ // Remove a callback from the list
+ remove: function() {
+ if ( list ) {
+ jQuery.each( arguments, function( _, arg ) {
+ var index;
+ while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+ list.splice( index, 1 );
+ // Handle firing indexes
+ if ( firing ) {
+ if ( index <= firingLength ) {
+ firingLength--;
+ }
+ if ( index <= firingIndex ) {
+ firingIndex--;
+ }
+ }
+ }
+ });
+ }
+ return this;
+ },
+ // Check if a given callback is in the list.
+ // If no argument is given, return whether or not list has callbacks attached.
+ has: function( fn ) {
+ return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
+ },
+ // Remove all callbacks from the list
+ empty: function() {
+ list = [];
+ return this;
+ },
+ // Have the list do nothing anymore
+ disable: function() {
+ list = stack = memory = undefined;
+ return this;
+ },
+ // Is it disabled?
+ disabled: function() {
+ return !list;
+ },
+ // Lock the list in its current state
+ lock: function() {
+ stack = undefined;
+ if ( !memory ) {
+ self.disable();
+ }
+ return this;
+ },
+ // Is it locked?
+ locked: function() {
+ return !stack;
+ },
+ // Call all callbacks with the given context and arguments
+ fireWith: function( context, args ) {
+ args = args || [];
+ args = [ context, args.slice ? args.slice() : args ];
+ if ( list && ( !fired || stack ) ) {
+ if ( firing ) {
+ stack.push( args );
+ } else {
+ fire( args );
+ }
+ }
+ return this;
+ },
+ // Call all the callbacks with the given arguments
+ fire: function() {
+ self.fireWith( this, arguments );
+ return this;
+ },
+ // To know if the callbacks have already been called at least once
+ fired: function() {
+ return !!fired;
+ }
+ };
+
+ return self;
+};
+jQuery.extend({
+
+ Deferred: function( func ) {
+ var tuples = [
+ // action, add listener, listener list, final state
+ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+ [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+ [ "notify", "progress", jQuery.Callbacks("memory") ]
+ ],
+ state = "pending",
+ promise = {
+ state: function() {
+ return state;
+ },
+ always: function() {
+ deferred.done( arguments ).fail( arguments );
+ return this;
+ },
+ then: function( /* fnDone, fnFail, fnProgress */ ) {
+ var fns = arguments;
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( tuples, function( i, tuple ) {
+ var action = tuple[ 0 ],
+ fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
+ // deferred[ done | fail | progress ] for forwarding actions to newDefer
+ deferred[ tuple[1] ](function() {
+ var returned = fn && fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise()
+ .done( newDefer.resolve )
+ .fail( newDefer.reject )
+ .progress( newDefer.notify );
+ } else {
+ newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
+ }
+ });
+ });
+ fns = null;
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ return obj != null ? jQuery.extend( obj, promise ) : promise;
+ }
+ },
+ deferred = {};
+
+ // Keep pipe for back-compat
+ promise.pipe = promise.then;
+
+ // Add list-specific methods
+ jQuery.each( tuples, function( i, tuple ) {
+ var list = tuple[ 2 ],
+ stateString = tuple[ 3 ];
+
+ // promise[ done | fail | progress ] = list.add
+ promise[ tuple[1] ] = list.add;
+
+ // Handle state
+ if ( stateString ) {
+ list.add(function() {
+ // state = [ resolved | rejected ]
+ state = stateString;
+
+ // [ reject_list | resolve_list ].disable; progress_list.lock
+ }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+ }
+
+ // deferred[ resolve | reject | notify ]
+ deferred[ tuple[0] ] = function() {
+ deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
+ return this;
+ };
+ deferred[ tuple[0] + "With" ] = list.fireWith;
+ });
+
+ // Make the deferred a promise
+ promise.promise( deferred );
+
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+
+ // All done!
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( subordinate /* , ..., subordinateN */ ) {
+ var i = 0,
+ resolveValues = core_slice.call( arguments ),
+ length = resolveValues.length,
+
+ // the count of uncompleted subordinates
+ remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+ // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+ deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+ // Update function for both resolve and progress values
+ updateFunc = function( i, contexts, values ) {
+ return function( value ) {
+ contexts[ i ] = this;
+ values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
+ if( values === progressValues ) {
+ deferred.notifyWith( contexts, values );
+ } else if ( !( --remaining ) ) {
+ deferred.resolveWith( contexts, values );
+ }
+ };
+ },
+
+ progressValues, progressContexts, resolveContexts;
+
+ // add listeners to Deferred subordinates; treat others as resolved
+ if ( length > 1 ) {
+ progressValues = new Array( length );
+ progressContexts = new Array( length );
+ resolveContexts = new Array( length );
+ for ( ; i < length; i++ ) {
+ if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+ resolveValues[ i ].promise()
+ .done( updateFunc( i, resolveContexts, resolveValues ) )
+ .fail( deferred.reject )
+ .progress( updateFunc( i, progressContexts, progressValues ) );
+ } else {
+ --remaining;
+ }
+ }
+ }
+
+ // if we're not waiting on anything, resolve the master
+ if ( !remaining ) {
+ deferred.resolveWith( resolveContexts, resolveValues );
+ }
+
+ return deferred.promise();
+ }
+});
+jQuery.support = (function() {
+
+ var support, all, a,
+ input, select, fragment,
+ opt, eventName, isSupported, i,
+ div = document.createElement("div");
+
+ // Setup
+ div.setAttribute( "className", "t" );
+ div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
+
+ // Support tests won't run in some limited or non-browser environments
+ all = div.getElementsByTagName("*");
+ a = div.getElementsByTagName("a")[ 0 ];
+ if ( !all || !a || !all.length ) {
+ return {};
+ }
+
+ // First batch of tests
+ select = document.createElement("select");
+ opt = select.appendChild( document.createElement("option") );
+ input = div.getElementsByTagName("input")[ 0 ];
+
+ a.style.cssText = "top:1px;float:left;opacity:.5";
+ support = {
+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+ getSetAttribute: div.className !== "t",
+
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: div.firstChild.nodeType === 3,
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName("tbody").length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName("link").length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText instead)
+ style: /top/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: a.getAttribute("href") === "/a",
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ // Use a regex to work around a WebKit issue. See #5145
+ opacity: /^0.5/.test( a.style.opacity ),
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
+ checkOn: !!input.value,
+
+ // Make sure that a selected-by-default option has a working selected property.
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+ optSelected: opt.selected,
+
+ // Tests for enctype support on a form (#6743)
+ enctype: !!document.createElement("form").enctype,
+
+ // Makes sure cloning an html5 element does not cause problems
+ // Where outerHTML is undefined, this still works
+ html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
+
+ // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode
+ boxModel: document.compatMode === "CSS1Compat",
+
+ // Will be defined later
+ deleteExpando: true,
+ noCloneEvent: true,
+ inlineBlockNeedsLayout: false,
+ shrinkWrapBlocks: false,
+ reliableMarginRight: true,
+ boxSizingReliable: true,
+ pixelPosition: false
+ };
+
+ // Make sure checked status is properly cloned
+ input.checked = true;
+ support.noCloneChecked = input.cloneNode( true ).checked;
+
+ // Make sure that the options inside disabled selects aren't marked as disabled
+ // (WebKit marks them as disabled)
+ select.disabled = true;
+ support.optDisabled = !opt.disabled;
+
+ // Support: IE<9
+ try {
+ delete div.test;
+ } catch( e ) {
+ support.deleteExpando = false;
+ }
+
+ // Check if we can trust getAttribute("value")
+ input = document.createElement("input");
+ input.setAttribute( "value", "" );
+ support.input = input.getAttribute( "value" ) === "";
+
+ // Check if an input maintains its value after becoming a radio
+ input.value = "t";
+ input.setAttribute( "type", "radio" );
+ support.radioValue = input.value === "t";
+
+ // #11217 - WebKit loses check when the name is after the checked attribute
+ input.setAttribute( "checked", "t" );
+ input.setAttribute( "name", "t" );
+
+ fragment = document.createDocumentFragment();
+ fragment.appendChild( input );
+
+ // Check if a disconnected checkbox will retain its checked
+ // value of true after appended to the DOM (IE6/7)
+ support.appendChecked = input.checked;
+
+ // WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ // Support: IE<9
+ // Opera does not clone events (and typeof div.attachEvent === undefined).
+ // IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
+ if ( div.attachEvent ) {
+ div.attachEvent( "onclick", function() {
+ support.noCloneEvent = false;
+ });
+
+ div.cloneNode( true ).click();
+ }
+
+ // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event)
+ // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP), test/csp.php
+ for ( i in { submit: true, change: true, focusin: true }) {
+ div.setAttribute( eventName = "on" + i, "t" );
+
+ support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false;
+ }
+
+ div.style.backgroundClip = "content-box";
+ div.cloneNode( true ).style.backgroundClip = "";
+ support.clearCloneStyle = div.style.backgroundClip === "content-box";
+
+ // Run tests that need a body at doc ready
+ jQuery(function() {
+ var container, marginDiv, tds,
+ divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",
+ body = document.getElementsByTagName("body")[0];
+
+ if ( !body ) {
+ // Return for frameset docs that don't have a body
+ return;
+ }
+
+ container = document.createElement("div");
+ container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";
+
+ body.appendChild( container ).appendChild( div );
+
+ // Support: IE8
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
+ tds = div.getElementsByTagName("td");
+ tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+ tds[ 0 ].style.display = "";
+ tds[ 1 ].style.display = "none";
+
+ // Support: IE8
+ // Check if empty table cells still have offsetWidth/Height
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+
+ // Check box-sizing and margin behavior
+ div.innerHTML = "";
+ div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
+ support.boxSizing = ( div.offsetWidth === 4 );
+ support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );
+
+ // Use window.getComputedStyle because jsdom on node.js will break without it.
+ if ( window.getComputedStyle ) {
+ support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
+ support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
+
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. (#3333)
+ // Fails in WebKit before Feb 2011 nightlies
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ marginDiv = div.appendChild( document.createElement("div") );
+ marginDiv.style.cssText = div.style.cssText = divReset;
+ marginDiv.style.marginRight = marginDiv.style.width = "0";
+ div.style.width = "1px";
+
+ support.reliableMarginRight =
+ !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
+ }
+
+ if ( typeof div.style.zoom !== core_strundefined ) {
+ // Support: IE<8
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ div.innerHTML = "";
+ div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
+
+ // Support: IE6
+ // Check if elements with layout shrink-wrap their children
+ div.style.display = "block";
+ div.innerHTML = "<div></div>";
+ div.firstChild.style.width = "5px";
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
+
+ if ( support.inlineBlockNeedsLayout ) {
+ // Prevent IE 6 from affecting layout for positioned elements #11048
+ // Prevent IE from shrinking the body in IE 7 mode #12869
+ // Support: IE<8
+ body.style.zoom = 1;
+ }
+ }
+
+ body.removeChild( container );
+
+ // Null elements to avoid leaks in IE
+ container = div = tds = marginDiv = null;
+ });
+
+ // Null elements to avoid leaks in IE
+ all = select = fragment = opt = a = input = null;
+
+ return support;
+})();
+
+var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
+ rmultiDash = /([A-Z])/g;
+
+function internalData( elem, name, data, pvt /* Internal Use Only */ ){
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var thisCache, ret,
+ internalKey = jQuery.expando,
+ getByName = typeof name === "string",
+
+ // We have to handle DOM nodes and JS objects differently because IE6-7
+ // can't GC object references properly across the DOM-JS boundary
+ isNode = elem.nodeType,
+
+ // Only DOM nodes need the global jQuery cache; JS object data is
+ // attached directly to the object so GC can occur automatically
+ cache = isNode ? jQuery.cache : elem,
+
+ // Only defining an ID for JS objects if its cache already exists allows
+ // the code to shortcut on the same path as a DOM node with no cache
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
+
+ // Avoid doing any more work than we need to when trying to get data on an
+ // object that has no data at all
+ if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
+ return;
+ }
+
+ if ( !id ) {
+ // Only DOM nodes need a new unique ID for each element since their data
+ // ends up in the global cache
+ if ( isNode ) {
+ elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++;
+ } else {
+ id = internalKey;
+ }
+ }
+
+ if ( !cache[ id ] ) {
+ cache[ id ] = {};
+
+ // Avoids exposing jQuery metadata on plain JS objects when the object
+ // is serialized using JSON.stringify
+ if ( !isNode ) {
+ cache[ id ].toJSON = jQuery.noop;
+ }
+ }
+
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
+ // shallow copied over onto the existing cache
+ if ( typeof name === "object" || typeof name === "function" ) {
+ if ( pvt ) {
+ cache[ id ] = jQuery.extend( cache[ id ], name );
+ } else {
+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+ }
+ }
+
+ thisCache = cache[ id ];
+
+ // jQuery data() is stored in a separate object inside the object's internal data
+ // cache in order to avoid key collisions between internal data and user-defined
+ // data.
+ if ( !pvt ) {
+ if ( !thisCache.data ) {
+ thisCache.data = {};
+ }
+
+ thisCache = thisCache.data;
+ }
+
+ if ( data !== undefined ) {
+ thisCache[ jQuery.camelCase( name ) ] = data;
+ }
+
+ // Check for both converted-to-camel and non-converted data property names
+ // If a data property was specified
+ if ( getByName ) {
+
+ // First Try to find as-is property data
+ ret = thisCache[ name ];
+
+ // Test for null|undefined property data
+ if ( ret == null ) {
+
+ // Try to find the camelCased property
+ ret = thisCache[ jQuery.camelCase( name ) ];
+ }
+ } else {
+ ret = thisCache;
+ }
+
+ return ret;
+}
+
+function internalRemoveData( elem, name, pvt ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var i, l, thisCache,
+ isNode = elem.nodeType,
+
+ // See jQuery.data for more information
+ cache = isNode ? jQuery.cache : elem,
+ id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+
+ // If there is already no cache entry for this object, there is no
+ // purpose in continuing
+ if ( !cache[ id ] ) {
+ return;
+ }
+
+ if ( name ) {
+
+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+ if ( thisCache ) {
+
+ // Support array or space separated string names for data keys
+ if ( !jQuery.isArray( name ) ) {
+
+ // try the string as a key before any manipulation
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+
+ // split the camel cased version by spaces unless a key with the spaces exists
+ name = jQuery.camelCase( name );
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+ name = name.split(" ");
+ }
+ }
+ } else {
+ // If "name" is an array of keys...
+ // When data is initially created, via ("key", "val") signature,
+ // keys will be converted to camelCase.
+ // Since there is no way to tell _how_ a key was added, remove
+ // both plain key and camelCase key. #12786
+ // This will only penalize the array argument path.
+ name = name.concat( jQuery.map( name, jQuery.camelCase ) );
+ }
+
+ for ( i = 0, l = name.length; i < l; i++ ) {
+ delete thisCache[ name[i] ];
+ }
+
+ // If there is no data left in the cache, we want to continue
+ // and let the cache object itself get destroyed
+ if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
+ return;
+ }
+ }
+ }
+
+ // See jQuery.data for more information
+ if ( !pvt ) {
+ delete cache[ id ].data;
+
+ // Don't destroy the parent cache unless the internal data object
+ // had been the only thing left in it
+ if ( !isEmptyDataObject( cache[ id ] ) ) {
+ return;
+ }
+ }
+
+ // Destroy the cache
+ if ( isNode ) {
+ jQuery.cleanData( [ elem ], true );
+
+ // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
+ } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
+ delete cache[ id ];
+
+ // When all else fails, null
+ } else {
+ cache[ id ] = null;
+ }
+}
+
+jQuery.extend({
+ cache: {},
+
+ // Unique for each copy of jQuery on the page
+ // Non-digits removed to match rinlinejQuery
+ expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
+
+ // The following elements throw uncatchable exceptions if you
+ // attempt to add expando properties to them.
+ noData: {
+ "embed": true,
+ // Ban all objects except for Flash (which handle expandos)
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+ "applet": true
+ },
+
+ hasData: function( elem ) {
+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+ return !!elem && !isEmptyDataObject( elem );
+ },
+
+ data: function( elem, name, data ) {
+ return internalData( elem, name, data );
+ },
+
+ removeData: function( elem, name ) {
+ return internalRemoveData( elem, name );
+ },
+
+ // For internal use only.
+ _data: function( elem, name, data ) {
+ return internalData( elem, name, data, true );
+ },
+
+ _removeData: function( elem, name ) {
+ return internalRemoveData( elem, name, true );
+ },
+
+ // A method for determining if a DOM node can handle the data expando
+ acceptData: function( elem ) {
+ // Do not set data on non-element because it will not be cleared (#8335).
+ if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
+ return false;
+ }
+
+ var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+ // nodes accept data unless otherwise specified; rejection can be conditional
+ return !noData || noData !== true && elem.getAttribute("classid") === noData;
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ var attrs, name,
+ elem = this[0],
+ i = 0,
+ data = null;
+
+ // Gets all values
+ if ( key === undefined ) {
+ if ( this.length ) {
+ data = jQuery.data( elem );
+
+ if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+ attrs = elem.attributes;
+ for ( ; i < attrs.length; i++ ) {
+ name = attrs[i].name;
+
+ if ( !name.indexOf( "data-" ) ) {
+ name = jQuery.camelCase( name.slice(5) );
+
+ dataAttr( elem, name, data[ name ] );
+ }
+ }
+ jQuery._data( elem, "parsedAttrs", true );
+ }
+ }
+
+ return data;
+ }
+
+ // Sets multiple values
+ if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ return jQuery.access( this, function( value ) {
+
+ if ( value === undefined ) {
+ // Try to fetch any internally stored data first
+ return elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null;
+ }
+
+ this.each(function() {
+ jQuery.data( this, key, value );
+ });
+ }, null, value, arguments.length > 1, null, true );
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+
+function dataAttr( elem, key, data ) {
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+
+ var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+
+ data = elem.getAttribute( name );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ // Only convert to a number if it doesn't change the string
+ +data + "" === data ? +data :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ jQuery.data( elem, key, data );
+
+ } else {
+ data = undefined;
+ }
+ }
+
+ return data;
+}
+
+// checks a cache object for emptiness
+function isEmptyDataObject( obj ) {
+ var name;
+ for ( name in obj ) {
+
+ // if the public data object is empty, the private is still empty
+ if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+ continue;
+ }
+ if ( name !== "toJSON" ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+jQuery.extend({
+ queue: function( elem, type, data ) {
+ var queue;
+
+ if ( elem ) {
+ type = ( type || "fx" ) + "queue";
+ queue = jQuery._data( elem, type );
+
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !queue || jQuery.isArray(data) ) {
+ queue = jQuery._data( elem, type, jQuery.makeArray(data) );
+ } else {
+ queue.push( data );
+ }
+ }
+ return queue || [];
+ }
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ startLength = queue.length,
+ fn = queue.shift(),
+ hooks = jQuery._queueHooks( elem, type ),
+ next = function() {
+ jQuery.dequeue( elem, type );
+ };
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ startLength--;
+ }
+
+ hooks.cur = fn;
+ if ( fn ) {
+
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift( "inprogress" );
+ }
+
+ // clear up the last queue stop function
+ delete hooks.stop;
+ fn.call( elem, next, hooks );
+ }
+
+ if ( !startLength && hooks ) {
+ hooks.empty.fire();
+ }
+ },
+
+ // not intended for public consumption - generates a queueHooks object, or returns the current one
+ _queueHooks: function( elem, type ) {
+ var key = type + "queueHooks";
+ return jQuery._data( elem, key ) || jQuery._data( elem, key, {
+ empty: jQuery.Callbacks("once memory").add(function() {
+ jQuery._removeData( elem, type + "queue" );
+ jQuery._removeData( elem, key );
+ })
+ });
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ var setter = 2;
+
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ setter--;
+ }
+
+ if ( arguments.length < setter ) {
+ return jQuery.queue( this[0], type );
+ }
+
+ return data === undefined ?
+ this :
+ this.each(function() {
+ var queue = jQuery.queue( this, type, data );
+
+ // ensure a hooks for this queue
+ jQuery._queueHooks( this, type );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+ // Based off of the plugin by Clint Helfers, with permission.
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function( next, hooks ) {
+ var timeout = setTimeout( next, time );
+ hooks.stop = function() {
+ clearTimeout( timeout );
+ };
+ });
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, obj ) {
+ var tmp,
+ count = 1,
+ defer = jQuery.Deferred(),
+ elements = this,
+ i = this.length,
+ resolve = function() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ };
+
+ if ( typeof type !== "string" ) {
+ obj = type;
+ type = undefined;
+ }
+ type = type || "fx";
+
+ while( i-- ) {
+ tmp = jQuery._data( elements[ i ], type + "queueHooks" );
+ if ( tmp && tmp.empty ) {
+ count++;
+ tmp.empty.add( resolve );
+ }
+ }
+ resolve();
+ return defer.promise( obj );
+ }
+});
+var nodeHook, boolHook,
+ rclass = /[\t\r\n]/g,
+ rreturn = /\r/g,
+ rfocusable = /^(?:input|select|textarea|button|object)$/i,
+ rclickable = /^(?:a|area)$/i,
+ rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,
+ ruseDefault = /^(?:checked|selected)$/i,
+ getSetAttribute = jQuery.support.getSetAttribute,
+ getSetInput = jQuery.support.input;
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
+ },
+
+ removeAttr: function( name ) {
+ return this.each(function() {
+ jQuery.removeAttr( this, name );
+ });
+ },
+
+ prop: function( name, value ) {
+ return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+ },
+
+ removeProp: function( name ) {
+ name = jQuery.propFix[ name ] || name;
+ return this.each(function() {
+ // try/catch handles cases where IE balks (such as removing a property on window)
+ try {
+ this[ name ] = undefined;
+ delete this[ name ];
+ } catch( e ) {}
+ });
+ },
+
+ addClass: function( value ) {
+ var classes, elem, cur, clazz, j,
+ i = 0,
+ len = this.length,
+ proceed = typeof value === "string" && value;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).addClass( value.call( this, j, this.className ) );
+ });
+ }
+
+ if ( proceed ) {
+ // The disjunction here is for better compressibility (see removeClass)
+ classes = ( value || "" ).match( core_rnotwhite ) || [];
+
+ for ( ; i < len; i++ ) {
+ elem = this[ i ];
+ cur = elem.nodeType === 1 && ( elem.className ?
+ ( " " + elem.className + " " ).replace( rclass, " " ) :
+ " "
+ );
+
+ if ( cur ) {
+ j = 0;
+ while ( (clazz = classes[j++]) ) {
+ if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
+ cur += clazz + " ";
+ }
+ }
+ elem.className = jQuery.trim( cur );
+
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ var classes, elem, cur, clazz, j,
+ i = 0,
+ len = this.length,
+ proceed = arguments.length === 0 || typeof value === "string" && value;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).removeClass( value.call( this, j, this.className ) );
+ });
+ }
+ if ( proceed ) {
+ classes = ( value || "" ).match( core_rnotwhite ) || [];
+
+ for ( ; i < len; i++ ) {
+ elem = this[ i ];
+ // This expression is here for better compressibility (see addClass)
+ cur = elem.nodeType === 1 && ( elem.className ?
+ ( " " + elem.className + " " ).replace( rclass, " " ) :
+ ""
+ );
+
+ if ( cur ) {
+ j = 0;
+ while ( (clazz = classes[j++]) ) {
+ // Remove *all* instances
+ while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
+ cur = cur.replace( " " + clazz + " ", " " );
+ }
+ }
+ elem.className = value ? jQuery.trim( cur ) : "";
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value,
+ isBool = typeof stateVal === "boolean";
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( i ) {
+ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className,
+ i = 0,
+ self = jQuery( this ),
+ state = stateVal,
+ classNames = value.match( core_rnotwhite ) || [];
+
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space separated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
+
+ // Toggle whole class name
+ } else if ( type === core_strundefined || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ jQuery._data( this, "__className__", this.className );
+ }
+
+ // If the element has a class name or if we're passed "false",
+ // then remove the whole classname (if there was one, the above saved it).
+ // Otherwise bring back whatever was previously saved (if anything),
+ // falling back to the empty string if nothing was stored.
+ this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ",
+ i = 0,
+ l = this.length;
+ for ( ; i < l; i++ ) {
+ if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ val: function( value ) {
+ var ret, hooks, isFunction,
+ elem = this[0];
+
+ if ( !arguments.length ) {
+ if ( elem ) {
+ hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+ return ret;
+ }
+
+ ret = elem.value;
+
+ return typeof ret === "string" ?
+ // handle most common string cases
+ ret.replace(rreturn, "") :
+ // handle cases where value is null/undef or number
+ ret == null ? "" : ret;
+ }
+
+ return;
+ }
+
+ isFunction = jQuery.isFunction( value );
+
+ return this.each(function( i ) {
+ var val,
+ self = jQuery(this);
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call( this, i, self.val() );
+ } else {
+ val = value;
+ }
+
+ // Treat null/undefined as ""; convert numbers to string
+ if ( val == null ) {
+ val = "";
+ } else if ( typeof val === "number" ) {
+ val += "";
+ } else if ( jQuery.isArray( val ) ) {
+ val = jQuery.map(val, function ( value ) {
+ return value == null ? "" : value + "";
+ });
+ }
+
+ hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+ // If set returns undefined, fall back to normal setting
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ valHooks: {
+ option: {
+ get: function( elem ) {
+ // attributes.value is undefined in Blackberry 4.7 but
+ // uses .value. See #6932
+ var val = elem.attributes.value;
+ return !val || val.specified ? elem.value : elem.text;
+ }
+ },
+ select: {
+ get: function( elem ) {
+ var value, option,
+ options = elem.options,
+ index = elem.selectedIndex,
+ one = elem.type === "select-one" || index < 0,
+ values = one ? null : [],
+ max = one ? index + 1 : options.length,
+ i = index < 0 ?
+ max :
+ one ? index : 0;
+
+ // Loop through all the selected options
+ for ( ; i < max; i++ ) {
+ option = options[ i ];
+
+ // oldIE doesn't update selected after form reset (#2551)
+ if ( ( option.selected || i === index ) &&
+ // Don't return options that are disabled or in a disabled optgroup
+ ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
+ ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
+
+ // Get the specific value for the option
+ value = jQuery( option ).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ return values;
+ },
+
+ set: function( elem, value ) {
+ var values = jQuery.makeArray( value );
+
+ jQuery(elem).find("option").each(function() {
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+ });
+
+ if ( !values.length ) {
+ elem.selectedIndex = -1;
+ }
+ return values;
+ }
+ }
+ },
+
+ attr: function( elem, name, value ) {
+ var hooks, notxml, ret,
+ nType = elem.nodeType;
+
+ // don't get/set attributes on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ // Fallback to prop when attributes are not supported
+ if ( typeof elem.getAttribute === core_strundefined ) {
+ return jQuery.prop( elem, name, value );
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ // All attributes are lowercase
+ // Grab necessary hook if one is defined
+ if ( notxml ) {
+ name = name.toLowerCase();
+ hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
+ }
+
+ if ( value !== undefined ) {
+
+ if ( value === null ) {
+ jQuery.removeAttr( elem, name );
+
+ } else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ elem.setAttribute( name, value + "" );
+ return value;
+ }
+
+ } else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+
+ // In IE9+, Flash objects don't have .getAttribute (#12945)
+ // Support: IE9+
+ if ( typeof elem.getAttribute !== core_strundefined ) {
+ ret = elem.getAttribute( name );
+ }
+
+ // Non-existent attributes return null, we normalize to undefined
+ return ret == null ?
+ undefined :
+ ret;
+ }
+ },
+
+ removeAttr: function( elem, value ) {
+ var name, propName,
+ i = 0,
+ attrNames = value && value.match( core_rnotwhite );
+
+ if ( attrNames && elem.nodeType === 1 ) {
+ while ( (name = attrNames[i++]) ) {
+ propName = jQuery.propFix[ name ] || name;
+
+ // Boolean attributes get special treatment (#10870)
+ if ( rboolean.test( name ) ) {
+ // Set corresponding property to false for boolean attributes
+ // Also clear defaultChecked/defaultSelected (if appropriate) for IE<8
+ if ( !getSetAttribute && ruseDefault.test( name ) ) {
+ elem[ jQuery.camelCase( "default-" + name ) ] =
+ elem[ propName ] = false;
+ } else {
+ elem[ propName ] = false;
+ }
+
+ // See #9699 for explanation of this approach (setting first, then removal)
+ } else {
+ jQuery.attr( elem, name, "" );
+ }
+
+ elem.removeAttribute( getSetAttribute ? name : propName );
+ }
+ }
+ },
+
+ attrHooks: {
+ type: {
+ set: function( elem, value ) {
+ if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+ // Setting the type on a radio button after the value resets the value in IE6-9
+ // Reset value to default in case type is set after value during creation
+ var val = elem.value;
+ elem.setAttribute( "type", value );
+ if ( val ) {
+ elem.value = val;
+ }
+ return value;
+ }
+ }
+ }
+ },
+
+ propFix: {
+ tabindex: "tabIndex",
+ readonly: "readOnly",
+ "for": "htmlFor",
+ "class": "className",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ cellpadding: "cellPadding",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ usemap: "useMap",
+ frameborder: "frameBorder",
+ contenteditable: "contentEditable"
+ },
+
+ prop: function( elem, name, value ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // don't get/set properties on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ if ( notxml ) {
+ // Fix name and attach hooks
+ name = jQuery.propFix[ name ] || name;
+ hooks = jQuery.propHooks[ name ];
+ }
+
+ if ( value !== undefined ) {
+ if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ return ( elem[ name ] = value );
+ }
+
+ } else {
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+ return elem[ name ];
+ }
+ }
+ },
+
+ propHooks: {
+ tabIndex: {
+ get: function( elem ) {
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+ var attributeNode = elem.getAttributeNode("tabindex");
+
+ return attributeNode && attributeNode.specified ?
+ parseInt( attributeNode.value, 10 ) :
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+ 0 :
+ undefined;
+ }
+ }
+ }
+});
+
+// Hook for boolean attributes
+boolHook = {
+ get: function( elem, name ) {
+ var
+ // Use .prop to determine if this attribute is understood as boolean
+ prop = jQuery.prop( elem, name ),
+
+ // Fetch it accordingly
+ attr = typeof prop === "boolean" && elem.getAttribute( name ),
+ detail = typeof prop === "boolean" ?
+
+ getSetInput && getSetAttribute ?
+ attr != null :
+ // oldIE fabricates an empty string for missing boolean attributes
+ // and conflates checked/selected into attroperties
+ ruseDefault.test( name ) ?
+ elem[ jQuery.camelCase( "default-" + name ) ] :
+ !!attr :
+
+ // fetch an attribute node for properties not recognized as boolean
+ elem.getAttributeNode( name );
+
+ return detail && detail.value !== false ?
+ name.toLowerCase() :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ if ( value === false ) {
+ // Remove boolean attributes when set to false
+ jQuery.removeAttr( elem, name );
+ } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
+ // IE<8 needs the *property* name
+ elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
+
+ // Use defaultChecked and defaultSelected for oldIE
+ } else {
+ elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
+ }
+
+ return name;
+ }
+};
+
+// fix oldIE value attroperty
+if ( !getSetInput || !getSetAttribute ) {
+ jQuery.attrHooks.value = {
+ get: function( elem, name ) {
+ var ret = elem.getAttributeNode( name );
+ return jQuery.nodeName( elem, "input" ) ?
+
+ // Ignore the value *property* by using defaultValue
+ elem.defaultValue :
+
+ ret && ret.specified ? ret.value : undefined;
+ },
+ set: function( elem, value, name ) {
+ if ( jQuery.nodeName( elem, "input" ) ) {
+ // Does not return so that setAttribute is also used
+ elem.defaultValue = value;
+ } else {
+ // Use nodeHook if defined (#1954); otherwise setAttribute is fine
+ return nodeHook && nodeHook.set( elem, value, name );
+ }
+ }
+ };
+}
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !getSetAttribute ) {
+
+ // Use this for any attribute in IE6/7
+ // This fixes almost every IE6/7 issue
+ nodeHook = jQuery.valHooks.button = {
+ get: function( elem, name ) {
+ var ret = elem.getAttributeNode( name );
+ return ret && ( name === "id" || name === "name" || name === "coords" ? ret.value !== "" : ret.specified ) ?
+ ret.value :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ // Set the existing or create a new attribute node
+ var ret = elem.getAttributeNode( name );
+ if ( !ret ) {
+ elem.setAttributeNode(
+ (ret = elem.ownerDocument.createAttribute( name ))
+ );
+ }
+
+ ret.value = value += "";
+
+ // Break association with cloned elements by also using setAttribute (#9646)
+ return name === "value" || value === elem.getAttribute( name ) ?
+ value :
+ undefined;
+ }
+ };
+
+ // Set contenteditable to false on removals(#10429)
+ // Setting to empty string throws an error as an invalid value
+ jQuery.attrHooks.contenteditable = {
+ get: nodeHook.get,
+ set: function( elem, value, name ) {
+ nodeHook.set( elem, value === "" ? false : value, name );
+ }
+ };
+
+ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
+ // This is for removals
+ jQuery.each([ "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ set: function( elem, value ) {
+ if ( value === "" ) {
+ elem.setAttribute( name, "auto" );
+ return value;
+ }
+ }
+ });
+ });
+}
+
+
+// Some attributes require a special call on IE
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+if ( !jQuery.support.hrefNormalized ) {
+ jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ get: function( elem ) {
+ var ret = elem.getAttribute( name, 2 );
+ return ret == null ? undefined : ret;
+ }
+ });
+ });
+
+ // href/src property should get the full normalized URL (#10299/#12915)
+ jQuery.each([ "href", "src" ], function( i, name ) {
+ jQuery.propHooks[ name ] = {
+ get: function( elem ) {
+ return elem.getAttribute( name, 4 );
+ }
+ };
+ });
+}
+
+if ( !jQuery.support.style ) {
+ jQuery.attrHooks.style = {
+ get: function( elem ) {
+ // Return undefined in the case of empty string
+ // Note: IE uppercases css property names, but if we were to .toLowerCase()
+ // .cssText, that would destroy case senstitivity in URL's, like in "background"
+ return elem.style.cssText || undefined;
+ },
+ set: function( elem, value ) {
+ return ( elem.style.cssText = value + "" );
+ }
+ };
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+ jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+ get: function( elem ) {
+ var parent = elem.parentNode;
+
+ if ( parent ) {
+ parent.selectedIndex;
+
+ // Make sure that it also works with optgroups, see #5701
+ if ( parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ }
+ return null;
+ }
+ });
+}
+
+// IE6/7 call enctype encoding
+if ( !jQuery.support.enctype ) {
+ jQuery.propFix.enctype = "encoding";
+}
+
+// Radios and checkboxes getter/setter
+if ( !jQuery.support.checkOn ) {
+ jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = {
+ get: function( elem ) {
+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ }
+ };
+ });
+}
+jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+ set: function( elem, value ) {
+ if ( jQuery.isArray( value ) ) {
+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+ }
+ }
+ });
+});
+var rformElems = /^(?:input|select|textarea)$/i,
+ rkeyEvent = /^key/,
+ rmouseEvent = /^(?:mouse|contextmenu)|click/,
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+ rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
+
+function returnTrue() {
+ return true;
+}
+
+function returnFalse() {
+ return false;
+}
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+ global: {},
+
+ add: function( elem, types, handler, data, selector ) {
+ var tmp, events, t, handleObjIn,
+ special, eventHandle, handleObj,
+ handlers, type, namespaces, origType,
+ elemData = jQuery._data( elem );
+
+ // Don't attach events to noData or text/comment nodes (but allow plain objects)
+ if ( !elemData ) {
+ return;
+ }
+
+ // Caller can pass in an object of custom data in lieu of the handler
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ selector = handleObjIn.selector;
+ }
+
+ // Make sure that the handler has a unique ID, used to find/remove it later
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure and main handler, if this is the first
+ if ( !(events = elemData.events) ) {
+ events = elemData.events = {};
+ }
+ if ( !(eventHandle = elemData.handle) ) {
+ eventHandle = elemData.handle = function( e ) {
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
+ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+ eventHandle.elem = elem;
+ }
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ types = ( types || "" ).match( core_rnotwhite ) || [""];
+ t = types.length;
+ while ( t-- ) {
+ tmp = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tmp[1];
+ namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+ // If event changes its type, use the special event handlers for the changed type
+ special = jQuery.event.special[ type ] || {};
+
+ // If selector defined, determine special event api type, otherwise given type
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+
+ // Update special based on newly reset type
+ special = jQuery.event.special[ type ] || {};
+
+ // handleObj is passed to all event handlers
+ handleObj = jQuery.extend({
+ type: type,
+ origType: origType,
+ data: data,
+ handler: handler,
+ guid: handler.guid,
+ selector: selector,
+ needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+ namespace: namespaces.join(".")
+ }, handleObjIn );
+
+ // Init the event handler queue if we're the first
+ if ( !(handlers = events[ type ]) ) {
+ handlers = events[ type ] = [];
+ handlers.delegateCount = 0;
+
+ // Only use addEventListener/attachEvent if the special events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add to the element's handler list, delegates in front
+ if ( selector ) {
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
+ } else {
+ handlers.push( handleObj );
+ }
+
+ // Keep track of which events have ever been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, selector, mappedTypes ) {
+ var j, handleObj, tmp,
+ origCount, t, events,
+ special, handlers, type,
+ namespaces, origType,
+ elemData = jQuery.hasData( elem ) && jQuery._data( elem );
+
+ if ( !elemData || !(events = elemData.events) ) {
+ return;
+ }
+
+ // Once for each type.namespace in types; type may be omitted
+ types = ( types || "" ).match( core_rnotwhite ) || [""];
+ t = types.length;
+ while ( t-- ) {
+ tmp = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tmp[1];
+ namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+ // Unbind all events (on this namespace, if provided) for the element
+ if ( !type ) {
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+ }
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+ handlers = events[ type ] || [];
+ tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
+
+ // Remove matching events
+ origCount = j = handlers.length;
+ while ( j-- ) {
+ handleObj = handlers[ j ];
+
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
+ ( !handler || handler.guid === handleObj.guid ) &&
+ ( !tmp || tmp.test( handleObj.namespace ) ) &&
+ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+ handlers.splice( j, 1 );
+
+ if ( handleObj.selector ) {
+ handlers.delegateCount--;
+ }
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+ }
+
+ // Remove generic event handler if we removed something and no more handlers exist
+ // (avoids potential for endless recursion during removal of special event handlers)
+ if ( origCount && !handlers.length ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ delete elemData.handle;
+
+ // removeData also checks for emptiness and clears the expando if empty
+ // so use it instead of delete
+ jQuery._removeData( elem, "events" );
+ }
+ },
+
+ trigger: function( event, data, elem, onlyHandlers ) {
+ var handle, ontype, cur,
+ bubbleType, special, tmp, i,
+ eventPath = [ elem || document ],
+ type = core_hasOwn.call( event, "type" ) ? event.type : event,
+ namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
+
+ cur = tmp = elem = elem || document;
+
+ // Don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+ return;
+ }
+
+ if ( type.indexOf(".") >= 0 ) {
+ // Namespaced trigger; create a regexp to match event type in handle()
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespaces.sort();
+ }
+ ontype = type.indexOf(":") < 0 && "on" + type;
+
+ // Caller can pass in a jQuery.Event object, Object, or just an event type string
+ event = event[ jQuery.expando ] ?
+ event :
+ new jQuery.Event( type, typeof event === "object" && event );
+
+ event.isTrigger = true;
+ event.namespace = namespaces.join(".");
+ event.namespace_re = event.namespace ?
+ new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
+ null;
+
+ // Clean up the event in case it is being reused
+ event.result = undefined;
+ if ( !event.target ) {
+ event.target = elem;
+ }
+
+ // Clone any incoming data and prepend the event, creating the handler arg list
+ data = data == null ?
+ [ event ] :
+ jQuery.makeArray( data, [ event ] );
+
+ // Allow special events to draw outside the lines
+ special = jQuery.event.special[ type ] || {};
+ if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
+ return;
+ }
+
+ // Determine event propagation path in advance, per W3C events spec (#9951)
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+ bubbleType = special.delegateType || type;
+ if ( !rfocusMorph.test( bubbleType + type ) ) {
+ cur = cur.parentNode;
+ }
+ for ( ; cur; cur = cur.parentNode ) {
+ eventPath.push( cur );
+ tmp = cur;
+ }
+
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
+ if ( tmp === (elem.ownerDocument || document) ) {
+ eventPath.push( tmp.defaultView || tmp.parentWindow || window );
+ }
+ }
+
+ // Fire handlers on the event path
+ i = 0;
+ while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
+
+ event.type = i > 1 ?
+ bubbleType :
+ special.bindType || type;
+
+ // jQuery handler
+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
+ if ( handle ) {
+ handle.apply( cur, data );
+ }
+
+ // Native handler
+ handle = ontype && cur[ ontype ];
+ if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
+ event.preventDefault();
+ }
+ }
+ event.type = type;
+
+ // If nobody prevented the default action, do it now
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+ if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
+ !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
+
+ // Call a native DOM method on the target with the same name name as the event.
+ // Can't use an .isFunction() check here because IE6/7 fails that test.
+ // Don't do default actions on window, that's where global variables be (#6170)
+ if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {
+
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ tmp = elem[ ontype ];
+
+ if ( tmp ) {
+ elem[ ontype ] = null;
+ }
+
+ // Prevent re-triggering of the same event, since we already bubbled it above
+ jQuery.event.triggered = type;
+ try {
+ elem[ type ]();
+ } catch ( e ) {
+ // IE<9 dies on focus/blur to hidden element (#1486,#12518)
+ // only reproducible on winXP IE8 native, not IE9 in IE8 mode
+ }
+ jQuery.event.triggered = undefined;
+
+ if ( tmp ) {
+ elem[ ontype ] = tmp;
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ dispatch: function( event ) {
+
+ // Make a writable jQuery.Event from the native event object
+ event = jQuery.event.fix( event );
+
+ var i, ret, handleObj, matched, j,
+ handlerQueue = [],
+ args = core_slice.call( arguments ),
+ handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
+ special = jQuery.event.special[ event.type ] || {};
+
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
+ args[0] = event;
+ event.delegateTarget = this;
+
+ // Call the preDispatch hook for the mapped type, and let it bail if desired
+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+ return;
+ }
+
+ // Determine handlers
+ handlerQueue = jQuery.event.handlers.call( this, event, handlers );
+
+ // Run delegates first; they may want to stop propagation beneath us
+ i = 0;
+ while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
+ event.currentTarget = matched.elem;
+
+ j = 0;
+ while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
+
+ // Triggered event must either 1) have no namespace, or
+ // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+ if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
+
+ event.handleObj = handleObj;
+ event.data = handleObj.data;
+
+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+ .apply( matched.elem, args );
+
+ if ( ret !== undefined ) {
+ if ( (event.result = ret) === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+ }
+ }
+
+ // Call the postDispatch hook for the mapped type
+ if ( special.postDispatch ) {
+ special.postDispatch.call( this, event );
+ }
+
+ return event.result;
+ },
+
+ handlers: function( event, handlers ) {
+ var sel, handleObj, matches, i,
+ handlerQueue = [],
+ delegateCount = handlers.delegateCount,
+ cur = event.target;
+
+ // Find delegate handlers
+ // Black-hole SVG <use> instance trees (#13180)
+ // Avoid non-left-click bubbling in Firefox (#3861)
+ if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
+
+ for ( ; cur != this; cur = cur.parentNode || this ) {
+
+ // Don't check non-elements (#13208)
+ // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
+ if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {
+ matches = [];
+ for ( i = 0; i < delegateCount; i++ ) {
+ handleObj = handlers[ i ];
+
+ // Don't conflict with Object.prototype properties (#13203)
+ sel = handleObj.selector + " ";
+
+ if ( matches[ sel ] === undefined ) {
+ matches[ sel ] = handleObj.needsContext ?
+ jQuery( sel, this ).index( cur ) >= 0 :
+ jQuery.find( sel, this, null, [ cur ] ).length;
+ }
+ if ( matches[ sel ] ) {
+ matches.push( handleObj );
+ }
+ }
+ if ( matches.length ) {
+ handlerQueue.push({ elem: cur, handlers: matches });
+ }
+ }
+ }
+ }
+
+ // Add the remaining (directly-bound) handlers
+ if ( delegateCount < handlers.length ) {
+ handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
+ }
+
+ return handlerQueue;
+ },
+
+ fix: function( event ) {
+ if ( event[ jQuery.expando ] ) {
+ return event;
+ }
+
+ // Create a writable copy of the event object and normalize some properties
+ var i, prop, copy,
+ type = event.type,
+ originalEvent = event,
+ fixHook = this.fixHooks[ type ];
+
+ if ( !fixHook ) {
+ this.fixHooks[ type ] = fixHook =
+ rmouseEvent.test( type ) ? this.mouseHooks :
+ rkeyEvent.test( type ) ? this.keyHooks :
+ {};
+ }
+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+ event = new jQuery.Event( originalEvent );
+
+ i = copy.length;
+ while ( i-- ) {
+ prop = copy[ i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Support: IE<9
+ // Fix target property (#1925)
+ if ( !event.target ) {
+ event.target = originalEvent.srcElement || document;
+ }
+
+ // Support: Chrome 23+, Safari?
+ // Target should not be a text node (#504, #13143)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // Support: IE<9
+ // For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
+ event.metaKey = !!event.metaKey;
+
+ return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
+ },
+
+ // Includes some event props shared by KeyEvent and MouseEvent
+ props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+ fixHooks: {},
+
+ keyHooks: {
+ props: "char charCode key keyCode".split(" "),
+ filter: function( event, original ) {
+
+ // Add which for key events
+ if ( event.which == null ) {
+ event.which = original.charCode != null ? original.charCode : original.keyCode;
+ }
+
+ return event;
+ }
+ },
+
+ mouseHooks: {
+ props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+ filter: function( event, original ) {
+ var body, eventDoc, doc,
+ button = original.button,
+ fromElement = original.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && original.clientX != null ) {
+ eventDoc = event.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+
+ event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && fromElement ) {
+ event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && button !== undefined ) {
+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+ }
+
+ return event;
+ }
+ },
+
+ special: {
+ load: {
+ // Prevent triggered image.load events from bubbling to window.load
+ noBubble: true
+ },
+ click: {
+ // For checkbox, fire native event so checked state will be right
+ trigger: function() {
+ if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
+ this.click();
+ return false;
+ }
+ }
+ },
+ focus: {
+ // Fire native event if possible so blur/focus sequence is correct
+ trigger: function() {
+ if ( this !== document.activeElement && this.focus ) {
+ try {
+ this.focus();
+ return false;
+ } catch ( e ) {
+ // Support: IE<9
+ // If we error on focus to hidden element (#1486, #12518),
+ // let .trigger() run the handlers
+ }
+ }
+ },
+ delegateType: "focusin"
+ },
+ blur: {
+ trigger: function() {
+ if ( this === document.activeElement && this.blur ) {
+ this.blur();
+ return false;
+ }
+ },
+ delegateType: "focusout"
+ },
+
+ beforeunload: {
+ postDispatch: function( event ) {
+
+ // Even when returnValue equals to undefined Firefox will still show alert
+ if ( event.result !== undefined ) {
+ event.originalEvent.returnValue = event.result;
+ }
+ }
+ }
+ },
+
+ simulate: function( type, elem, event, bubble ) {
+ // Piggyback on a donor event to simulate a different one.
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
+ // simulated event prevents default then we do the same on the donor.
+ var e = jQuery.extend(
+ new jQuery.Event(),
+ event,
+ { type: type,
+ isSimulated: true,
+ originalEvent: {}
+ }
+ );
+ if ( bubble ) {
+ jQuery.event.trigger( e, null, elem );
+ } else {
+ jQuery.event.dispatch.call( elem, e );
+ }
+ if ( e.isDefaultPrevented() ) {
+ event.preventDefault();
+ }
+ }
+};
+
+jQuery.removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+ if ( elem.removeEventListener ) {
+ elem.removeEventListener( type, handle, false );
+ }
+ } :
+ function( elem, type, handle ) {
+ var name = "on" + type;
+
+ if ( elem.detachEvent ) {
+
+ // #8545, #7054, preventing memory leaks for custom events in IE6-8
+ // detachEvent needed property on element, by name of that event, to properly expose it to GC
+ if ( typeof elem[ name ] === core_strundefined ) {
+ elem[ name ] = null;
+ }
+
+ elem.detachEvent( name, handle );
+ }
+ };
+
+jQuery.Event = function( src, props ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !(this instanceof jQuery.Event) ) {
+ return new jQuery.Event( src, props );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+
+ // Events bubbling up the document may have been marked as prevented
+ // by a handler lower down the tree; reflect the correct value.
+ this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+ src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
+
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // Put explicitly provided properties onto the event object
+ if ( props ) {
+ jQuery.extend( this, props );
+ }
+
+ // Create a timestamp if incoming event doesn't have one
+ this.timeStamp = src && src.timeStamp || jQuery.now();
+
+ // Mark it as fixed
+ this[ jQuery.expando ] = true;
+};
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse,
+
+ preventDefault: function() {
+ var e = this.originalEvent;
+
+ this.isDefaultPrevented = returnTrue;
+ if ( !e ) {
+ return;
+ }
+
+ // If preventDefault exists, run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+
+ // Support: IE
+ // Otherwise set the returnValue property of the original event to false
+ } else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ var e = this.originalEvent;
+
+ this.isPropagationStopped = returnTrue;
+ if ( !e ) {
+ return;
+ }
+ // If stopPropagation exists, run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+
+ // Support: IE
+ // Set the cancelBubble property of the original event to true
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ }
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ delegateType: fix,
+ bindType: fix,
+
+ handle: function( event ) {
+ var ret,
+ target = this,
+ related = event.relatedTarget,
+ handleObj = event.handleObj;
+
+ // For mousenter/leave call the handler if related is outside the target.
+ // NB: No relatedTarget if the mouse left/entered the browser window
+ if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+ event.type = handleObj.origType;
+ ret = handleObj.handler.apply( this, arguments );
+ event.type = fix;
+ }
+ return ret;
+ }
+ };
+});
+
+// IE submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+ jQuery.event.special.submit = {
+ setup: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Lazy-add a submit handler when a descendant form may potentially be submitted
+ jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+ // Node name check avoids a VML-related crash in IE (#9807)
+ var elem = e.target,
+ form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+ if ( form && !jQuery._data( form, "submitBubbles" ) ) {
+ jQuery.event.add( form, "submit._submit", function( event ) {
+ event._submit_bubble = true;
+ });
+ jQuery._data( form, "submitBubbles", true );
+ }
+ });
+ // return undefined since we don't need an event listener
+ },
+
+ postDispatch: function( event ) {
+ // If form was submitted by the user, bubble the event up the tree
+ if ( event._submit_bubble ) {
+ delete event._submit_bubble;
+ if ( this.parentNode && !event.isTrigger ) {
+ jQuery.event.simulate( "submit", this.parentNode, event, true );
+ }
+ }
+ },
+
+ teardown: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+ jQuery.event.remove( this, "._submit" );
+ }
+ };
+}
+
+// IE change delegation and checkbox/radio fix
+if ( !jQuery.support.changeBubbles ) {
+
+ jQuery.event.special.change = {
+
+ setup: function() {
+
+ if ( rformElems.test( this.nodeName ) ) {
+ // IE doesn't fire change on a check/radio until blur; trigger it on click
+ // after a propertychange. Eat the blur-change in special.change.handle.
+ // This still fires onchange a second time for check/radio after blur.
+ if ( this.type === "checkbox" || this.type === "radio" ) {
+ jQuery.event.add( this, "propertychange._change", function( event ) {
+ if ( event.originalEvent.propertyName === "checked" ) {
+ this._just_changed = true;
+ }
+ });
+ jQuery.event.add( this, "click._change", function( event ) {
+ if ( this._just_changed && !event.isTrigger ) {
+ this._just_changed = false;
+ }
+ // Allow triggered, simulated change events (#11500)
+ jQuery.event.simulate( "change", this, event, true );
+ });
+ }
+ return false;
+ }
+ // Delegated event; lazy-add a change handler on descendant inputs
+ jQuery.event.add( this, "beforeactivate._change", function( e ) {
+ var elem = e.target;
+
+ if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
+ jQuery.event.add( elem, "change._change", function( event ) {
+ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+ jQuery.event.simulate( "change", this.parentNode, event, true );
+ }
+ });
+ jQuery._data( elem, "changeBubbles", true );
+ }
+ });
+ },
+
+ handle: function( event ) {
+ var elem = event.target;
+
+ // Swallow native change events from checkbox/radio, we already triggered them above
+ if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+ return event.handleObj.handler.apply( this, arguments );
+ }
+ },
+
+ teardown: function() {
+ jQuery.event.remove( this, "._change" );
+
+ return !rformElems.test( this.nodeName );
+ }
+ };
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+ // Attach a single capturing handler while someone wants focusin/focusout
+ var attaches = 0,
+ handler = function( event ) {
+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+ };
+
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ if ( attaches++ === 0 ) {
+ document.addEventListener( orig, handler, true );
+ }
+ },
+ teardown: function() {
+ if ( --attaches === 0 ) {
+ document.removeEventListener( orig, handler, true );
+ }
+ }
+ };
+ });
+}
+
+jQuery.fn.extend({
+
+ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+ var type, origFn;
+
+ // Types can be a map of types/handlers
+ if ( typeof types === "object" ) {
+ // ( types-Object, selector, data )
+ if ( typeof selector !== "string" ) {
+ // ( types-Object, data )
+ data = data || selector;
+ selector = undefined;
+ }
+ for ( type in types ) {
+ this.on( type, selector, data, types[ type ], one );
+ }
+ return this;
+ }
+
+ if ( data == null && fn == null ) {
+ // ( types, fn )
+ fn = selector;
+ data = selector = undefined;
+ } else if ( fn == null ) {
+ if ( typeof selector === "string" ) {
+ // ( types, selector, fn )
+ fn = data;
+ data = undefined;
+ } else {
+ // ( types, data, fn )
+ fn = data;
+ data = selector;
+ selector = undefined;
+ }
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ } else if ( !fn ) {
+ return this;
+ }
+
+ if ( one === 1 ) {
+ origFn = fn;
+ fn = function( event ) {
+ // Can use an empty set, since event contains the info
+ jQuery().off( event );
+ return origFn.apply( this, arguments );
+ };
+ // Use same guid so caller can remove using origFn
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+ }
+ return this.each( function() {
+ jQuery.event.add( this, types, fn, data, selector );
+ });
+ },
+ one: function( types, selector, data, fn ) {
+ return this.on( types, selector, data, fn, 1 );
+ },
+ off: function( types, selector, fn ) {
+ var handleObj, type;
+ if ( types && types.preventDefault && types.handleObj ) {
+ // ( event ) dispatched jQuery.Event
+ handleObj = types.handleObj;
+ jQuery( types.delegateTarget ).off(
+ handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+ handleObj.selector,
+ handleObj.handler
+ );
+ return this;
+ }
+ if ( typeof types === "object" ) {
+ // ( types-object [, selector] )
+ for ( type in types ) {
+ this.off( type, selector, types[ type ] );
+ }
+ return this;
+ }
+ if ( selector === false || typeof selector === "function" ) {
+ // ( types [, fn] )
+ fn = selector;
+ selector = undefined;
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ }
+ return this.each(function() {
+ jQuery.event.remove( this, types, fn, selector );
+ });
+ },
+
+ bind: function( types, data, fn ) {
+ return this.on( types, null, data, fn );
+ },
+ unbind: function( types, fn ) {
+ return this.off( types, null, fn );
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.on( types, selector, data, fn );
+ },
+ undelegate: function( selector, types, fn ) {
+ // ( namespace ) or ( selector, types [, fn] )
+ return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+ triggerHandler: function( type, data ) {
+ var elem = this[0];
+ if ( elem ) {
+ return jQuery.event.trigger( type, data, elem, true );
+ }
+ }
+});
+/*!
+ * Sizzle CSS Selector Engine
+ * Copyright 2012 jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://sizzlejs.com/
+ */
+(function( window, undefined ) {
+
+var i,
+ cachedruns,
+ Expr,
+ getText,
+ isXML,
+ compile,
+ hasDuplicate,
+ outermostContext,
+
+ // Local document vars
+ setDocument,
+ document,
+ docElem,
+ documentIsXML,
+ rbuggyQSA,
+ rbuggyMatches,
+ matches,
+ contains,
+ sortOrder,
+
+ // Instance-specific data
+ expando = "sizzle" + -(new Date()),
+ preferredDoc = window.document,
+ support = {},
+ dirruns = 0,
+ done = 0,
+ classCache = createCache(),
+ tokenCache = createCache(),
+ compilerCache = createCache(),
+
+ // General-purpose constants
+ strundefined = typeof undefined,
+ MAX_NEGATIVE = 1 << 31,
+
+ // Array methods
+ arr = [],
+ pop = arr.pop,
+ push = arr.push,
+ slice = arr.slice,
+ // Use a stripped-down indexOf if we can't use a native one
+ indexOf = arr.indexOf || function( elem ) {
+ var i = 0,
+ len = this.length;
+ for ( ; i < len; i++ ) {
+ if ( this[i] === elem ) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+
+ // Regular expressions
+
+ // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
+ whitespace = "[\\x20\\t\\r\\n\\f]",
+ // http://www.w3.org/TR/css3-syntax/#characters
+ characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+
+ // Loosely modeled on CSS identifier characters
+ // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
+ // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+ identifier = characterEncoding.replace( "w", "w#" ),
+
+ // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
+ operators = "([*^$|!~]?=)",
+ attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
+ "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
+
+ // Prefer arguments quoted,
+ // then not containing pseudos/brackets,
+ // then attribute selectors/non-parenthetical expressions,
+ // then anything else
+ // These preferences are here to reduce the number of selectors
+ // needing tokenize in the PSEUDO preFilter
+ pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
+
+ // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+ rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+ rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+ rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
+ rpseudo = new RegExp( pseudos ),
+ ridentifier = new RegExp( "^" + identifier + "$" ),
+
+ matchExpr = {
+ "ID": new RegExp( "^#(" + characterEncoding + ")" ),
+ "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+ "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
+ "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+ "ATTR": new RegExp( "^" + attributes ),
+ "PSEUDO": new RegExp( "^" + pseudos ),
+ "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+ "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+ "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+ // For use in libraries implementing .is()
+ // We use this for POS matching in `select`
+ "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+ whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+ },
+
+ rsibling = /[\x20\t\r\n\f]*[+~]/,
+
+ rnative = /^[^{]+\{\s*\[native code/,
+
+ // Easily-parseable/retrievable ID or TAG or CLASS selectors
+ rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+
+ rinputs = /^(?:input|select|textarea|button)$/i,
+ rheader = /^h\d$/i,
+
+ rescape = /'|\\/g,
+ rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
+
+ // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+ runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,
+ funescape = function( _, escaped ) {
+ var high = "0x" + escaped - 0x10000;
+ // NaN means non-codepoint
+ return high !== high ?
+ escaped :
+ // BMP codepoint
+ high < 0 ?
+ String.fromCharCode( high + 0x10000 ) :
+ // Supplemental Plane codepoint (surrogate pair)
+ String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+ };
+
+// Use a stripped-down slice if we can't use a native one
+try {
+ slice.call( preferredDoc.documentElement.childNodes, 0 )[0].nodeType;
+} catch ( e ) {
+ slice = function( i ) {
+ var elem,
+ results = [];
+ while ( (elem = this[i++]) ) {
+ results.push( elem );
+ }
+ return results;
+ };
+}
+
+/**
+ * For feature detection
+ * @param {Function} fn The function to test for native support
+ */
+function isNative( fn ) {
+ return rnative.test( fn + "" );
+}
+
+/**
+ * Create key-value caches of limited size
+ * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
+ * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ * deleting the oldest entry
+ */
+function createCache() {
+ var cache,
+ keys = [];
+
+ return (cache = function( key, value ) {
+ // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
+ if ( keys.push( key += " " ) > Expr.cacheLength ) {
+ // Only keep the most recent entries
+ delete cache[ keys.shift() ];
+ }
+ return (cache[ key ] = value);
+ });
+}
+
+/**
+ * Mark a function for special use by Sizzle
+ * @param {Function} fn The function to mark
+ */
+function markFunction( fn ) {
+ fn[ expando ] = true;
+ return fn;
+}
+
+/**
+ * Support testing using an element
+ * @param {Function} fn Passed the created div and expects a boolean result
+ */
+function assert( fn ) {
+ var div = document.createElement("div");
+
+ try {
+ return fn( div );
+ } catch (e) {
+ return false;
+ } finally {
+ // release memory in IE
+ div = null;
+ }
+}
+
+function Sizzle( selector, context, results, seed ) {
+ var match, elem, m, nodeType,
+ // QSA vars
+ i, groups, old, nid, newContext, newSelector;
+
+ if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+ setDocument( context );
+ }
+
+ context = context || document;
+ results = results || [];
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
+ return [];
+ }
+
+ if ( !documentIsXML && !seed ) {
+
+ // Shortcuts
+ if ( (match = rquickExpr.exec( selector )) ) {
+ // Speed-up: Sizzle("#ID")
+ if ( (m = match[1]) ) {
+ if ( nodeType === 9 ) {
+ elem = context.getElementById( m );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE, Opera, and Webkit return items
+ // by name instead of ID
+ if ( elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ } else {
+ return results;
+ }
+ } else {
+ // Context is not a document
+ if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+ contains( context, elem ) && elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ }
+
+ // Speed-up: Sizzle("TAG")
+ } else if ( match[2] ) {
+ push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );
+ return results;
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( (m = match[3]) && support.getByClassName && context.getElementsByClassName ) {
+ push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );
+ return results;
+ }
+ }
+
+ // QSA path
+ if ( support.qsa && !rbuggyQSA.test(selector) ) {
+ old = true;
+ nid = expando;
+ newContext = context;
+ newSelector = nodeType === 9 && selector;
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ groups = tokenize( selector );
+
+ if ( (old = context.getAttribute("id")) ) {
+ nid = old.replace( rescape, "\\$&" );
+ } else {
+ context.setAttribute( "id", nid );
+ }
+ nid = "[id='" + nid + "'] ";
+
+ i = groups.length;
+ while ( i-- ) {
+ groups[i] = nid + toSelector( groups[i] );
+ }
+ newContext = rsibling.test( selector ) && context.parentNode || context;
+ newSelector = groups.join(",");
+ }
+
+ if ( newSelector ) {
+ try {
+ push.apply( results, slice.call( newContext.querySelectorAll(
+ newSelector
+ ), 0 ) );
+ return results;
+ } catch(qsaError) {
+ } finally {
+ if ( !old ) {
+ context.removeAttribute("id");
+ }
+ }
+ }
+ }
+ }
+
+ // All others
+ return select( selector.replace( rtrim, "$1" ), context, results, seed );
+}
+
+/**
+ * Detect xml
+ * @param {Element|Object} elem An element or a document
+ */
+isXML = Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+/**
+ * Sets document-related variables once based on the current document
+ * @param {Element|Object} [doc] An element or document object to use to set the document
+ * @returns {Object} Returns the current document
+ */
+setDocument = Sizzle.setDocument = function( node ) {
+ var doc = node ? node.ownerDocument || node : preferredDoc;
+
+ // If no document and documentElement is available, return
+ if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+ return document;
+ }
+
+ // Set our document
+ document = doc;
+ docElem = doc.documentElement;
+
+ // Support tests
+ documentIsXML = isXML( doc );
+
+ // Check if getElementsByTagName("*") returns only elements
+ support.tagNameNoComments = assert(function( div ) {
+ div.appendChild( doc.createComment("") );
+ return !div.getElementsByTagName("*").length;
+ });
+
+ // Check if attributes should be retrieved by attribute nodes
+ support.attributes = assert(function( div ) {
+ div.innerHTML = "<select></select>";
+ var type = typeof div.lastChild.getAttribute("multiple");
+ // IE8 returns a string for some attributes even when not present
+ return type !== "boolean" && type !== "string";
+ });
+
+ // Check if getElementsByClassName can be trusted
+ support.getByClassName = assert(function( div ) {
+ // Opera can't find a second classname (in 9.6)
+ div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";
+ if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
+ return false;
+ }
+
+ // Safari 3.2 caches class attributes and doesn't catch changes
+ div.lastChild.className = "e";
+ return div.getElementsByClassName("e").length === 2;
+ });
+
+ // Check if getElementById returns elements by name
+ // Check if getElementsByName privileges form controls or returns elements by ID
+ support.getByName = assert(function( div ) {
+ // Inject content
+ div.id = expando + 0;
+ div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>";
+ docElem.insertBefore( div, docElem.firstChild );
+
+ // Test
+ var pass = doc.getElementsByName &&
+ // buggy browsers will return fewer than the correct 2
+ doc.getElementsByName( expando ).length === 2 +
+ // buggy browsers will return more than the correct 0
+ doc.getElementsByName( expando + 0 ).length;
+ support.getIdNotName = !doc.getElementById( expando );
+
+ // Cleanup
+ docElem.removeChild( div );
+
+ return pass;
+ });
+
+ // IE6/7 return modified attributes
+ Expr.attrHandle = assert(function( div ) {
+ div.innerHTML = "<a href='#'></a>";
+ return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
+ div.firstChild.getAttribute("href") === "#";
+ }) ?
+ {} :
+ {
+ "href": function( elem ) {
+ return elem.getAttribute( "href", 2 );
+ },
+ "type": function( elem ) {
+ return elem.getAttribute("type");
+ }
+ };
+
+ // ID find and filter
+ if ( support.getIdNotName ) {
+ Expr.find["ID"] = function( id, context ) {
+ if ( typeof context.getElementById !== strundefined && !documentIsXML ) {
+ var m = context.getElementById( id );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ };
+ Expr.filter["ID"] = function( id ) {
+ var attrId = id.replace( runescape, funescape );
+ return function( elem ) {
+ return elem.getAttribute("id") === attrId;
+ };
+ };
+ } else {
+ Expr.find["ID"] = function( id, context ) {
+ if ( typeof context.getElementById !== strundefined && !documentIsXML ) {
+ var m = context.getElementById( id );
+
+ return m ?
+ m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
+ [m] :
+ undefined :
+ [];
+ }
+ };
+ Expr.filter["ID"] = function( id ) {
+ var attrId = id.replace( runescape, funescape );
+ return function( elem ) {
+ var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+ return node && node.value === attrId;
+ };
+ };
+ }
+
+ // Tag
+ Expr.find["TAG"] = support.tagNameNoComments ?
+ function( tag, context ) {
+ if ( typeof context.getElementsByTagName !== strundefined ) {
+ return context.getElementsByTagName( tag );
+ }
+ } :
+ function( tag, context ) {
+ var elem,
+ tmp = [],
+ i = 0,
+ results = context.getElementsByTagName( tag );
+
+ // Filter out possible comments
+ if ( tag === "*" ) {
+ while ( (elem = results[i++]) ) {
+ if ( elem.nodeType === 1 ) {
+ tmp.push( elem );
+ }
+ }
+
+ return tmp;
+ }
+ return results;
+ };
+
+ // Name
+ Expr.find["NAME"] = support.getByName && function( tag, context ) {
+ if ( typeof context.getElementsByName !== strundefined ) {
+ return context.getElementsByName( name );
+ }
+ };
+
+ // Class
+ Expr.find["CLASS"] = support.getByClassName && function( className, context ) {
+ if ( typeof context.getElementsByClassName !== strundefined && !documentIsXML ) {
+ return context.getElementsByClassName( className );
+ }
+ };
+
+ // QSA and matchesSelector support
+
+ // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+ rbuggyMatches = [];
+
+ // qSa(:focus) reports false when true (Chrome 21),
+ // no need to also add to buggyMatches since matches checks buggyQSA
+ // A support test would require too much code (would include document ready)
+ rbuggyQSA = [ ":focus" ];
+
+ if ( (support.qsa = isNative(doc.querySelectorAll)) ) {
+ // Build QSA regex
+ // Regex strategy adopted from Diego Perini
+ assert(function( div ) {
+ // Select is set to empty string on purpose
+ // This is to test IE's treatment of not explictly
+ // setting a boolean content attribute,
+ // since its presence should be enough
+ // http://bugs.jquery.com/ticket/12359
+ div.innerHTML = "<select><option selected=''></option></select>";
+
+ // IE8 - Some boolean attributes are not treated correctly
+ if ( !div.querySelectorAll("[selected]").length ) {
+ rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
+ }
+
+ // Webkit/Opera - :checked should return selected option elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ // IE8 throws error here and will not see later tests
+ if ( !div.querySelectorAll(":checked").length ) {
+ rbuggyQSA.push(":checked");
+ }
+ });
+
+ assert(function( div ) {
+
+ // Opera 10-12/IE8 - ^= $= *= and empty values
+ // Should not select anything
+ div.innerHTML = "<input type='hidden' i=''/>";
+ if ( div.querySelectorAll("[i^='']").length ) {
+ rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
+ }
+
+ // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+ // IE8 throws error here and will not see later tests
+ if ( !div.querySelectorAll(":enabled").length ) {
+ rbuggyQSA.push( ":enabled", ":disabled" );
+ }
+
+ // Opera 10-11 does not throw on post-comma invalid pseudos
+ div.querySelectorAll("*,:x");
+ rbuggyQSA.push(",.*:");
+ });
+ }
+
+ if ( (support.matchesSelector = isNative( (matches = docElem.matchesSelector ||
+ docElem.mozMatchesSelector ||
+ docElem.webkitMatchesSelector ||
+ docElem.oMatchesSelector ||
+ docElem.msMatchesSelector) )) ) {
+
+ assert(function( div ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9)
+ support.disconnectedMatch = matches.call( div, "div" );
+
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( div, "[s!='']:x" );
+ rbuggyMatches.push( "!=", pseudos );
+ });
+ }
+
+ rbuggyQSA = new RegExp( rbuggyQSA.join("|") );
+ rbuggyMatches = new RegExp( rbuggyMatches.join("|") );
+
+ // Element contains another
+ // Purposefully does not implement inclusive descendent
+ // As in, an element does not contain itself
+ contains = isNative(docElem.contains) || docElem.compareDocumentPosition ?
+ function( a, b ) {
+ var adown = a.nodeType === 9 ? a.documentElement : a,
+ bup = b && b.parentNode;
+ return a === bup || !!( bup && bup.nodeType === 1 && (
+ adown.contains ?
+ adown.contains( bup ) :
+ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+ ));
+ } :
+ function( a, b ) {
+ if ( b ) {
+ while ( (b = b.parentNode) ) {
+ if ( b === a ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ // Document order sorting
+ sortOrder = docElem.compareDocumentPosition ?
+ function( a, b ) {
+ var compare;
+
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ if ( (compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b )) ) {
+ if ( compare & 1 || a.parentNode && a.parentNode.nodeType === 11 ) {
+ if ( a === doc || contains( preferredDoc, a ) ) {
+ return -1;
+ }
+ if ( b === doc || contains( preferredDoc, b ) ) {
+ return 1;
+ }
+ return 0;
+ }
+ return compare & 4 ? -1 : 1;
+ }
+
+ return a.compareDocumentPosition ? -1 : 1;
+ } :
+ function( a, b ) {
+ var cur,
+ i = 0,
+ aup = a.parentNode,
+ bup = b.parentNode,
+ ap = [ a ],
+ bp = [ b ];
+
+ // Exit early if the nodes are identical
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // Parentless nodes are either documents or disconnected
+ } else if ( !aup || !bup ) {
+ return a === doc ? -1 :
+ b === doc ? 1 :
+ aup ? -1 :
+ bup ? 1 :
+ 0;
+
+ // If the nodes are siblings, we can do a quick check
+ } else if ( aup === bup ) {
+ return siblingCheck( a, b );
+ }
+
+ // Otherwise we need full lists of their ancestors for comparison
+ cur = a;
+ while ( (cur = cur.parentNode) ) {
+ ap.unshift( cur );
+ }
+ cur = b;
+ while ( (cur = cur.parentNode) ) {
+ bp.unshift( cur );
+ }
+
+ // Walk down the tree looking for a discrepancy
+ while ( ap[i] === bp[i] ) {
+ i++;
+ }
+
+ return i ?
+ // Do a sibling check if the nodes have a common ancestor
+ siblingCheck( ap[i], bp[i] ) :
+
+ // Otherwise nodes in our document sort first
+ ap[i] === preferredDoc ? -1 :
+ bp[i] === preferredDoc ? 1 :
+ 0;
+ };
+
+ // Always assume the presence of duplicates if sort doesn't
+ // pass them to our comparison function (as in Google Chrome).
+ hasDuplicate = false;
+ [0, 0].sort( sortOrder );
+ support.detectDuplicates = hasDuplicate;
+
+ return document;
+};
+
+Sizzle.matches = function( expr, elements ) {
+ return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+ // Set document vars if needed
+ if ( ( elem.ownerDocument || elem ) !== document ) {
+ setDocument( elem );
+ }
+
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace( rattributeQuotes, "='$1']" );
+
+ // rbuggyQSA always contains :focus, so no need for an existence check
+ if ( support.matchesSelector && !documentIsXML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !rbuggyQSA.test(expr) ) {
+ try {
+ var ret = matches.call( elem, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || support.disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9
+ elem.document && elem.document.nodeType !== 11 ) {
+ return ret;
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle( expr, document, null, [elem] ).length > 0;
+};
+
+Sizzle.contains = function( context, elem ) {
+ // Set document vars if needed
+ if ( ( context.ownerDocument || context ) !== document ) {
+ setDocument( context );
+ }
+ return contains( context, elem );
+};
+
+Sizzle.attr = function( elem, name ) {
+ var val;
+
+ // Set document vars if needed
+ if ( ( elem.ownerDocument || elem ) !== document ) {
+ setDocument( elem );
+ }
+
+ if ( !documentIsXML ) {
+ name = name.toLowerCase();
+ }
+ if ( (val = Expr.attrHandle[ name ]) ) {
+ return val( elem );
+ }
+ if ( documentIsXML || support.attributes ) {
+ return elem.getAttribute( name );
+ }
+ return ( (val = elem.getAttributeNode( name )) || elem.getAttribute( name ) ) && elem[ name ] === true ?
+ name :
+ val && val.specified ? val.value : null;
+};
+
+Sizzle.error = function( msg ) {
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+// Document sorting and removing duplicates
+Sizzle.uniqueSort = function( results ) {
+ var elem,
+ duplicates = [],
+ i = 1,
+ j = 0;
+
+ // Unless we *know* we can detect duplicates, assume their presence
+ hasDuplicate = !support.detectDuplicates;
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ for ( ; (elem = results[i]); i++ ) {
+ if ( elem === results[ i - 1 ] ) {
+ j = duplicates.push( i );
+ }
+ }
+ while ( j-- ) {
+ results.splice( duplicates[ j ], 1 );
+ }
+ }
+
+ return results;
+};
+
+function siblingCheck( a, b ) {
+ var cur = b && a,
+ diff = cur && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE );
+
+ // Use IE sourceIndex if available on both nodes
+ if ( diff ) {
+ return diff;
+ }
+
+ // Check if b follows a
+ if ( cur ) {
+ while ( (cur = cur.nextSibling) ) {
+ if ( cur === b ) {
+ return -1;
+ }
+ }
+ }
+
+ return a ? 1 : -1;
+}
+
+// Returns a function to use in pseudos for input types
+function createInputPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === type;
+ };
+}
+
+// Returns a function to use in pseudos for buttons
+function createButtonPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && elem.type === type;
+ };
+}
+
+// Returns a function to use in pseudos for positionals
+function createPositionalPseudo( fn ) {
+ return markFunction(function( argument ) {
+ argument = +argument;
+ return markFunction(function( seed, matches ) {
+ var j,
+ matchIndexes = fn( [], seed.length, argument ),
+ i = matchIndexes.length;
+
+ // Match elements found at the specified indexes
+ while ( i-- ) {
+ if ( seed[ (j = matchIndexes[i]) ] ) {
+ seed[j] = !(matches[j] = seed[j]);
+ }
+ }
+ });
+ });
+}
+
+/**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+getText = Sizzle.getText = function( elem ) {
+ var node,
+ ret = "",
+ i = 0,
+ nodeType = elem.nodeType;
+
+ if ( !nodeType ) {
+ // If no nodeType, this is expected to be an array
+ for ( ; (node = elem[i]); i++ ) {
+ // Do not traverse comment nodes
+ ret += getText( node );
+ }
+ } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ // Use textContent for elements
+ // innerText usage removed for consistency of new lines (see #11153)
+ if ( typeof elem.textContent === "string" ) {
+ return elem.textContent;
+ } else {
+ // Traverse its children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ // Do not include comment or processing instruction nodes
+
+ return ret;
+};
+
+Expr = Sizzle.selectors = {
+
+ // Can be adjusted by the user
+ cacheLength: 50,
+
+ createPseudo: markFunction,
+
+ match: matchExpr,
+
+ find: {},
+
+ relative: {
+ ">": { dir: "parentNode", first: true },
+ " ": { dir: "parentNode" },
+ "+": { dir: "previousSibling", first: true },
+ "~": { dir: "previousSibling" }
+ },
+
+ preFilter: {
+ "ATTR": function( match ) {
+ match[1] = match[1].replace( runescape, funescape );
+
+ // Move the given value to match[3] whether quoted or unquoted
+ match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
+
+ if ( match[2] === "~=" ) {
+ match[3] = " " + match[3] + " ";
+ }
+
+ return match.slice( 0, 4 );
+ },
+
+ "CHILD": function( match ) {
+ /* matches from matchExpr["CHILD"]
+ 1 type (only|nth|...)
+ 2 what (child|of-type)
+ 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+ 4 xn-component of xn+y argument ([+-]?\d*n|)
+ 5 sign of xn-component
+ 6 x of xn-component
+ 7 sign of y-component
+ 8 y of y-component
+ */
+ match[1] = match[1].toLowerCase();
+
+ if ( match[1].slice( 0, 3 ) === "nth" ) {
+ // nth-* requires argument
+ if ( !match[3] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // numeric x and y parameters for Expr.filter.CHILD
+ // remember that false/true cast respectively to 0/1
+ match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
+ match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
+
+ // other types prohibit arguments
+ } else if ( match[3] ) {
+ Sizzle.error( match[0] );
+ }
+
+ return match;
+ },
+
+ "PSEUDO": function( match ) {
+ var excess,
+ unquoted = !match[5] && match[2];
+
+ if ( matchExpr["CHILD"].test( match[0] ) ) {
+ return null;
+ }
+
+ // Accept quoted arguments as-is
+ if ( match[4] ) {
+ match[2] = match[4];
+
+ // Strip excess characters from unquoted arguments
+ } else if ( unquoted && rpseudo.test( unquoted ) &&
+ // Get excess from tokenize (recursively)
+ (excess = tokenize( unquoted, true )) &&
+ // advance to the next closing parenthesis
+ (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+ // excess is a negative index
+ match[0] = match[0].slice( 0, excess );
+ match[2] = unquoted.slice( 0, excess );
+ }
+
+ // Return only captures needed by the pseudo filter method (type and argument)
+ return match.slice( 0, 3 );
+ }
+ },
+
+ filter: {
+
+ "TAG": function( nodeName ) {
+ if ( nodeName === "*" ) {
+ return function() { return true; };
+ }
+
+ nodeName = nodeName.replace( runescape, funescape ).toLowerCase();
+ return function( elem ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+ };
+ },
+
+ "CLASS": function( className ) {
+ var pattern = classCache[ className + " " ];
+
+ return pattern ||
+ (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+ classCache( className, function( elem ) {
+ return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
+ });
+ },
+
+ "ATTR": function( name, operator, check ) {
+ return function( elem ) {
+ var result = Sizzle.attr( elem, name );
+
+ if ( result == null ) {
+ return operator === "!=";
+ }
+ if ( !operator ) {
+ return true;
+ }
+
+ result += "";
+
+ return operator === "=" ? result === check :
+ operator === "!=" ? result !== check :
+ operator === "^=" ? check && result.indexOf( check ) === 0 :
+ operator === "*=" ? check && result.indexOf( check ) > -1 :
+ operator === "$=" ? check && result.slice( -check.length ) === check :
+ operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
+ operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+ false;
+ };
+ },
+
+ "CHILD": function( type, what, argument, first, last ) {
+ var simple = type.slice( 0, 3 ) !== "nth",
+ forward = type.slice( -4 ) !== "last",
+ ofType = what === "of-type";
+
+ return first === 1 && last === 0 ?
+
+ // Shortcut for :nth-*(n)
+ function( elem ) {
+ return !!elem.parentNode;
+ } :
+
+ function( elem, context, xml ) {
+ var cache, outerCache, node, diff, nodeIndex, start,
+ dir = simple !== forward ? "nextSibling" : "previousSibling",
+ parent = elem.parentNode,
+ name = ofType && elem.nodeName.toLowerCase(),
+ useCache = !xml && !ofType;
+
+ if ( parent ) {
+
+ // :(first|last|only)-(child|of-type)
+ if ( simple ) {
+ while ( dir ) {
+ node = elem;
+ while ( (node = node[ dir ]) ) {
+ if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
+ return false;
+ }
+ }
+ // Reverse direction for :only-* (if we haven't yet done so)
+ start = dir = type === "only" && !start && "nextSibling";
+ }
+ return true;
+ }
+
+ start = [ forward ? parent.firstChild : parent.lastChild ];
+
+ // non-xml :nth-child(...) stores cache data on `parent`
+ if ( forward && useCache ) {
+ // Seek `elem` from a previously-cached index
+ outerCache = parent[ expando ] || (parent[ expando ] = {});
+ cache = outerCache[ type ] || [];
+ nodeIndex = cache[0] === dirruns && cache[1];
+ diff = cache[0] === dirruns && cache[2];
+ node = nodeIndex && parent.childNodes[ nodeIndex ];
+
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
+
+ // Fallback to seeking `elem` from the start
+ (diff = nodeIndex = 0) || start.pop()) ) {
+
+ // When found, cache indexes on `parent` and break
+ if ( node.nodeType === 1 && ++diff && node === elem ) {
+ outerCache[ type ] = [ dirruns, nodeIndex, diff ];
+ break;
+ }
+ }
+
+ // Use previously-cached element index if available
+ } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
+ diff = cache[1];
+
+ // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
+ } else {
+ // Use the same loop as above to seek `elem` from the start
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
+ (diff = nodeIndex = 0) || start.pop()) ) {
+
+ if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
+ // Cache the index of each encountered element
+ if ( useCache ) {
+ (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
+ }
+
+ if ( node === elem ) {
+ break;
+ }
+ }
+ }
+ }
+
+ // Incorporate the offset, then check against cycle size
+ diff -= last;
+ return diff === first || ( diff % first === 0 && diff / first >= 0 );
+ }
+ };
+ },
+
+ "PSEUDO": function( pseudo, argument ) {
+ // pseudo-class names are case-insensitive
+ // http://www.w3.org/TR/selectors/#pseudo-classes
+ // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+ // Remember that setFilters inherits from pseudos
+ var args,
+ fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+ Sizzle.error( "unsupported pseudo: " + pseudo );
+
+ // The user may use createPseudo to indicate that
+ // arguments are needed to create the filter function
+ // just as Sizzle does
+ if ( fn[ expando ] ) {
+ return fn( argument );
+ }
+
+ // But maintain support for old signatures
+ if ( fn.length > 1 ) {
+ args = [ pseudo, pseudo, "", argument ];
+ return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+ markFunction(function( seed, matches ) {
+ var idx,
+ matched = fn( seed, argument ),
+ i = matched.length;
+ while ( i-- ) {
+ idx = indexOf.call( seed, matched[i] );
+ seed[ idx ] = !( matches[ idx ] = matched[i] );
+ }
+ }) :
+ function( elem ) {
+ return fn( elem, 0, args );
+ };
+ }
+
+ return fn;
+ }
+ },
+
+ pseudos: {
+ // Potentially complex pseudos
+ "not": markFunction(function( selector ) {
+ // Trim the selector passed to compile
+ // to avoid treating leading and trailing
+ // spaces as combinators
+ var input = [],
+ results = [],
+ matcher = compile( selector.replace( rtrim, "$1" ) );
+
+ return matcher[ expando ] ?
+ markFunction(function( seed, matches, context, xml ) {
+ var elem,
+ unmatched = matcher( seed, null, xml, [] ),
+ i = seed.length;
+
+ // Match elements unmatched by `matcher`
+ while ( i-- ) {
+ if ( (elem = unmatched[i]) ) {
+ seed[i] = !(matches[i] = elem);
+ }
+ }
+ }) :
+ function( elem, context, xml ) {
+ input[0] = elem;
+ matcher( input, null, xml, results );
+ return !results.pop();
+ };
+ }),
+
+ "has": markFunction(function( selector ) {
+ return function( elem ) {
+ return Sizzle( selector, elem ).length > 0;
+ };
+ }),
+
+ "contains": markFunction(function( text ) {
+ return function( elem ) {
+ return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+ };
+ }),
+
+ // "Whether an element is represented by a :lang() selector
+ // is based solely on the element's language value
+ // being equal to the identifier C,
+ // or beginning with the identifier C immediately followed by "-".
+ // The matching of C against the element's language value is performed case-insensitively.
+ // The identifier C does not have to be a valid language name."
+ // http://www.w3.org/TR/selectors/#lang-pseudo
+ "lang": markFunction( function( lang ) {
+ // lang value must be a valid identifider
+ if ( !ridentifier.test(lang || "") ) {
+ Sizzle.error( "unsupported lang: " + lang );
+ }
+ lang = lang.replace( runescape, funescape ).toLowerCase();
+ return function( elem ) {
+ var elemLang;
+ do {
+ if ( (elemLang = documentIsXML ?
+ elem.getAttribute("xml:lang") || elem.getAttribute("lang") :
+ elem.lang) ) {
+
+ elemLang = elemLang.toLowerCase();
+ return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
+ }
+ } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
+ return false;
+ };
+ }),
+
+ // Miscellaneous
+ "target": function( elem ) {
+ var hash = window.location && window.location.hash;
+ return hash && hash.slice( 1 ) === elem.id;
+ },
+
+ "root": function( elem ) {
+ return elem === docElem;
+ },
+
+ "focus": function( elem ) {
+ return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+ },
+
+ // Boolean properties
+ "enabled": function( elem ) {
+ return elem.disabled === false;
+ },
+
+ "disabled": function( elem ) {
+ return elem.disabled === true;
+ },
+
+ "checked": function( elem ) {
+ // In CSS3, :checked should return both checked and selected elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ var nodeName = elem.nodeName.toLowerCase();
+ return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+ },
+
+ "selected": function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ // Contents
+ "empty": function( elem ) {
+ // http://www.w3.org/TR/selectors/#empty-pseudo
+ // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
+ // not comment, processing instructions, or others
+ // Thanks to Diego Perini for the nodeName shortcut
+ // Greater than "@" means alpha characters (specifically not starting with "#" or "?")
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ "parent": function( elem ) {
+ return !Expr.pseudos["empty"]( elem );
+ },
+
+ // Element/input types
+ "header": function( elem ) {
+ return rheader.test( elem.nodeName );
+ },
+
+ "input": function( elem ) {
+ return rinputs.test( elem.nodeName );
+ },
+
+ "button": function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === "button" || name === "button";
+ },
+
+ "text": function( elem ) {
+ var attr;
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return elem.nodeName.toLowerCase() === "input" &&
+ elem.type === "text" &&
+ ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
+ },
+
+ // Position-in-collection
+ "first": createPositionalPseudo(function() {
+ return [ 0 ];
+ }),
+
+ "last": createPositionalPseudo(function( matchIndexes, length ) {
+ return [ length - 1 ];
+ }),
+
+ "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ return [ argument < 0 ? argument + length : argument ];
+ }),
+
+ "even": createPositionalPseudo(function( matchIndexes, length ) {
+ var i = 0;
+ for ( ; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "odd": createPositionalPseudo(function( matchIndexes, length ) {
+ var i = 1;
+ for ( ; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ var i = argument < 0 ? argument + length : argument;
+ for ( ; --i >= 0; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ var i = argument < 0 ? argument + length : argument;
+ for ( ; ++i < length; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ })
+ }
+};
+
+// Add button/input type pseudos
+for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
+ Expr.pseudos[ i ] = createInputPseudo( i );
+}
+for ( i in { submit: true, reset: true } ) {
+ Expr.pseudos[ i ] = createButtonPseudo( i );
+}
+
+function tokenize( selector, parseOnly ) {
+ var matched, match, tokens, type,
+ soFar, groups, preFilters,
+ cached = tokenCache[ selector + " " ];
+
+ if ( cached ) {
+ return parseOnly ? 0 : cached.slice( 0 );
+ }
+
+ soFar = selector;
+ groups = [];
+ preFilters = Expr.preFilter;
+
+ while ( soFar ) {
+
+ // Comma and first run
+ if ( !matched || (match = rcomma.exec( soFar )) ) {
+ if ( match ) {
+ // Don't consume trailing commas as valid
+ soFar = soFar.slice( match[0].length ) || soFar;
+ }
+ groups.push( tokens = [] );
+ }
+
+ matched = false;
+
+ // Combinators
+ if ( (match = rcombinators.exec( soFar )) ) {
+ matched = match.shift();
+ tokens.push( {
+ value: matched,
+ // Cast descendant combinators to space
+ type: match[0].replace( rtrim, " " )
+ } );
+ soFar = soFar.slice( matched.length );
+ }
+
+ // Filters
+ for ( type in Expr.filter ) {
+ if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+ (match = preFilters[ type ]( match ))) ) {
+ matched = match.shift();
+ tokens.push( {
+ value: matched,
+ type: type,
+ matches: match
+ } );
+ soFar = soFar.slice( matched.length );
+ }
+ }
+
+ if ( !matched ) {
+ break;
+ }
+ }
+
+ // Return the length of the invalid excess
+ // if we're just parsing
+ // Otherwise, throw an error or return tokens
+ return parseOnly ?
+ soFar.length :
+ soFar ?
+ Sizzle.error( selector ) :
+ // Cache the tokens
+ tokenCache( selector, groups ).slice( 0 );
+}
+
+function toSelector( tokens ) {
+ var i = 0,
+ len = tokens.length,
+ selector = "";
+ for ( ; i < len; i++ ) {
+ selector += tokens[i].value;
+ }
+ return selector;
+}
+
+function addCombinator( matcher, combinator, base ) {
+ var dir = combinator.dir,
+ checkNonElements = base && dir === "parentNode",
+ doneName = done++;
+
+ return combinator.first ?
+ // Check against closest ancestor/preceding element
+ function( elem, context, xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ return matcher( elem, context, xml );
+ }
+ }
+ } :
+
+ // Check against all ancestor/preceding elements
+ function( elem, context, xml ) {
+ var data, cache, outerCache,
+ dirkey = dirruns + " " + doneName;
+
+ // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
+ if ( xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ if ( matcher( elem, context, xml ) ) {
+ return true;
+ }
+ }
+ }
+ } else {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ outerCache = elem[ expando ] || (elem[ expando ] = {});
+ if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
+ if ( (data = cache[1]) === true || data === cachedruns ) {
+ return data === true;
+ }
+ } else {
+ cache = outerCache[ dir ] = [ dirkey ];
+ cache[1] = matcher( elem, context, xml ) || cachedruns;
+ if ( cache[1] === true ) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ };
+}
+
+function elementMatcher( matchers ) {
+ return matchers.length > 1 ?
+ function( elem, context, xml ) {
+ var i = matchers.length;
+ while ( i-- ) {
+ if ( !matchers[i]( elem, context, xml ) ) {
+ return false;
+ }
+ }
+ return true;
+ } :
+ matchers[0];
+}
+
+function condense( unmatched, map, filter, context, xml ) {
+ var elem,
+ newUnmatched = [],
+ i = 0,
+ len = unmatched.length,
+ mapped = map != null;
+
+ for ( ; i < len; i++ ) {
+ if ( (elem = unmatched[i]) ) {
+ if ( !filter || filter( elem, context, xml ) ) {
+ newUnmatched.push( elem );
+ if ( mapped ) {
+ map.push( i );
+ }
+ }
+ }
+ }
+
+ return newUnmatched;
+}
+
+function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+ if ( postFilter && !postFilter[ expando ] ) {
+ postFilter = setMatcher( postFilter );
+ }
+ if ( postFinder && !postFinder[ expando ] ) {
+ postFinder = setMatcher( postFinder, postSelector );
+ }
+ return markFunction(function( seed, results, context, xml ) {
+ var temp, i, elem,
+ preMap = [],
+ postMap = [],
+ preexisting = results.length,
+
+ // Get initial elements from seed or context
+ elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+ // Prefilter to get matcher input, preserving a map for seed-results synchronization
+ matcherIn = preFilter && ( seed || !selector ) ?
+ condense( elems, preMap, preFilter, context, xml ) :
+ elems,
+
+ matcherOut = matcher ?
+ // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+ postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+ // ...intermediate processing is necessary
+ [] :
+
+ // ...otherwise use results directly
+ results :
+ matcherIn;
+
+ // Find primary matches
+ if ( matcher ) {
+ matcher( matcherIn, matcherOut, context, xml );
+ }
+
+ // Apply postFilter
+ if ( postFilter ) {
+ temp = condense( matcherOut, postMap );
+ postFilter( temp, [], context, xml );
+
+ // Un-match failing elements by moving them back to matcherIn
+ i = temp.length;
+ while ( i-- ) {
+ if ( (elem = temp[i]) ) {
+ matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+ }
+ }
+ }
+
+ if ( seed ) {
+ if ( postFinder || preFilter ) {
+ if ( postFinder ) {
+ // Get the final matcherOut by condensing this intermediate into postFinder contexts
+ temp = [];
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) ) {
+ // Restore matcherIn since elem is not yet a final match
+ temp.push( (matcherIn[i] = elem) );
+ }
+ }
+ postFinder( null, (matcherOut = []), temp, xml );
+ }
+
+ // Move matched elements from seed to results to keep them synchronized
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) &&
+ (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+
+ seed[temp] = !(results[temp] = elem);
+ }
+ }
+ }
+
+ // Add elements to results, through postFinder if defined
+ } else {
+ matcherOut = condense(
+ matcherOut === results ?
+ matcherOut.splice( preexisting, matcherOut.length ) :
+ matcherOut
+ );
+ if ( postFinder ) {
+ postFinder( null, results, matcherOut, xml );
+ } else {
+ push.apply( results, matcherOut );
+ }
+ }
+ });
+}
+
+function matcherFromTokens( tokens ) {
+ var checkContext, matcher, j,
+ len = tokens.length,
+ leadingRelative = Expr.relative[ tokens[0].type ],
+ implicitRelative = leadingRelative || Expr.relative[" "],
+ i = leadingRelative ? 1 : 0,
+
+ // The foundational matcher ensures that elements are reachable from top-level context(s)
+ matchContext = addCombinator( function( elem ) {
+ return elem === checkContext;
+ }, implicitRelative, true ),
+ matchAnyContext = addCombinator( function( elem ) {
+ return indexOf.call( checkContext, elem ) > -1;
+ }, implicitRelative, true ),
+ matchers = [ function( elem, context, xml ) {
+ return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+ (checkContext = context).nodeType ?
+ matchContext( elem, context, xml ) :
+ matchAnyContext( elem, context, xml ) );
+ } ];
+
+ for ( ; i < len; i++ ) {
+ if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+ matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
+ } else {
+ matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+ // Return special upon seeing a positional matcher
+ if ( matcher[ expando ] ) {
+ // Find the next relative operator (if any) for proper handling
+ j = ++i;
+ for ( ; j < len; j++ ) {
+ if ( Expr.relative[ tokens[j].type ] ) {
+ break;
+ }
+ }
+ return setMatcher(
+ i > 1 && elementMatcher( matchers ),
+ i > 1 && toSelector( tokens.slice( 0, i - 1 ) ).replace( rtrim, "$1" ),
+ matcher,
+ i < j && matcherFromTokens( tokens.slice( i, j ) ),
+ j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+ j < len && toSelector( tokens )
+ );
+ }
+ matchers.push( matcher );
+ }
+ }
+
+ return elementMatcher( matchers );
+}
+
+function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+ // A counter to specify which element is currently being matched
+ var matcherCachedRuns = 0,
+ bySet = setMatchers.length > 0,
+ byElement = elementMatchers.length > 0,
+ superMatcher = function( seed, context, xml, results, expandContext ) {
+ var elem, j, matcher,
+ setMatched = [],
+ matchedCount = 0,
+ i = "0",
+ unmatched = seed && [],
+ outermost = expandContext != null,
+ contextBackup = outermostContext,
+ // We must always have either seed elements or context
+ elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
+ // Use integer dirruns iff this is the outermost matcher
+ dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
+
+ if ( outermost ) {
+ outermostContext = context !== document && context;
+ cachedruns = matcherCachedRuns;
+ }
+
+ // Add elements passing elementMatchers directly to results
+ // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
+ for ( ; (elem = elems[i]) != null; i++ ) {
+ if ( byElement && elem ) {
+ j = 0;
+ while ( (matcher = elementMatchers[j++]) ) {
+ if ( matcher( elem, context, xml ) ) {
+ results.push( elem );
+ break;
+ }
+ }
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ cachedruns = ++matcherCachedRuns;
+ }
+ }
+
+ // Track unmatched elements for set filters
+ if ( bySet ) {
+ // They will have gone through all possible matchers
+ if ( (elem = !matcher && elem) ) {
+ matchedCount--;
+ }
+
+ // Lengthen the array for every element, matched or not
+ if ( seed ) {
+ unmatched.push( elem );
+ }
+ }
+ }
+
+ // Apply set filters to unmatched elements
+ matchedCount += i;
+ if ( bySet && i !== matchedCount ) {
+ j = 0;
+ while ( (matcher = setMatchers[j++]) ) {
+ matcher( unmatched, setMatched, context, xml );
+ }
+
+ if ( seed ) {
+ // Reintegrate element matches to eliminate the need for sorting
+ if ( matchedCount > 0 ) {
+ while ( i-- ) {
+ if ( !(unmatched[i] || setMatched[i]) ) {
+ setMatched[i] = pop.call( results );
+ }
+ }
+ }
+
+ // Discard index placeholder values to get only actual matches
+ setMatched = condense( setMatched );
+ }
+
+ // Add matches to results
+ push.apply( results, setMatched );
+
+ // Seedless set matches succeeding multiple successful matchers stipulate sorting
+ if ( outermost && !seed && setMatched.length > 0 &&
+ ( matchedCount + setMatchers.length ) > 1 ) {
+
+ Sizzle.uniqueSort( results );
+ }
+ }
+
+ // Override manipulation of globals by nested matchers
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ outermostContext = contextBackup;
+ }
+
+ return unmatched;
+ };
+
+ return bySet ?
+ markFunction( superMatcher ) :
+ superMatcher;
+}
+
+compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
+ var i,
+ setMatchers = [],
+ elementMatchers = [],
+ cached = compilerCache[ selector + " " ];
+
+ if ( !cached ) {
+ // Generate a function of recursive functions that can be used to check each element
+ if ( !group ) {
+ group = tokenize( selector );
+ }
+ i = group.length;
+ while ( i-- ) {
+ cached = matcherFromTokens( group[i] );
+ if ( cached[ expando ] ) {
+ setMatchers.push( cached );
+ } else {
+ elementMatchers.push( cached );
+ }
+ }
+
+ // Cache the compiled function
+ cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+ }
+ return cached;
+};
+
+function multipleContexts( selector, contexts, results ) {
+ var i = 0,
+ len = contexts.length;
+ for ( ; i < len; i++ ) {
+ Sizzle( selector, contexts[i], results );
+ }
+ return results;
+}
+
+function select( selector, context, results, seed ) {
+ var i, tokens, token, type, find,
+ match = tokenize( selector );
+
+ if ( !seed ) {
+ // Try to minimize operations if there is only one group
+ if ( match.length === 1 ) {
+
+ // Take a shortcut and set the context if the root selector is an ID
+ tokens = match[0] = match[0].slice( 0 );
+ if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+ context.nodeType === 9 && !documentIsXML &&
+ Expr.relative[ tokens[1].type ] ) {
+
+ context = Expr.find["ID"]( token.matches[0].replace( runescape, funescape ), context )[0];
+ if ( !context ) {
+ return results;
+ }
+
+ selector = selector.slice( tokens.shift().value.length );
+ }
+
+ // Fetch a seed set for right-to-left matching
+ i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
+ while ( i-- ) {
+ token = tokens[i];
+
+ // Abort if we hit a combinator
+ if ( Expr.relative[ (type = token.type) ] ) {
+ break;
+ }
+ if ( (find = Expr.find[ type ]) ) {
+ // Search, expanding context for leading sibling combinators
+ if ( (seed = find(
+ token.matches[0].replace( runescape, funescape ),
+ rsibling.test( tokens[0].type ) && context.parentNode || context
+ )) ) {
+
+ // If seed is empty or no tokens remain, we can return early
+ tokens.splice( i, 1 );
+ selector = seed.length && toSelector( tokens );
+ if ( !selector ) {
+ push.apply( results, slice.call( seed, 0 ) );
+ return results;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Compile and execute a filtering function
+ // Provide `match` to avoid retokenization if we modified the selector above
+ compile( selector, match )(
+ seed,
+ context,
+ documentIsXML,
+ results,
+ rsibling.test( selector )
+ );
+ return results;
+}
+
+// Deprecated
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Easy API for creating new setFilters
+function setFilters() {}
+Expr.filters = setFilters.prototype = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
+// Initialize with the default document
+setDocument();
+
+// Override sizzle attribute retrieval
+Sizzle.attr = jQuery.attr;
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.pseudos;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})( window );
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prev(?:Until|All))/,
+ isSimple = /^.[^:#\[\.,]*$/,
+ rneedsContext = jQuery.expr.match.needsContext,
+ // methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var i, ret, self,
+ len = this.length;
+
+ if ( typeof selector !== "string" ) {
+ self = this;
+ return this.pushStack( jQuery( selector ).filter(function() {
+ for ( i = 0; i < len; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ }) );
+ }
+
+ ret = [];
+ for ( i = 0; i < len; i++ ) {
+ jQuery.find( selector, this[ i ], ret );
+ }
+
+ // Needed because $( selector, context ) becomes $( context ).find( selector )
+ ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
+ ret.selector = ( this.selector ? this.selector + " " : "" ) + selector;
+ return ret;
+ },
+
+ has: function( target ) {
+ var i,
+ targets = jQuery( target, this ),
+ len = targets.length;
+
+ return this.filter(function() {
+ for ( i = 0; i < len; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false) );
+ },
+
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true) );
+ },
+
+ is: function( selector ) {
+ return !!selector && (
+ typeof selector === "string" ?
+ // If this is a positional/relative selector, check membership in the returned set
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
+ rneedsContext.test( selector ) ?
+ jQuery( selector, this.context ).index( this[0] ) >= 0 :
+ jQuery.filter( selector, this ).length > 0 :
+ this.filter( selector ).length > 0 );
+ },
+
+ closest: function( selectors, context ) {
+ var cur,
+ i = 0,
+ l = this.length,
+ ret = [],
+ pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
+
+ for ( ; i < l; i++ ) {
+ cur = this[i];
+
+ while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+ ret.push( cur );
+ break;
+ }
+ cur = cur.parentNode;
+ }
+ }
+
+ return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+
+ // No argument, return index in parent
+ if ( !elem ) {
+ return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
+ }
+
+ // index in selector
+ if ( typeof elem === "string" ) {
+ return jQuery.inArray( this[0], jQuery( elem ) );
+ }
+
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ var set = typeof selector === "string" ?
+ jQuery( selector, context ) :
+ jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+ all = jQuery.merge( this.get(), set );
+
+ return this.pushStack( jQuery.unique(all) );
+ },
+
+ addBack: function( selector ) {
+ return this.add( selector == null ?
+ this.prevObject : this.prevObject.filter(selector)
+ );
+ }
+});
+
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
+function sibling( cur, dir ) {
+ do {
+ cur = cur[ dir ];
+ } while ( cur && cur.nodeType !== 1 );
+
+ return cur;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return sibling( elem, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return sibling( elem, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.merge( [], elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until );
+
+ if ( !runtil.test( name ) ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
+
+ if ( this.length > 1 && rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+
+ return this.pushStack( ret );
+ };
+});
+
+jQuery.extend({
+ filter: function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 ?
+ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+ jQuery.find.matches(expr, elems);
+ },
+
+ dir: function( elem, dir, until ) {
+ var matched = [],
+ cur = elem[ dir ];
+
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+
+ // Can't pass null or undefined to indexOf in Firefox 4
+ // Set to 0 to skip string check
+ qualifier = qualifier || 0;
+
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ var retVal = !!qualifier.call( elem, i, elem );
+ return retVal === keep;
+ });
+
+ } else if ( qualifier.nodeType ) {
+ return jQuery.grep(elements, function( elem ) {
+ return ( elem === qualifier ) === keep;
+ });
+
+ } else if ( typeof qualifier === "string" ) {
+ var filtered = jQuery.grep(elements, function( elem ) {
+ return elem.nodeType === 1;
+ });
+
+ if ( isSimple.test( qualifier ) ) {
+ return jQuery.filter(qualifier, filtered, !keep);
+ } else {
+ qualifier = jQuery.filter( qualifier, filtered );
+ }
+ }
+
+ return jQuery.grep(elements, function( elem ) {
+ return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
+ });
+}
+function createSafeFragment( document ) {
+ var list = nodeNames.split( "|" ),
+ safeFrag = document.createDocumentFragment();
+
+ if ( safeFrag.createElement ) {
+ while ( list.length ) {
+ safeFrag.createElement(
+ list.pop()
+ );
+ }
+ }
+ return safeFrag;
+}
+
+var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
+ "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+ rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
+ rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
+ rleadingWhitespace = /^\s+/,
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+ rtagName = /<([\w:]+)/,
+ rtbody = /<tbody/i,
+ rhtml = /<|&#?\w+;/,
+ rnoInnerhtml = /<(?:script|style|link)/i,
+ manipulation_rcheckableType = /^(?:checkbox|radio)$/i,
+ // checked="checked" or checked
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+ rscriptType = /^$|\/(?:java|ecma)script/i,
+ rscriptTypeMasked = /^true\/(.*)/,
+ rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
+
+ // We have to close these tags to support XHTML (#13200)
+ wrapMap = {
+ option: [ 1, "<select multiple='multiple'>", "</select>" ],
+ legend: [ 1, "<fieldset>", "</fieldset>" ],
+ area: [ 1, "<map>", "</map>" ],
+ param: [ 1, "<object>", "</object>" ],
+ thead: [ 1, "<table>", "</table>" ],
+ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+ col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+
+ // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
+ // unless wrapped in a div with non-breaking characters in front of it.
+ _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>" ]
+ },
+ safeFragment = createSafeFragment( document ),
+ fragmentDiv = safeFragment.appendChild( document.createElement("div") );
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+jQuery.fn.extend({
+ text: function( value ) {
+ return jQuery.access( this, function( value ) {
+ return value === undefined ?
+ jQuery.text( this ) :
+ this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
+ }, null, value, arguments.length );
+ },
+
+ wrapAll: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapAll( html.call(this, i) );
+ });
+ }
+
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+ if ( this[0].parentNode ) {
+ wrap.insertBefore( this[0] );
+ }
+
+ wrap.map(function() {
+ var elem = this;
+
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+ elem = elem.firstChild;
+ }
+
+ return elem;
+ }).append( this );
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapInner( html.call(this, i) );
+ });
+ }
+
+ return this.each(function() {
+ var self = jQuery( this ),
+ contents = self.contents();
+
+ if ( contents.length ) {
+ contents.wrapAll( html );
+
+ } else {
+ self.append( html );
+ }
+ });
+ },
+
+ wrap: function( html ) {
+ var isFunction = jQuery.isFunction( html );
+
+ return this.each(function(i) {
+ jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+ });
+ },
+
+ unwrap: function() {
+ return this.parent().each(function() {
+ if ( !jQuery.nodeName( this, "body" ) ) {
+ jQuery( this ).replaceWith( this.childNodes );
+ }
+ }).end();
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+ this.appendChild( elem );
+ }
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+ this.insertBefore( elem, this.firstChild );
+ }
+ });
+ },
+
+ before: function() {
+ return this.domManip( arguments, false, function( elem ) {
+ if ( this.parentNode ) {
+ this.parentNode.insertBefore( elem, this );
+ }
+ });
+ },
+
+ after: function() {
+ return this.domManip( arguments, false, function( elem ) {
+ if ( this.parentNode ) {
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ }
+ });
+ },
+
+ // keepData is for internal use only--do not document
+ remove: function( selector, keepData ) {
+ var elem,
+ i = 0;
+
+ for ( ; (elem = this[i]) != null; i++ ) {
+ if ( !selector || jQuery.filter( selector, [ elem ] ).length > 0 ) {
+ if ( !keepData && elem.nodeType === 1 ) {
+ jQuery.cleanData( getAll( elem ) );
+ }
+
+ if ( elem.parentNode ) {
+ if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
+ setGlobalEval( getAll( elem, "script" ) );
+ }
+ elem.parentNode.removeChild( elem );
+ }
+ }
+ }
+
+ return this;
+ },
+
+ empty: function() {
+ var elem,
+ i = 0;
+
+ for ( ; (elem = this[i]) != null; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( getAll( elem, false ) );
+ }
+
+ // Remove any remaining nodes
+ while ( elem.firstChild ) {
+ elem.removeChild( elem.firstChild );
+ }
+
+ // If this is a select, ensure that it displays empty (#12336)
+ // Support: IE<9
+ if ( elem.options && jQuery.nodeName( elem, "select" ) ) {
+ elem.options.length = 0;
+ }
+ }
+
+ return this;
+ },
+
+ clone: function( dataAndEvents, deepDataAndEvents ) {
+ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+ return this.map( function () {
+ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+ });
+ },
+
+ html: function( value ) {
+ return jQuery.access( this, function( value ) {
+ var elem = this[0] || {},
+ i = 0,
+ l = this.length;
+
+ if ( value === undefined ) {
+ return elem.nodeType === 1 ?
+ elem.innerHTML.replace( rinlinejQuery, "" ) :
+ undefined;
+ }
+
+ // See if we can take a shortcut and just use innerHTML
+ if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+ ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) &&
+ ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
+ !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
+
+ value = value.replace( rxhtmlTag, "<$1></$2>" );
+
+ try {
+ for (; i < l; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ elem = this[i] || {};
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( getAll( elem, false ) );
+ elem.innerHTML = value;
+ }
+ }
+
+ elem = 0;
+
+ // If using innerHTML throws an exception, use the fallback method
+ } catch(e) {}
+ }
+
+ if ( elem ) {
+ this.empty().append( value );
+ }
+ }, null, value, arguments.length );
+ },
+
+ replaceWith: function( value ) {
+ var isFunc = jQuery.isFunction( value );
+
+ // Make sure that the elements are removed from the DOM before they are inserted
+ // this can help fix replacing a parent with child elements
+ if ( !isFunc && typeof value !== "string" ) {
+ value = jQuery( value ).not( this ).detach();
+ }
+
+ return this.domManip( [ value ], true, function( elem ) {
+ var next = this.nextSibling,
+ parent = this.parentNode;
+
+ if ( parent ) {
+ jQuery( this ).remove();
+ parent.insertBefore( elem, next );
+ }
+ });
+ },
+
+ detach: function( selector ) {
+ return this.remove( selector, true );
+ },
+
+ domManip: function( args, table, callback ) {
+
+ // Flatten any nested arrays
+ args = core_concat.apply( [], args );
+
+ var first, node, hasScripts,
+ scripts, doc, fragment,
+ i = 0,
+ l = this.length,
+ set = this,
+ iNoClone = l - 1,
+ value = args[0],
+ isFunction = jQuery.isFunction( value );
+
+ // We can't cloneNode fragments that contain checked, in WebKit
+ if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) {
+ return this.each(function( index ) {
+ var self = set.eq( index );
+ if ( isFunction ) {
+ args[0] = value.call( this, index, table ? self.html() : undefined );
+ }
+ self.domManip( args, table, callback );
+ });
+ }
+
+ if ( l ) {
+ fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
+ first = fragment.firstChild;
+
+ if ( fragment.childNodes.length === 1 ) {
+ fragment = first;
+ }
+
+ if ( first ) {
+ table = table && jQuery.nodeName( first, "tr" );
+ scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
+ hasScripts = scripts.length;
+
+ // Use the original fragment for the last item instead of the first because it can end up
+ // being emptied incorrectly in certain situations (#8070).
+ for ( ; i < l; i++ ) {
+ node = fragment;
+
+ if ( i !== iNoClone ) {
+ node = jQuery.clone( node, true, true );
+
+ // Keep references to cloned scripts for later restoration
+ if ( hasScripts ) {
+ jQuery.merge( scripts, getAll( node, "script" ) );
+ }
+ }
+
+ callback.call(
+ table && jQuery.nodeName( this[i], "table" ) ?
+ findOrAppend( this[i], "tbody" ) :
+ this[i],
+ node,
+ i
+ );
+ }
+
+ if ( hasScripts ) {
+ doc = scripts[ scripts.length - 1 ].ownerDocument;
+
+ // Reenable scripts
+ jQuery.map( scripts, restoreScript );
+
+ // Evaluate executable scripts on first document insertion
+ for ( i = 0; i < hasScripts; i++ ) {
+ node = scripts[ i ];
+ if ( rscriptType.test( node.type || "" ) &&
+ !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
+
+ if ( node.src ) {
+ // Hope ajax is available...
+ jQuery.ajax({
+ url: node.src,
+ type: "GET",
+ dataType: "script",
+ async: false,
+ global: false,
+ "throws": true
+ });
+ } else {
+ jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
+ }
+ }
+ }
+ }
+
+ // Fix #11809: Avoid leaking memory
+ fragment = first = null;
+ }
+ }
+
+ return this;
+ }
+});
+
+function findOrAppend( elem, tag ) {
+ return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) );
+}
+
+// Replace/restore the type attribute of script elements for safe DOM manipulation
+function disableScript( elem ) {
+ var attr = elem.getAttributeNode("type");
+ elem.type = ( attr && attr.specified ) + "/" + elem.type;
+ return elem;
+}
+function restoreScript( elem ) {
+ var match = rscriptTypeMasked.exec( elem.type );
+ if ( match ) {
+ elem.type = match[1];
+ } else {
+ elem.removeAttribute("type");
+ }
+ return elem;
+}
+
+// Mark scripts as having already been evaluated
+function setGlobalEval( elems, refElements ) {
+ var elem,
+ i = 0;
+ for ( ; (elem = elems[i]) != null; i++ ) {
+ jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) );
+ }
+}
+
+function cloneCopyEvent( src, dest ) {
+
+ if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+ return;
+ }
+
+ var type, i, l,
+ oldData = jQuery._data( src ),
+ curData = jQuery._data( dest, oldData ),
+ events = oldData.events;
+
+ if ( events ) {
+ delete curData.handle;
+ curData.events = {};
+
+ for ( type in events ) {
+ for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+ jQuery.event.add( dest, type, events[ type ][ i ] );
+ }
+ }
+ }
+
+ // make the cloned public data object a copy from the original
+ if ( curData.data ) {
+ curData.data = jQuery.extend( {}, curData.data );
+ }
+}
+
+function fixCloneNodeIssues( src, dest ) {
+ var nodeName, e, data;
+
+ // We do not need to do anything for non-Elements
+ if ( dest.nodeType !== 1 ) {
+ return;
+ }
+
+ nodeName = dest.nodeName.toLowerCase();
+
+ // IE6-8 copies events bound via attachEvent when using cloneNode.
+ if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) {
+ data = jQuery._data( dest );
+
+ for ( e in data.events ) {
+ jQuery.removeEvent( dest, e, data.handle );
+ }
+
+ // Event data gets referenced instead of copied if the expando gets copied too
+ dest.removeAttribute( jQuery.expando );
+ }
+
+ // IE blanks contents when cloning scripts, and tries to evaluate newly-set text
+ if ( nodeName === "script" && dest.text !== src.text ) {
+ disableScript( dest ).text = src.text;
+ restoreScript( dest );
+
+ // IE6-10 improperly clones children of object elements using classid.
+ // IE10 throws NoModificationAllowedError if parent is null, #12132.
+ } else if ( nodeName === "object" ) {
+ if ( dest.parentNode ) {
+ dest.outerHTML = src.outerHTML;
+ }
+
+ // This path appears unavoidable for IE9. When cloning an object
+ // element in IE9, the outerHTML strategy above is not sufficient.
+ // If the src has innerHTML and the destination does not,
+ // copy the src.innerHTML into the dest.innerHTML. #10324
+ if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
+ dest.innerHTML = src.innerHTML;
+ }
+
+ } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) {
+ // IE6-8 fails to persist the checked state of a cloned checkbox
+ // or radio button. Worse, IE6-7 fail to give the cloned element
+ // a checked appearance if the defaultChecked value isn't also set
+
+ dest.defaultChecked = dest.checked = src.checked;
+
+ // IE6-7 get confused and end up setting the value of a cloned
+ // checkbox/radio button to an empty string instead of "on"
+ if ( dest.value !== src.value ) {
+ dest.value = src.value;
+ }
+
+ // IE6-8 fails to return the selected option to the default selected
+ // state when cloning options
+ } else if ( nodeName === "option" ) {
+ dest.defaultSelected = dest.selected = src.defaultSelected;
+
+ // IE6-8 fails to set the defaultValue to the correct value when
+ // cloning other types of input fields
+ } else if ( nodeName === "input" || nodeName === "textarea" ) {
+ dest.defaultValue = src.defaultValue;
+ }
+}
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var elems,
+ i = 0,
+ ret = [],
+ insert = jQuery( selector ),
+ last = insert.length - 1;
+
+ for ( ; i <= last; i++ ) {
+ elems = i === last ? this : this.clone(true);
+ jQuery( insert[i] )[ original ]( elems );
+
+ // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get()
+ core_push.apply( ret, elems.get() );
+ }
+
+ return this.pushStack( ret );
+ };
+});
+
+function getAll( context, tag ) {
+ var elems, elem,
+ i = 0,
+ found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) :
+ typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) :
+ undefined;
+
+ if ( !found ) {
+ for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) {
+ if ( !tag || jQuery.nodeName( elem, tag ) ) {
+ found.push( elem );
+ } else {
+ jQuery.merge( found, getAll( elem, tag ) );
+ }
+ }
+ }
+
+ return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
+ jQuery.merge( [ context ], found ) :
+ found;
+}
+
+// Used in buildFragment, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+ if ( manipulation_rcheckableType.test( elem.type ) ) {
+ elem.defaultChecked = elem.checked;
+ }
+}
+
+jQuery.extend({
+ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+ var destElements, node, clone, i, srcElements,
+ inPage = jQuery.contains( elem.ownerDocument, elem );
+
+ if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
+ clone = elem.cloneNode( true );
+
+ // IE<=8 does not properly clone detached, unknown element nodes
+ } else {
+ fragmentDiv.innerHTML = elem.outerHTML;
+ fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
+ }
+
+ if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
+ (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
+
+ // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
+ destElements = getAll( clone );
+ srcElements = getAll( elem );
+
+ // Fix all IE cloning issues
+ for ( i = 0; (node = srcElements[i]) != null; ++i ) {
+ // Ensure that the destination node is not null; Fixes #9587
+ if ( destElements[i] ) {
+ fixCloneNodeIssues( node, destElements[i] );
+ }
+ }
+ }
+
+ // Copy the events from the original to the clone
+ if ( dataAndEvents ) {
+ if ( deepDataAndEvents ) {
+ srcElements = srcElements || getAll( elem );
+ destElements = destElements || getAll( clone );
+
+ for ( i = 0; (node = srcElements[i]) != null; i++ ) {
+ cloneCopyEvent( node, destElements[i] );
+ }
+ } else {
+ cloneCopyEvent( elem, clone );
+ }
+ }
+
+ // Preserve script evaluation history
+ destElements = getAll( clone, "script" );
+ if ( destElements.length > 0 ) {
+ setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
+ }
+
+ destElements = srcElements = node = null;
+
+ // Return the cloned set
+ return clone;
+ },
+
+ buildFragment: function( elems, context, scripts, selection ) {
+ var j, elem, contains,
+ tmp, tag, tbody, wrap,
+ l = elems.length,
+
+ // Ensure a safe fragment
+ safe = createSafeFragment( context ),
+
+ nodes = [],
+ i = 0;
+
+ for ( ; i < l; i++ ) {
+ elem = elems[ i ];
+
+ if ( elem || elem === 0 ) {
+
+ // Add nodes directly
+ if ( jQuery.type( elem ) === "object" ) {
+ jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
+
+ // Convert non-html into a text node
+ } else if ( !rhtml.test( elem ) ) {
+ nodes.push( context.createTextNode( elem ) );
+
+ // Convert html into DOM nodes
+ } else {
+ tmp = tmp || safe.appendChild( context.createElement("div") );
+
+ // Deserialize a standard representation
+ tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase();
+ wrap = wrapMap[ tag ] || wrapMap._default;
+
+ tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2];
+
+ // Descend through wrappers to the right content
+ j = wrap[0];
+ while ( j-- ) {
+ tmp = tmp.lastChild;
+ }
+
+ // Manually add leading whitespace removed by IE
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+ nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
+ }
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ elem = tag === "table" && !rtbody.test( elem ) ?
+ tmp.firstChild :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] === "<table>" && !rtbody.test( elem ) ?
+ tmp :
+ 0;
+
+ j = elem && elem.childNodes.length;
+ while ( j-- ) {
+ if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) {
+ elem.removeChild( tbody );
+ }
+ }
+ }
+
+ jQuery.merge( nodes, tmp.childNodes );
+
+ // Fix #12392 for WebKit and IE > 9
+ tmp.textContent = "";
+
+ // Fix #12392 for oldIE
+ while ( tmp.firstChild ) {
+ tmp.removeChild( tmp.firstChild );
+ }
+
+ // Remember the top-level container for proper cleanup
+ tmp = safe.lastChild;
+ }
+ }
+ }
+
+ // Fix #11356: Clear elements from fragment
+ if ( tmp ) {
+ safe.removeChild( tmp );
+ }
+
+ // Reset defaultChecked for any radios and checkboxes
+ // about to be appended to the DOM in IE 6/7 (#8060)
+ if ( !jQuery.support.appendChecked ) {
+ jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
+ }
+
+ i = 0;
+ while ( (elem = nodes[ i++ ]) ) {
+
+ // #4087 - If origin and destination elements are the same, and this is
+ // that element, do not do anything
+ if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
+ continue;
+ }
+
+ contains = jQuery.contains( elem.ownerDocument, elem );
+
+ // Append to fragment
+ tmp = getAll( safe.appendChild( elem ), "script" );
+
+ // Preserve script evaluation history
+ if ( contains ) {
+ setGlobalEval( tmp );
+ }
+
+ // Capture executables
+ if ( scripts ) {
+ j = 0;
+ while ( (elem = tmp[ j++ ]) ) {
+ if ( rscriptType.test( elem.type || "" ) ) {
+ scripts.push( elem );
+ }
+ }
+ }
+ }
+
+ tmp = null;
+
+ return safe;
+ },
+
+ cleanData: function( elems, /* internal */ acceptData ) {
+ var elem, type, id, data,
+ i = 0,
+ internalKey = jQuery.expando,
+ cache = jQuery.cache,
+ deleteExpando = jQuery.support.deleteExpando,
+ special = jQuery.event.special;
+
+ for ( ; (elem = elems[i]) != null; i++ ) {
+
+ if ( acceptData || jQuery.acceptData( elem ) ) {
+
+ id = elem[ internalKey ];
+ data = id && cache[ id ];
+
+ if ( data ) {
+ if ( data.events ) {
+ for ( type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
+
+ // This is a shortcut to avoid jQuery.event.remove's overhead
+ } else {
+ jQuery.removeEvent( elem, type, data.handle );
+ }
+ }
+ }
+
+ // Remove cache only if it was not already removed by jQuery.event.remove
+ if ( cache[ id ] ) {
+
+ delete cache[ id ];
+
+ // IE does not allow us to delete expando properties from nodes,
+ // nor does it have a removeAttribute function on Document nodes;
+ // we must handle all of these cases
+ if ( deleteExpando ) {
+ delete elem[ internalKey ];
+
+ } else if ( typeof elem.removeAttribute !== core_strundefined ) {
+ elem.removeAttribute( internalKey );
+
+ } else {
+ elem[ internalKey ] = null;
+ }
+
+ core_deletedIds.push( id );
+ }
+ }
+ }
+ }
+ }
+});
+var iframe, getStyles, curCSS,
+ ralpha = /alpha\([^)]*\)/i,
+ ropacity = /opacity\s*=\s*([^)]*)/,
+ rposition = /^(top|right|bottom|left)$/,
+ // swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
+ // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
+ rdisplayswap = /^(none|table(?!-c[ea]).+)/,
+ rmargin = /^margin/,
+ rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
+ rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
+ rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ),
+ elemdisplay = { BODY: "block" },
+
+ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+ cssNormalTransform = {
+ letterSpacing: 0,
+ fontWeight: 400
+ },
+
+ cssExpand = [ "Top", "Right", "Bottom", "Left" ],
+ cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
+
+// return a css property mapped to a potentially vendor prefixed property
+function vendorPropName( style, name ) {
+
+ // shortcut for names that are not vendor prefixed
+ if ( name in style ) {
+ return name;
+ }
+
+ // check for vendor prefixed names
+ var capName = name.charAt(0).toUpperCase() + name.slice(1),
+ origName = name,
+ i = cssPrefixes.length;
+
+ while ( i-- ) {
+ name = cssPrefixes[ i ] + capName;
+ if ( name in style ) {
+ return name;
+ }
+ }
+
+ return origName;
+}
+
+function isHidden( elem, el ) {
+ // isHidden might be called from jQuery#filter function;
+ // in that case, element will be second argument
+ elem = el || elem;
+ return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
+}
+
+function showHide( elements, show ) {
+ var display, elem, hidden,
+ values = [],
+ index = 0,
+ length = elements.length;
+
+ for ( ; index < length; index++ ) {
+ elem = elements[ index ];
+ if ( !elem.style ) {
+ continue;
+ }
+
+ values[ index ] = jQuery._data( elem, "olddisplay" );
+ display = elem.style.display;
+ if ( show ) {
+ // Reset the inline display of this element to learn if it is
+ // being hidden by cascaded rules or not
+ if ( !values[ index ] && display === "none" ) {
+ elem.style.display = "";
+ }
+
+ // Set elements which have been overridden with display: none
+ // in a stylesheet to whatever the default browser style is
+ // for such an element
+ if ( elem.style.display === "" && isHidden( elem ) ) {
+ values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
+ }
+ } else {
+
+ if ( !values[ index ] ) {
+ hidden = isHidden( elem );
+
+ if ( display && display !== "none" || !hidden ) {
+ jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
+ }
+ }
+ }
+ }
+
+ // Set the display of most of the elements in a second loop
+ // to avoid the constant reflow
+ for ( index = 0; index < length; index++ ) {
+ elem = elements[ index ];
+ if ( !elem.style ) {
+ continue;
+ }
+ if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
+ elem.style.display = show ? values[ index ] || "" : "none";
+ }
+ }
+
+ return elements;
+}
+
+jQuery.fn.extend({
+ css: function( name, value ) {
+ return jQuery.access( this, function( elem, name, value ) {
+ var len, styles,
+ map = {},
+ i = 0;
+
+ if ( jQuery.isArray( name ) ) {
+ styles = getStyles( elem );
+ len = name.length;
+
+ for ( ; i < len; i++ ) {
+ map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
+ }
+
+ return map;
+ }
+
+ return value !== undefined ?
+ jQuery.style( elem, name, value ) :
+ jQuery.css( elem, name );
+ }, name, value, arguments.length > 1 );
+ },
+ show: function() {
+ return showHide( this, true );
+ },
+ hide: function() {
+ return showHide( this );
+ },
+ toggle: function( state ) {
+ var bool = typeof state === "boolean";
+
+ return this.each(function() {
+ if ( bool ? state : isHidden( this ) ) {
+ jQuery( this ).show();
+ } else {
+ jQuery( this ).hide();
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ // Add in style property hooks for overriding the default
+ // behavior of getting and setting a style property
+ cssHooks: {
+ opacity: {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ // We should always get a number back from opacity
+ var ret = curCSS( elem, "opacity" );
+ return ret === "" ? "1" : ret;
+ }
+ }
+ }
+ },
+
+ // Exclude the following css properties to add px
+ cssNumber: {
+ "columnCount": true,
+ "fillOpacity": true,
+ "fontWeight": true,
+ "lineHeight": true,
+ "opacity": true,
+ "orphans": true,
+ "widows": true,
+ "zIndex": true,
+ "zoom": true
+ },
+
+ // Add in properties whose names you wish to fix before
+ // setting or getting the value
+ cssProps: {
+ // normalize float css property
+ "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
+ },
+
+ // Get and set the style property on a DOM Node
+ style: function( elem, name, value, extra ) {
+ // Don't set styles on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+ return;
+ }
+
+ // Make sure that we're working with the right name
+ var ret, type, hooks,
+ origName = jQuery.camelCase( name ),
+ style = elem.style;
+
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
+
+ // gets hook for the prefixed version
+ // followed by the unprefixed version
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+ // Check if we're setting a value
+ if ( value !== undefined ) {
+ type = typeof value;
+
+ // convert relative number strings (+= or -=) to relative numbers. #7345
+ if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+ value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
+ // Fixes bug #9237
+ type = "number";
+ }
+
+ // Make sure that NaN and null values aren't set. See: #7116
+ if ( value == null || type === "number" && isNaN( value ) ) {
+ return;
+ }
+
+ // If a number was passed in, add 'px' to the (except for certain CSS properties)
+ if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+ value += "px";
+ }
+
+ // Fixes #8908, it can be done more correctly by specifing setters in cssHooks,
+ // but it would mean to define eight (for every problematic property) identical functions
+ if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
+ style[ name ] = "inherit";
+ }
+
+ // If a hook was provided, use that value, otherwise just set the specified value
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
+
+ // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+ // Fixes bug #5509
+ try {
+ style[ name ] = value;
+ } catch(e) {}
+ }
+
+ } else {
+ // If a hook was provided get the non-computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+ return ret;
+ }
+
+ // Otherwise just get the value from the style object
+ return style[ name ];
+ }
+ },
+
+ css: function( elem, name, extra, styles ) {
+ var num, val, hooks,
+ origName = jQuery.camelCase( name );
+
+ // Make sure that we're working with the right name
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
+
+ // gets hook for the prefixed version
+ // followed by the unprefixed version
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+ // If a hook was provided get the computed value from there
+ if ( hooks && "get" in hooks ) {
+ val = hooks.get( elem, true, extra );
+ }
+
+ // Otherwise, if a way to get the computed value exists, use that
+ if ( val === undefined ) {
+ val = curCSS( elem, name, styles );
+ }
+
+ //convert "normal" to computed value
+ if ( val === "normal" && name in cssNormalTransform ) {
+ val = cssNormalTransform[ name ];
+ }
+
+ // Return, converting to number if forced or a qualifier was provided and val looks numeric
+ if ( extra === "" || extra ) {
+ num = parseFloat( val );
+ return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
+ }
+ return val;
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback, args ) {
+ var ret, name,
+ old = {};
+
+ // Remember the old values, and insert the new ones
+ for ( name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ ret = callback.apply( elem, args || [] );
+
+ // Revert the old values
+ for ( name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+
+ return ret;
+ }
+});
+
+// NOTE: we've included the "window" in window.getComputedStyle
+// because jsdom on node.js will break without it.
+if ( window.getComputedStyle ) {
+ getStyles = function( elem ) {
+ return window.getComputedStyle( elem, null );
+ };
+
+ curCSS = function( elem, name, _computed ) {
+ var width, minWidth, maxWidth,
+ computed = _computed || getStyles( elem ),
+
+ // getPropertyValue is only needed for .css('filter') in IE9, see #12537
+ ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined,
+ style = elem.style;
+
+ if ( computed ) {
+
+ if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
+ ret = jQuery.style( elem, name );
+ }
+
+ // A tribute to the "awesome hack by Dean Edwards"
+ // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
+ // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
+ // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+ if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
+
+ // Remember the original values
+ width = style.width;
+ minWidth = style.minWidth;
+ maxWidth = style.maxWidth;
+
+ // Put in the new values to get a computed value out
+ style.minWidth = style.maxWidth = style.width = ret;
+ ret = computed.width;
+
+ // Revert the changed values
+ style.width = width;
+ style.minWidth = minWidth;
+ style.maxWidth = maxWidth;
+ }
+ }
+
+ return ret;
+ };
+} else if ( document.documentElement.currentStyle ) {
+ getStyles = function( elem ) {
+ return elem.currentStyle;
+ };
+
+ curCSS = function( elem, name, _computed ) {
+ var left, rs, rsLeft,
+ computed = _computed || getStyles( elem ),
+ ret = computed ? computed[ name ] : undefined,
+ style = elem.style;
+
+ // Avoid setting ret to empty string here
+ // so we don't default to auto
+ if ( ret == null && style && style[ name ] ) {
+ ret = style[ name ];
+ }
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ // but not position css attributes, as those are proportional to the parent element instead
+ // and we can't measure the parent instead because it might trigger a "stacking dolls" problem
+ if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
+
+ // Remember the original values
+ left = style.left;
+ rs = elem.runtimeStyle;
+ rsLeft = rs && rs.left;
+
+ // Put in the new values to get a computed value out
+ if ( rsLeft ) {
+ rs.left = elem.currentStyle.left;
+ }
+ style.left = name === "fontSize" ? "1em" : ret;
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ if ( rsLeft ) {
+ rs.left = rsLeft;
+ }
+ }
+
+ return ret === "" ? "auto" : ret;
+ };
+}
+
+function setPositiveNumber( elem, value, subtract ) {
+ var matches = rnumsplit.exec( value );
+ return matches ?
+ // Guard against undefined "subtract", e.g., when used as in cssHooks
+ Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
+ value;
+}
+
+function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
+ var i = extra === ( isBorderBox ? "border" : "content" ) ?
+ // If we already have the right measurement, avoid augmentation
+ 4 :
+ // Otherwise initialize for horizontal or vertical properties
+ name === "width" ? 1 : 0,
+
+ val = 0;
+
+ for ( ; i < 4; i += 2 ) {
+ // both box models exclude margin, so add it if we want it
+ if ( extra === "margin" ) {
+ val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
+ }
+
+ if ( isBorderBox ) {
+ // border-box includes padding, so remove it if we want content
+ if ( extra === "content" ) {
+ val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+ }
+
+ // at this point, extra isn't border nor margin, so remove border
+ if ( extra !== "margin" ) {
+ val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+ }
+ } else {
+ // at this point, extra isn't content, so add padding
+ val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+
+ // at this point, extra isn't content nor padding, so add border
+ if ( extra !== "padding" ) {
+ val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+ }
+ }
+ }
+
+ return val;
+}
+
+function getWidthOrHeight( elem, name, extra ) {
+
+ // Start with offset property, which is equivalent to the border-box value
+ var valueIsBorderBox = true,
+ val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+ styles = getStyles( elem ),
+ isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
+
+ // some non-html elements return undefined for offsetWidth, so check for null/undefined
+ // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
+ // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
+ if ( val <= 0 || val == null ) {
+ // Fall back to computed then uncomputed css if necessary
+ val = curCSS( elem, name, styles );
+ if ( val < 0 || val == null ) {
+ val = elem.style[ name ];
+ }
+
+ // Computed unit is not pixels. Stop here and return.
+ if ( rnumnonpx.test(val) ) {
+ return val;
+ }
+
+ // we need the check for style in case a browser which returns unreliable values
+ // for getComputedStyle silently falls back to the reliable elem.style
+ valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
+
+ // Normalize "", auto, and prepare for extra
+ val = parseFloat( val ) || 0;
+ }
+
+ // use the active box-sizing model to add/subtract irrelevant styles
+ return ( val +
+ augmentWidthOrHeight(
+ elem,
+ name,
+ extra || ( isBorderBox ? "border" : "content" ),
+ valueIsBorderBox,
+ styles
+ )
+ ) + "px";
+}
+
+// Try to determine the default display value of an element
+function css_defaultDisplay( nodeName ) {
+ var doc = document,
+ display = elemdisplay[ nodeName ];
+
+ if ( !display ) {
+ display = actualDisplay( nodeName, doc );
+
+ // If the simple way fails, read from inside an iframe
+ if ( display === "none" || !display ) {
+ // Use the already-created iframe if possible
+ iframe = ( iframe ||
+ jQuery("<iframe frameborder='0' width='0' height='0'/>")
+ .css( "cssText", "display:block !important" )
+ ).appendTo( doc.documentElement );
+
+ // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
+ doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document;
+ doc.write("<!doctype html><html><body>");
+ doc.close();
+
+ display = actualDisplay( nodeName, doc );
+ iframe.detach();
+ }
+
+ // Store the correct default display
+ elemdisplay[ nodeName ] = display;
+ }
+
+ return display;
+}
+
+// Called ONLY from within css_defaultDisplay
+function actualDisplay( name, doc ) {
+ var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
+ display = jQuery.css( elem[0], "display" );
+ elem.remove();
+ return display;
+}
+
+jQuery.each([ "height", "width" ], function( i, name ) {
+ jQuery.cssHooks[ name ] = {
+ get: function( elem, computed, extra ) {
+ if ( computed ) {
+ // certain elements can have dimension info if we invisibly show them
+ // however, it must have a current display style that would benefit from this
+ return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ?
+ jQuery.swap( elem, cssShow, function() {
+ return getWidthOrHeight( elem, name, extra );
+ }) :
+ getWidthOrHeight( elem, name, extra );
+ }
+ },
+
+ set: function( elem, value, extra ) {
+ var styles = extra && getStyles( elem );
+ return setPositiveNumber( elem, value, extra ?
+ augmentWidthOrHeight(
+ elem,
+ name,
+ extra,
+ jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
+ styles
+ ) : 0
+ );
+ }
+ };
+});
+
+if ( !jQuery.support.opacity ) {
+ jQuery.cssHooks.opacity = {
+ get: function( elem, computed ) {
+ // IE uses filters for opacity
+ return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+ ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
+ computed ? "1" : "";
+ },
+
+ set: function( elem, value ) {
+ var style = elem.style,
+ currentStyle = elem.currentStyle,
+ opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
+ filter = currentStyle && currentStyle.filter || style.filter || "";
+
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ style.zoom = 1;
+
+ // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
+ // if value === "", then remove inline opacity #12685
+ if ( ( value >= 1 || value === "" ) &&
+ jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
+ style.removeAttribute ) {
+
+ // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
+ // if "filter:" is present at all, clearType is disabled, we want to avoid this
+ // style.removeAttribute is IE Only, but so apparently is this code path...
+ style.removeAttribute( "filter" );
+
+ // if there is no filter style applied in a css rule or unset inline opacity, we are done
+ if ( value === "" || currentStyle && !currentStyle.filter ) {
+ return;
+ }
+ }
+
+ // otherwise, set new filter values
+ style.filter = ralpha.test( filter ) ?
+ filter.replace( ralpha, opacity ) :
+ filter + " " + opacity;
+ }
+ };
+}
+
+// These hooks cannot be added until DOM ready because the support test
+// for it is not run until after DOM ready
+jQuery(function() {
+ if ( !jQuery.support.reliableMarginRight ) {
+ jQuery.cssHooks.marginRight = {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ // Work around by temporarily setting element display to inline-block
+ return jQuery.swap( elem, { "display": "inline-block" },
+ curCSS, [ elem, "marginRight" ] );
+ }
+ }
+ };
+ }
+
+ // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+ // getComputedStyle returns percent when specified for top/left/bottom/right
+ // rather than make the css module depend on the offset module, we just check for it here
+ if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
+ jQuery.each( [ "top", "left" ], function( i, prop ) {
+ jQuery.cssHooks[ prop ] = {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ computed = curCSS( elem, prop );
+ // if curCSS returns percentage, fallback to offset
+ return rnumnonpx.test( computed ) ?
+ jQuery( elem ).position()[ prop ] + "px" :
+ computed;
+ }
+ }
+ };
+ });
+ }
+
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.hidden = function( elem ) {
+ // Support: Opera <= 12.12
+ // Opera reports offsetWidths and offsetHeights less than zero on some elements
+ return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
+ (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
+ };
+
+ jQuery.expr.filters.visible = function( elem ) {
+ return !jQuery.expr.filters.hidden( elem );
+ };
+}
+
+// These hooks are used by animate to expand properties
+jQuery.each({
+ margin: "",
+ padding: "",
+ border: "Width"
+}, function( prefix, suffix ) {
+ jQuery.cssHooks[ prefix + suffix ] = {
+ expand: function( value ) {
+ var i = 0,
+ expanded = {},
+
+ // assumes a single number if not a string
+ parts = typeof value === "string" ? value.split(" ") : [ value ];
+
+ for ( ; i < 4; i++ ) {
+ expanded[ prefix + cssExpand[ i ] + suffix ] =
+ parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+ }
+
+ return expanded;
+ }
+ };
+
+ if ( !rmargin.test( prefix ) ) {
+ jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
+ }
+});
+var r20 = /%20/g,
+ rbracket = /\[\]$/,
+ rCRLF = /\r?\n/g,
+ rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
+ rsubmittable = /^(?:input|select|textarea|keygen)/i;
+
+jQuery.fn.extend({
+ serialize: function() {
+ return jQuery.param( this.serializeArray() );
+ },
+ serializeArray: function() {
+ return this.map(function(){
+ // Can add propHook for "elements" to filter or add form elements
+ var elements = jQuery.prop( this, "elements" );
+ return elements ? jQuery.makeArray( elements ) : this;
+ })
+ .filter(function(){
+ var type = this.type;
+ // Use .is(":disabled") so that fieldset[disabled] works
+ return this.name && !jQuery( this ).is( ":disabled" ) &&
+ rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
+ ( this.checked || !manipulation_rcheckableType.test( type ) );
+ })
+ .map(function( i, elem ){
+ var val = jQuery( this ).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray( val ) ?
+ jQuery.map( val, function( val ){
+ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }) :
+ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }).get();
+ }
+});
+
+//Serialize an array of form elements or a set of
+//key/values into a query string
+jQuery.param = function( a, traditional ) {
+ var prefix,
+ s = [],
+ add = function( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
+ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+ };
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( prefix in a ) {
+ buildParams( prefix, a[ prefix ], traditional, add );
+ }
+ }
+
+ // Return the resulting serialization
+ return s.join( "&" ).replace( r20, "+" );
+};
+
+function buildParams( prefix, obj, traditional, add ) {
+ var name;
+
+ if ( jQuery.isArray( obj ) ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || rbracket.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+
+ } else {
+ // Item is non-scalar (array or object), encode its numeric index.
+ buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+ }
+ });
+
+ } else if ( !traditional && jQuery.type( obj ) === "object" ) {
+ // Serialize object item.
+ for ( name in obj ) {
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+ }
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+}
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( data, fn ) {
+ return arguments.length > 0 ?
+ this.on( name, null, data, fn ) :
+ this.trigger( name );
+ };
+});
+
+jQuery.fn.hover = function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+};
+var
+ // Document location
+ ajaxLocParts,
+ ajaxLocation,
+ ajax_nonce = jQuery.now(),
+
+ ajax_rquery = /\?/,
+ rhash = /#.*$/,
+ rts = /([?&])_=[^&]*/,
+ rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+ // #7653, #8125, #8152: local protocol detection
+ rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
+ rnoContent = /^(?:GET|HEAD)$/,
+ rprotocol = /^\/\//,
+ rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
+
+ // Keep a copy of the old load method
+ _load = jQuery.fn.load,
+
+ /* Prefilters
+ * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+ * 2) These are called:
+ * - BEFORE asking for a transport
+ * - AFTER param serialization (s.data is a string if s.processData is true)
+ * 3) key is the dataType
+ * 4) the catchall symbol "*" can be used
+ * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+ */
+ prefilters = {},
+
+ /* Transports bindings
+ * 1) key is the dataType
+ * 2) the catchall symbol "*" can be used
+ * 3) selection will start with transport dataType and THEN go to "*" if needed
+ */
+ transports = {},
+
+ // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+ allTypes = "*/".concat("*");
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+ ajaxLocation = location.href;
+} catch( e ) {
+ // Use the href attribute of an A element
+ // since IE will modify it given document.location
+ ajaxLocation = document.createElement( "a" );
+ ajaxLocation.href = "";
+ ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+ // dataTypeExpression is optional and defaults to "*"
+ return function( dataTypeExpression, func ) {
+
+ if ( typeof dataTypeExpression !== "string" ) {
+ func = dataTypeExpression;
+ dataTypeExpression = "*";
+ }
+
+ var dataType,
+ i = 0,
+ dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || [];
+
+ if ( jQuery.isFunction( func ) ) {
+ // For each dataType in the dataTypeExpression
+ while ( (dataType = dataTypes[i++]) ) {
+ // Prepend if requested
+ if ( dataType[0] === "+" ) {
+ dataType = dataType.slice( 1 ) || "*";
+ (structure[ dataType ] = structure[ dataType ] || []).unshift( func );
+
+ // Otherwise append
+ } else {
+ (structure[ dataType ] = structure[ dataType ] || []).push( func );
+ }
+ }
+ }
+ };
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
+
+ var inspected = {},
+ seekingTransport = ( structure === transports );
+
+ function inspect( dataType ) {
+ var selected;
+ inspected[ dataType ] = true;
+ jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
+ var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
+ if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
+ options.dataTypes.unshift( dataTypeOrTransport );
+ inspect( dataTypeOrTransport );
+ return false;
+ } else if ( seekingTransport ) {
+ return !( selected = dataTypeOrTransport );
+ }
+ });
+ return selected;
+ }
+
+ return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
+}
+
+// A special extend for ajax options
+// that takes "flat" options (not to be deep extended)
+// Fixes #9887
+function ajaxExtend( target, src ) {
+ var deep, key,
+ flatOptions = jQuery.ajaxSettings.flatOptions || {};
+
+ for ( key in src ) {
+ if ( src[ key ] !== undefined ) {
+ ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
+ }
+ }
+ if ( deep ) {
+ jQuery.extend( true, target, deep );
+ }
+
+ return target;
+}
+
+jQuery.fn.load = function( url, params, callback ) {
+ if ( typeof url !== "string" && _load ) {
+ return _load.apply( this, arguments );
+ }
+
+ var selector, response, type,
+ self = this,
+ off = url.indexOf(" ");
+
+ if ( off >= 0 ) {
+ selector = url.slice( off, url.length );
+ url = url.slice( 0, off );
+ }
+
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+
+ // We assume that it's the callback
+ callback = params;
+ params = undefined;
+
+ // Otherwise, build a param string
+ } else if ( params && typeof params === "object" ) {
+ type = "POST";
+ }
+
+ // If we have elements to modify, make the request
+ if ( self.length > 0 ) {
+ jQuery.ajax({
+ url: url,
+
+ // if "type" variable is undefined, then "GET" method will be used
+ type: type,
+ dataType: "html",
+ data: params
+ }).done(function( responseText ) {
+
+ // Save response for use in complete callback
+ response = arguments;
+
+ self.html( selector ?
+
+ // If a selector was specified, locate the right elements in a dummy div
+ // Exclude scripts to avoid IE 'Permission Denied' errors
+ jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
+
+ // Otherwise use the full result
+ responseText );
+
+ }).complete( callback && function( jqXHR, status ) {
+ self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
+ });
+ }
+
+ return this;
+};
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){
+ jQuery.fn[ type ] = function( fn ){
+ return this.on( type, fn );
+ };
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+ jQuery[ method ] = function( url, data, callback, type ) {
+ // shift arguments if data argument was omitted
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = undefined;
+ }
+
+ return jQuery.ajax({
+ url: url,
+ type: method,
+ dataType: type,
+ data: data,
+ success: callback
+ });
+ };
+});
+
+jQuery.extend({
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ etag: {},
+
+ ajaxSettings: {
+ url: ajaxLocation,
+ type: "GET",
+ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+ global: true,
+ processData: true,
+ async: true,
+ contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+ /*
+ timeout: 0,
+ data: null,
+ dataType: null,
+ username: null,
+ password: null,
+ cache: null,
+ throws: false,
+ traditional: false,
+ headers: {},
+ */
+
+ accepts: {
+ "*": allTypes,
+ text: "text/plain",
+ html: "text/html",
+ xml: "application/xml, text/xml",
+ json: "application/json, text/javascript"
+ },
+
+ contents: {
+ xml: /xml/,
+ html: /html/,
+ json: /json/
+ },
+
+ responseFields: {
+ xml: "responseXML",
+ text: "responseText"
+ },
+
+ // Data converters
+ // Keys separate source (or catchall "*") and destination types with a single space
+ converters: {
+
+ // Convert anything to text
+ "* text": window.String,
+
+ // Text to html (true = no transformation)
+ "text html": true,
+
+ // Evaluate text as a json expression
+ "text json": jQuery.parseJSON,
+
+ // Parse text as xml
+ "text xml": jQuery.parseXML
+ },
+
+ // For options that shouldn't be deep extended:
+ // you can add your own custom options here if
+ // and when you create one that shouldn't be
+ // deep extended (see ajaxExtend)
+ flatOptions: {
+ url: true,
+ context: true
+ }
+ },
+
+ // Creates a full fledged settings object into target
+ // with both ajaxSettings and settings fields.
+ // If target is omitted, writes into ajaxSettings.
+ ajaxSetup: function( target, settings ) {
+ return settings ?
+
+ // Building a settings object
+ ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
+
+ // Extending ajaxSettings
+ ajaxExtend( jQuery.ajaxSettings, target );
+ },
+
+ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+ ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+ // Main method
+ ajax: function( url, options ) {
+
+ // If url is an object, simulate pre-1.5 signature
+ if ( typeof url === "object" ) {
+ options = url;
+ url = undefined;
+ }
+
+ // Force options to be an object
+ options = options || {};
+
+ var // Cross-domain detection vars
+ parts,
+ // Loop variable
+ i,
+ // URL without anti-cache param
+ cacheURL,
+ // Response headers as string
+ responseHeadersString,
+ // timeout handle
+ timeoutTimer,
+
+ // To know if global events are to be dispatched
+ fireGlobals,
+
+ transport,
+ // Response headers
+ responseHeaders,
+ // Create the final options object
+ s = jQuery.ajaxSetup( {}, options ),
+ // Callbacks context
+ callbackContext = s.context || s,
+ // Context for global events is callbackContext if it is a DOM node or jQuery collection
+ globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
+ jQuery( callbackContext ) :
+ jQuery.event,
+ // Deferreds
+ deferred = jQuery.Deferred(),
+ completeDeferred = jQuery.Callbacks("once memory"),
+ // Status-dependent callbacks
+ statusCode = s.statusCode || {},
+ // Headers (they are sent all at once)
+ requestHeaders = {},
+ requestHeadersNames = {},
+ // The jqXHR state
+ state = 0,
+ // Default abort message
+ strAbort = "canceled",
+ // Fake xhr
+ jqXHR = {
+ readyState: 0,
+
+ // Builds headers hashtable if needed
+ getResponseHeader: function( key ) {
+ var match;
+ if ( state === 2 ) {
+ if ( !responseHeaders ) {
+ responseHeaders = {};
+ while ( (match = rheaders.exec( responseHeadersString )) ) {
+ responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+ }
+ }
+ match = responseHeaders[ key.toLowerCase() ];
+ }
+ return match == null ? null : match;
+ },
+
+ // Raw string
+ getAllResponseHeaders: function() {
+ return state === 2 ? responseHeadersString : null;
+ },
+
+ // Caches the header
+ setRequestHeader: function( name, value ) {
+ var lname = name.toLowerCase();
+ if ( !state ) {
+ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+ requestHeaders[ name ] = value;
+ }
+ return this;
+ },
+
+ // Overrides response content-type header
+ overrideMimeType: function( type ) {
+ if ( !state ) {
+ s.mimeType = type;
+ }
+ return this;
+ },
+
+ // Status-dependent callbacks
+ statusCode: function( map ) {
+ var code;
+ if ( map ) {
+ if ( state < 2 ) {
+ for ( code in map ) {
+ // Lazy-add the new callback in a way that preserves old ones
+ statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
+ }
+ } else {
+ // Execute the appropriate callbacks
+ jqXHR.always( map[ jqXHR.status ] );
+ }
+ }
+ return this;
+ },
+
+ // Cancel the request
+ abort: function( statusText ) {
+ var finalText = statusText || strAbort;
+ if ( transport ) {
+ transport.abort( finalText );
+ }
+ done( 0, finalText );
+ return this;
+ }
+ };
+
+ // Attach deferreds
+ deferred.promise( jqXHR ).complete = completeDeferred.add;
+ jqXHR.success = jqXHR.done;
+ jqXHR.error = jqXHR.fail;
+
+ // Remove hash character (#7531: and string promotion)
+ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+ // Handle falsy url in the settings object (#10093: consistency with old signature)
+ // We also use the url parameter if available
+ s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+ // Alias method option to type as per ticket #12004
+ s.type = options.method || options.type || s.method || s.type;
+
+ // Extract dataTypes list
+ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""];
+
+ // A cross-domain request is in order when we have a protocol:host:port mismatch
+ if ( s.crossDomain == null ) {
+ parts = rurl.exec( s.url.toLowerCase() );
+ s.crossDomain = !!( parts &&
+ ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
+ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
+ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
+ );
+ }
+
+ // Convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
+ s.data = jQuery.param( s.data, s.traditional );
+ }
+
+ // Apply prefilters
+ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+ // If request was aborted inside a prefilter, stop there
+ if ( state === 2 ) {
+ return jqXHR;
+ }
+
+ // We can fire global events as of now if asked to
+ fireGlobals = s.global;
+
+ // Watch for a new set of requests
+ if ( fireGlobals && jQuery.active++ === 0 ) {
+ jQuery.event.trigger("ajaxStart");
+ }
+
+ // Uppercase the type
+ s.type = s.type.toUpperCase();
+
+ // Determine if request has content
+ s.hasContent = !rnoContent.test( s.type );
+
+ // Save the URL in case we're toying with the If-Modified-Since
+ // and/or If-None-Match header later on
+ cacheURL = s.url;
+
+ // More options handling for requests with no content
+ if ( !s.hasContent ) {
+
+ // If data is available, append data to url
+ if ( s.data ) {
+ cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
+ // #9682: remove data so that it's not used in an eventual retry
+ delete s.data;
+ }
+
+ // Add anti-cache in url if needed
+ if ( s.cache === false ) {
+ s.url = rts.test( cacheURL ) ?
+
+ // If there is already a '_' parameter, set its value
+ cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
+
+ // Otherwise add one to the end
+ cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
+ }
+ }
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ if ( jQuery.lastModified[ cacheURL ] ) {
+ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
+ }
+ if ( jQuery.etag[ cacheURL ] ) {
+ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
+ }
+ }
+
+ // Set the correct header, if data is being sent
+ if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+ jqXHR.setRequestHeader( "Content-Type", s.contentType );
+ }
+
+ // Set the Accepts header for the server, depending on the dataType
+ jqXHR.setRequestHeader(
+ "Accept",
+ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+ s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+ s.accepts[ "*" ]
+ );
+
+ // Check for headers option
+ for ( i in s.headers ) {
+ jqXHR.setRequestHeader( i, s.headers[ i ] );
+ }
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+ // Abort if not done already and return
+ return jqXHR.abort();
+ }
+
+ // aborting is no longer a cancellation
+ strAbort = "abort";
+
+ // Install callbacks on deferreds
+ for ( i in { success: 1, error: 1, complete: 1 } ) {
+ jqXHR[ i ]( s[ i ] );
+ }
+
+ // Get transport
+ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+ // If no transport, we auto-abort
+ if ( !transport ) {
+ done( -1, "No Transport" );
+ } else {
+ jqXHR.readyState = 1;
+
+ // Send global event
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+ }
+ // Timeout
+ if ( s.async && s.timeout > 0 ) {
+ timeoutTimer = setTimeout(function() {
+ jqXHR.abort("timeout");
+ }, s.timeout );
+ }
+
+ try {
+ state = 1;
+ transport.send( requestHeaders, done );
+ } catch ( e ) {
+ // Propagate exception as error if not done
+ if ( state < 2 ) {
+ done( -1, e );
+ // Simply rethrow otherwise
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ // Callback for when everything is done
+ function done( status, nativeStatusText, responses, headers ) {
+ var isSuccess, success, error, response, modified,
+ statusText = nativeStatusText;
+
+ // Called once
+ if ( state === 2 ) {
+ return;
+ }
+
+ // State is "done" now
+ state = 2;
+
+ // Clear timeout if it exists
+ if ( timeoutTimer ) {
+ clearTimeout( timeoutTimer );
+ }
+
+ // Dereference transport for early garbage collection
+ // (no matter how long the jqXHR object will be used)
+ transport = undefined;
+
+ // Cache response headers
+ responseHeadersString = headers || "";
+
+ // Set readyState
+ jqXHR.readyState = status > 0 ? 4 : 0;
+
+ // Get response data
+ if ( responses ) {
+ response = ajaxHandleResponses( s, jqXHR, responses );
+ }
+
+ // If successful, handle type chaining
+ if ( status >= 200 && status < 300 || status === 304 ) {
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ modified = jqXHR.getResponseHeader("Last-Modified");
+ if ( modified ) {
+ jQuery.lastModified[ cacheURL ] = modified;
+ }
+ modified = jqXHR.getResponseHeader("etag");
+ if ( modified ) {
+ jQuery.etag[ cacheURL ] = modified;
+ }
+ }
+
+ // if no content
+ if ( status === 204 ) {
+ isSuccess = true;
+ statusText = "nocontent";
+
+ // if not modified
+ } else if ( status === 304 ) {
+ isSuccess = true;
+ statusText = "notmodified";
+
+ // If we have data, let's convert it
+ } else {
+ isSuccess = ajaxConvert( s, response );
+ statusText = isSuccess.state;
+ success = isSuccess.data;
+ error = isSuccess.error;
+ isSuccess = !error;
+ }
+ } else {
+ // We extract error from statusText
+ // then normalize statusText and status for non-aborts
+ error = statusText;
+ if ( status || !statusText ) {
+ statusText = "error";
+ if ( status < 0 ) {
+ status = 0;
+ }
+ }
+ }
+
+ // Set data for the fake xhr object
+ jqXHR.status = status;
+ jqXHR.statusText = ( nativeStatusText || statusText ) + "";
+
+ // Success/Error
+ if ( isSuccess ) {
+ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+ } else {
+ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+ }
+
+ // Status-dependent callbacks
+ jqXHR.statusCode( statusCode );
+ statusCode = undefined;
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
+ [ jqXHR, s, isSuccess ? success : error ] );
+ }
+
+ // Complete
+ completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+ // Handle the global AJAX counter
+ if ( !( --jQuery.active ) ) {
+ jQuery.event.trigger("ajaxStop");
+ }
+ }
+ }
+
+ return jqXHR;
+ },
+
+ getScript: function( url, callback ) {
+ return jQuery.get( url, undefined, callback, "script" );
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get( url, data, callback, "json" );
+ }
+});
+
+/* Handles responses to an ajax request:
+ * - sets all responseXXX fields accordingly
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+ var firstDataType, ct, finalDataType, type,
+ contents = s.contents,
+ dataTypes = s.dataTypes,
+ responseFields = s.responseFields;
+
+ // Fill responseXXX fields
+ for ( type in responseFields ) {
+ if ( type in responses ) {
+ jqXHR[ responseFields[type] ] = responses[ type ];
+ }
+ }
+
+ // Remove auto dataType and get content-type in the process
+ while( dataTypes[ 0 ] === "*" ) {
+ dataTypes.shift();
+ if ( ct === undefined ) {
+ ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
+ }
+ }
+
+ // Check if we're dealing with a known content-type
+ if ( ct ) {
+ for ( type in contents ) {
+ if ( contents[ type ] && contents[ type ].test( ct ) ) {
+ dataTypes.unshift( type );
+ break;
+ }
+ }
+ }
+
+ // Check to see if we have a response for the expected dataType
+ if ( dataTypes[ 0 ] in responses ) {
+ finalDataType = dataTypes[ 0 ];
+ } else {
+ // Try convertible dataTypes
+ for ( type in responses ) {
+ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+ finalDataType = type;
+ break;
+ }
+ if ( !firstDataType ) {
+ firstDataType = type;
+ }
+ }
+ // Or just use first one
+ finalDataType = finalDataType || firstDataType;
+ }
+
+ // If we found a dataType
+ // We add the dataType to the list if needed
+ // and return the corresponding response
+ if ( finalDataType ) {
+ if ( finalDataType !== dataTypes[ 0 ] ) {
+ dataTypes.unshift( finalDataType );
+ }
+ return responses[ finalDataType ];
+ }
+}
+
+// Chain conversions given the request and the original response
+function ajaxConvert( s, response ) {
+ var conv2, current, conv, tmp,
+ converters = {},
+ i = 0,
+ // Work with a copy of dataTypes in case we need to modify it for conversion
+ dataTypes = s.dataTypes.slice(),
+ prev = dataTypes[ 0 ];
+
+ // Apply the dataFilter if provided
+ if ( s.dataFilter ) {
+ response = s.dataFilter( response, s.dataType );
+ }
+
+ // Create converters map with lowercased keys
+ if ( dataTypes[ 1 ] ) {
+ for ( conv in s.converters ) {
+ converters[ conv.toLowerCase() ] = s.converters[ conv ];
+ }
+ }
+
+ // Convert to each sequential dataType, tolerating list modification
+ for ( ; (current = dataTypes[++i]); ) {
+
+ // There's only work to do if current dataType is non-auto
+ if ( current !== "*" ) {
+
+ // Convert response if prev dataType is non-auto and differs from current
+ if ( prev !== "*" && prev !== current ) {
+
+ // Seek a direct converter
+ conv = converters[ prev + " " + current ] || converters[ "* " + current ];
+
+ // If none found, seek a pair
+ if ( !conv ) {
+ for ( conv2 in converters ) {
+
+ // If conv2 outputs current
+ tmp = conv2.split(" ");
+ if ( tmp[ 1 ] === current ) {
+
+ // If prev can be converted to accepted input
+ conv = converters[ prev + " " + tmp[ 0 ] ] ||
+ converters[ "* " + tmp[ 0 ] ];
+ if ( conv ) {
+ // Condense equivalence converters
+ if ( conv === true ) {
+ conv = converters[ conv2 ];
+
+ // Otherwise, insert the intermediate dataType
+ } else if ( converters[ conv2 ] !== true ) {
+ current = tmp[ 0 ];
+ dataTypes.splice( i--, 0, current );
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ // Apply converter (if not an equivalence)
+ if ( conv !== true ) {
+
+ // Unless errors are allowed to bubble, catch and return them
+ if ( conv && s["throws"] ) {
+ response = conv( response );
+ } else {
+ try {
+ response = conv( response );
+ } catch ( e ) {
+ return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
+ }
+ }
+ }
+ }
+
+ // Update prev for next iteration
+ prev = current;
+ }
+ }
+
+ return { state: "success", data: response };
+}
+// Install script dataType
+jQuery.ajaxSetup({
+ accepts: {
+ script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+ },
+ contents: {
+ script: /(?:java|ecma)script/
+ },
+ converters: {
+ "text script": function( text ) {
+ jQuery.globalEval( text );
+ return text;
+ }
+ }
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter( "script", function( s ) {
+ if ( s.cache === undefined ) {
+ s.cache = false;
+ }
+ if ( s.crossDomain ) {
+ s.type = "GET";
+ s.global = false;
+ }
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+ // This transport only deals with cross domain requests
+ if ( s.crossDomain ) {
+
+ var script,
+ head = document.head || jQuery("head")[0] || document.documentElement;
+
+ return {
+
+ send: function( _, callback ) {
+
+ script = document.createElement("script");
+
+ script.async = true;
+
+ if ( s.scriptCharset ) {
+ script.charset = s.scriptCharset;
+ }
+
+ script.src = s.url;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+ if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+
+ // Remove the script
+ if ( script.parentNode ) {
+ script.parentNode.removeChild( script );
+ }
+
+ // Dereference the script
+ script = null;
+
+ // Callback if not abort
+ if ( !isAbort ) {
+ callback( 200, "success" );
+ }
+ }
+ };
+
+ // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
+ // Use native DOM manipulation to avoid our domManip AJAX trickery
+ head.insertBefore( script, head.firstChild );
+ },
+
+ abort: function() {
+ if ( script ) {
+ script.onload( undefined, true );
+ }
+ }
+ };
+ }
+});
+var oldCallbacks = [],
+ rjsonp = /(=)\?(?=&|$)|\?\?/;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+ jsonp: "callback",
+ jsonpCallback: function() {
+ var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) );
+ this[ callback ] = true;
+ return callback;
+ }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+ var callbackName, overwritten, responseContainer,
+ jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
+ "url" :
+ typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
+ );
+
+ // Handle iff the expected data type is "jsonp" or we have a parameter to set
+ if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
+
+ // Get callback name, remembering preexisting value associated with it
+ callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
+ s.jsonpCallback() :
+ s.jsonpCallback;
+
+ // Insert callback into url or form data
+ if ( jsonProp ) {
+ s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
+ } else if ( s.jsonp !== false ) {
+ s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
+ }
+
+ // Use data converter to retrieve json after script execution
+ s.converters["script json"] = function() {
+ if ( !responseContainer ) {
+ jQuery.error( callbackName + " was not called" );
+ }
+ return responseContainer[ 0 ];
+ };
+
+ // force json dataType
+ s.dataTypes[ 0 ] = "json";
+
+ // Install callback
+ overwritten = window[ callbackName ];
+ window[ callbackName ] = function() {
+ responseContainer = arguments;
+ };
+
+ // Clean-up function (fires after converters)
+ jqXHR.always(function() {
+ // Restore preexisting value
+ window[ callbackName ] = overwritten;
+
+ // Save back as free
+ if ( s[ callbackName ] ) {
+ // make sure that re-using the options doesn't screw things around
+ s.jsonpCallback = originalSettings.jsonpCallback;
+
+ // save the callback name for future use
+ oldCallbacks.push( callbackName );
+ }
+
+ // Call if it was a function and we have a response
+ if ( responseContainer && jQuery.isFunction( overwritten ) ) {
+ overwritten( responseContainer[ 0 ] );
+ }
+
+ responseContainer = overwritten = undefined;
+ });
+
+ // Delegate to script
+ return "script";
+ }
+});
+var xhrCallbacks, xhrSupported,
+ xhrId = 0,
+ // #5280: Internet Explorer will keep connections alive if we don't abort on unload
+ xhrOnUnloadAbort = window.ActiveXObject && function() {
+ // Abort all pending requests
+ var key;
+ for ( key in xhrCallbacks ) {
+ xhrCallbacks[ key ]( undefined, true );
+ }
+ };
+
+// Functions to create xhrs
+function createStandardXHR() {
+ try {
+ return new window.XMLHttpRequest();
+ } catch( e ) {}
+}
+
+function createActiveXHR() {
+ try {
+ return new window.ActiveXObject("Microsoft.XMLHTTP");
+ } catch( e ) {}
+}
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+ /* Microsoft failed to properly
+ * implement the XMLHttpRequest in IE7 (can't request local files),
+ * so we use the ActiveXObject when it is available
+ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+ * we need a fallback.
+ */
+ function() {
+ return !this.isLocal && createStandardXHR() || createActiveXHR();
+ } :
+ // For all other browsers, use the standard XMLHttpRequest object
+ createStandardXHR;
+
+// Determine support properties
+xhrSupported = jQuery.ajaxSettings.xhr();
+jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
+xhrSupported = jQuery.support.ajax = !!xhrSupported;
+
+// Create transport if the browser can provide an xhr
+if ( xhrSupported ) {
+
+ jQuery.ajaxTransport(function( s ) {
+ // Cross domain only allowed if supported through XMLHttpRequest
+ if ( !s.crossDomain || jQuery.support.cors ) {
+
+ var callback;
+
+ return {
+ send: function( headers, complete ) {
+
+ // Get a new xhr
+ var handle, i,
+ xhr = s.xhr();
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if ( s.username ) {
+ xhr.open( s.type, s.url, s.async, s.username, s.password );
+ } else {
+ xhr.open( s.type, s.url, s.async );
+ }
+
+ // Apply custom fields if provided
+ if ( s.xhrFields ) {
+ for ( i in s.xhrFields ) {
+ xhr[ i ] = s.xhrFields[ i ];
+ }
+ }
+
+ // Override mime type if needed
+ if ( s.mimeType && xhr.overrideMimeType ) {
+ xhr.overrideMimeType( s.mimeType );
+ }
+
+ // X-Requested-With header
+ // For cross-domain requests, seeing as conditions for a preflight are
+ // akin to a jigsaw puzzle, we simply never set it to be sure.
+ // (it can always be set on a per-request basis or even using ajaxSetup)
+ // For same-domain requests, won't change header if already provided.
+ if ( !s.crossDomain && !headers["X-Requested-With"] ) {
+ headers["X-Requested-With"] = "XMLHttpRequest";
+ }
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ for ( i in headers ) {
+ xhr.setRequestHeader( i, headers[ i ] );
+ }
+ } catch( err ) {}
+
+ // Do send the request
+ // This may raise an exception which is actually
+ // handled in jQuery.ajax (so no try/catch here)
+ xhr.send( ( s.hasContent && s.data ) || null );
+
+ // Listener
+ callback = function( _, isAbort ) {
+ var status, responseHeaders, statusText, responses;
+
+ // Firefox throws exceptions when accessing properties
+ // of an xhr when a network error occurred
+ // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
+ try {
+
+ // Was never called and is aborted or complete
+ if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+ // Only called once
+ callback = undefined;
+
+ // Do not keep as active anymore
+ if ( handle ) {
+ xhr.onreadystatechange = jQuery.noop;
+ if ( xhrOnUnloadAbort ) {
+ delete xhrCallbacks[ handle ];
+ }
+ }
+
+ // If it's an abort
+ if ( isAbort ) {
+ // Abort it manually if needed
+ if ( xhr.readyState !== 4 ) {
+ xhr.abort();
+ }
+ } else {
+ responses = {};
+ status = xhr.status;
+ responseHeaders = xhr.getAllResponseHeaders();
+
+ // When requesting binary data, IE6-9 will throw an exception
+ // on any attempt to access responseText (#11426)
+ if ( typeof xhr.responseText === "string" ) {
+ responses.text = xhr.responseText;
+ }
+
+ // Firefox throws an exception when accessing
+ // statusText for faulty cross-domain requests
+ try {
+ statusText = xhr.statusText;
+ } catch( e ) {
+ // We normalize with Webkit giving an empty statusText
+ statusText = "";
+ }
+
+ // Filter status for non standard behaviors
+
+ // If the request is local and we have data: assume a success
+ // (success with no data won't get notified, that's the best we
+ // can do given current implementations)
+ if ( !status && s.isLocal && !s.crossDomain ) {
+ status = responses.text ? 200 : 404;
+ // IE - #1450: sometimes returns 1223 when it should be 204
+ } else if ( status === 1223 ) {
+ status = 204;
+ }
+ }
+ }
+ } catch( firefoxAccessException ) {
+ if ( !isAbort ) {
+ complete( -1, firefoxAccessException );
+ }
+ }
+
+ // Call complete if needed
+ if ( responses ) {
+ complete( status, statusText, responses, responseHeaders );
+ }
+ };
+
+ if ( !s.async ) {
+ // if we're in sync mode we fire the callback
+ callback();
+ } else if ( xhr.readyState === 4 ) {
+ // (IE6 & IE7) if it's in cache and has been
+ // retrieved directly we need to fire the callback
+ setTimeout( callback );
+ } else {
+ handle = ++xhrId;
+ if ( xhrOnUnloadAbort ) {
+ // Create the active xhrs callbacks list if needed
+ // and attach the unload handler
+ if ( !xhrCallbacks ) {
+ xhrCallbacks = {};
+ jQuery( window ).unload( xhrOnUnloadAbort );
+ }
+ // Add to list of active xhrs callbacks
+ xhrCallbacks[ handle ] = callback;
+ }
+ xhr.onreadystatechange = callback;
+ }
+ },
+
+ abort: function() {
+ if ( callback ) {
+ callback( undefined, true );
+ }
+ }
+ };
+ }
+ });
+}
+var fxNow, timerId,
+ rfxtypes = /^(?:toggle|show|hide)$/,
+ rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
+ rrun = /queueHooks$/,
+ animationPrefilters = [ defaultPrefilter ],
+ tweeners = {
+ "*": [function( prop, value ) {
+ var end, unit,
+ tween = this.createTween( prop, value ),
+ parts = rfxnum.exec( value ),
+ target = tween.cur(),
+ start = +target || 0,
+ scale = 1,
+ maxIterations = 20;
+
+ if ( parts ) {
+ end = +parts[2];
+ unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+
+ // We need to compute starting value
+ if ( unit !== "px" && start ) {
+ // Iteratively approximate from a nonzero starting point
+ // Prefer the current property, because this process will be trivial if it uses the same units
+ // Fallback to end or a simple constant
+ start = jQuery.css( tween.elem, prop, true ) || end || 1;
+
+ do {
+ // If previous iteration zeroed out, double until we get *something*
+ // Use a string for doubling factor so we don't accidentally see scale as unchanged below
+ scale = scale || ".5";
+
+ // Adjust and apply
+ start = start / scale;
+ jQuery.style( tween.elem, prop, start + unit );
+
+ // Update scale, tolerating zero or NaN from tween.cur()
+ // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
+ } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
+ }
+
+ tween.unit = unit;
+ tween.start = start;
+ // If a +=/-= token was provided, we're doing a relative animation
+ tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
+ }
+ return tween;
+ }]
+ };
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+ setTimeout(function() {
+ fxNow = undefined;
+ });
+ return ( fxNow = jQuery.now() );
+}
+
+function createTweens( animation, props ) {
+ jQuery.each( props, function( prop, value ) {
+ var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+ index = 0,
+ length = collection.length;
+ for ( ; index < length; index++ ) {
+ if ( collection[ index ].call( animation, prop, value ) ) {
+
+ // we're done with this property
+ return;
+ }
+ }
+ });
+}
+
+function Animation( elem, properties, options ) {
+ var result,
+ stopped,
+ index = 0,
+ length = animationPrefilters.length,
+ deferred = jQuery.Deferred().always( function() {
+ // don't match elem in the :animated selector
+ delete tick.elem;
+ }),
+ tick = function() {
+ if ( stopped ) {
+ return false;
+ }
+ var currentTime = fxNow || createFxNow(),
+ remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
+ // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
+ temp = remaining / animation.duration || 0,
+ percent = 1 - temp,
+ index = 0,
+ length = animation.tweens.length;
+
+ for ( ; index < length ; index++ ) {
+ animation.tweens[ index ].run( percent );
+ }
+
+ deferred.notifyWith( elem, [ animation, percent, remaining ]);
+
+ if ( percent < 1 && length ) {
+ return remaining;
+ } else {
+ deferred.resolveWith( elem, [ animation ] );
+ return false;
+ }
+ },
+ animation = deferred.promise({
+ elem: elem,
+ props: jQuery.extend( {}, properties ),
+ opts: jQuery.extend( true, { specialEasing: {} }, options ),
+ originalProperties: properties,
+ originalOptions: options,
+ startTime: fxNow || createFxNow(),
+ duration: options.duration,
+ tweens: [],
+ createTween: function( prop, end ) {
+ var tween = jQuery.Tween( elem, animation.opts, prop, end,
+ animation.opts.specialEasing[ prop ] || animation.opts.easing );
+ animation.tweens.push( tween );
+ return tween;
+ },
+ stop: function( gotoEnd ) {
+ var index = 0,
+ // if we are going to the end, we want to run all the tweens
+ // otherwise we skip this part
+ length = gotoEnd ? animation.tweens.length : 0;
+ if ( stopped ) {
+ return this;
+ }
+ stopped = true;
+ for ( ; index < length ; index++ ) {
+ animation.tweens[ index ].run( 1 );
+ }
+
+ // resolve when we played the last frame
+ // otherwise, reject
+ if ( gotoEnd ) {
+ deferred.resolveWith( elem, [ animation, gotoEnd ] );
+ } else {
+ deferred.rejectWith( elem, [ animation, gotoEnd ] );
+ }
+ return this;
+ }
+ }),
+ props = animation.props;
+
+ propFilter( props, animation.opts.specialEasing );
+
+ for ( ; index < length ; index++ ) {
+ result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
+ if ( result ) {
+ return result;
+ }
+ }
+
+ createTweens( animation, props );
+
+ if ( jQuery.isFunction( animation.opts.start ) ) {
+ animation.opts.start.call( elem, animation );
+ }
+
+ jQuery.fx.timer(
+ jQuery.extend( tick, {
+ elem: elem,
+ anim: animation,
+ queue: animation.opts.queue
+ })
+ );
+
+ // attach callbacks from options
+ return animation.progress( animation.opts.progress )
+ .done( animation.opts.done, animation.opts.complete )
+ .fail( animation.opts.fail )
+ .always( animation.opts.always );
+}
+
+function propFilter( props, specialEasing ) {
+ var value, name, index, easing, hooks;
+
+ // camelCase, specialEasing and expand cssHook pass
+ for ( index in props ) {
+ name = jQuery.camelCase( index );
+ easing = specialEasing[ name ];
+ value = props[ index ];
+ if ( jQuery.isArray( value ) ) {
+ easing = value[ 1 ];
+ value = props[ index ] = value[ 0 ];
+ }
+
+ if ( index !== name ) {
+ props[ name ] = value;
+ delete props[ index ];
+ }
+
+ hooks = jQuery.cssHooks[ name ];
+ if ( hooks && "expand" in hooks ) {
+ value = hooks.expand( value );
+ delete props[ name ];
+
+ // not quite $.extend, this wont overwrite keys already present.
+ // also - reusing 'index' from above because we have the correct "name"
+ for ( index in value ) {
+ if ( !( index in props ) ) {
+ props[ index ] = value[ index ];
+ specialEasing[ index ] = easing;
+ }
+ }
+ } else {
+ specialEasing[ name ] = easing;
+ }
+ }
+}
+
+jQuery.Animation = jQuery.extend( Animation, {
+
+ tweener: function( props, callback ) {
+ if ( jQuery.isFunction( props ) ) {
+ callback = props;
+ props = [ "*" ];
+ } else {
+ props = props.split(" ");
+ }
+
+ var prop,
+ index = 0,
+ length = props.length;
+
+ for ( ; index < length ; index++ ) {
+ prop = props[ index ];
+ tweeners[ prop ] = tweeners[ prop ] || [];
+ tweeners[ prop ].unshift( callback );
+ }
+ },
+
+ prefilter: function( callback, prepend ) {
+ if ( prepend ) {
+ animationPrefilters.unshift( callback );
+ } else {
+ animationPrefilters.push( callback );
+ }
+ }
+});
+
+function defaultPrefilter( elem, props, opts ) {
+ /*jshint validthis:true */
+ var prop, index, length,
+ value, dataShow, toggle,
+ tween, hooks, oldfire,
+ anim = this,
+ style = elem.style,
+ orig = {},
+ handled = [],
+ hidden = elem.nodeType && isHidden( elem );
+
+ // handle queue: false promises
+ if ( !opts.queue ) {
+ hooks = jQuery._queueHooks( elem, "fx" );
+ if ( hooks.unqueued == null ) {
+ hooks.unqueued = 0;
+ oldfire = hooks.empty.fire;
+ hooks.empty.fire = function() {
+ if ( !hooks.unqueued ) {
+ oldfire();
+ }
+ };
+ }
+ hooks.unqueued++;
+
+ anim.always(function() {
+ // doing this makes sure that the complete handler will be called
+ // before this completes
+ anim.always(function() {
+ hooks.unqueued--;
+ if ( !jQuery.queue( elem, "fx" ).length ) {
+ hooks.empty.fire();
+ }
+ });
+ });
+ }
+
+ // height/width overflow pass
+ if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
+ // Make sure that nothing sneaks out
+ // Record all 3 overflow attributes because IE does not
+ // change the overflow attribute when overflowX and
+ // overflowY are set to the same value
+ opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
+
+ // Set display property to inline-block for height/width
+ // animations on inline elements that are having width/height animated
+ if ( jQuery.css( elem, "display" ) === "inline" &&
+ jQuery.css( elem, "float" ) === "none" ) {
+
+ // inline-level elements accept inline-block;
+ // block-level elements need to be inline with layout
+ if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
+ style.display = "inline-block";
+
+ } else {
+ style.zoom = 1;
+ }
+ }
+ }
+
+ if ( opts.overflow ) {
+ style.overflow = "hidden";
+ if ( !jQuery.support.shrinkWrapBlocks ) {
+ anim.always(function() {
+ style.overflow = opts.overflow[ 0 ];
+ style.overflowX = opts.overflow[ 1 ];
+ style.overflowY = opts.overflow[ 2 ];
+ });
+ }
+ }
+
+
+ // show/hide pass
+ for ( index in props ) {
+ value = props[ index ];
+ if ( rfxtypes.exec( value ) ) {
+ delete props[ index ];
+ toggle = toggle || value === "toggle";
+ if ( value === ( hidden ? "hide" : "show" ) ) {
+ continue;
+ }
+ handled.push( index );
+ }
+ }
+
+ length = handled.length;
+ if ( length ) {
+ dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
+ if ( "hidden" in dataShow ) {
+ hidden = dataShow.hidden;
+ }
+
+ // store state if its toggle - enables .stop().toggle() to "reverse"
+ if ( toggle ) {
+ dataShow.hidden = !hidden;
+ }
+ if ( hidden ) {
+ jQuery( elem ).show();
+ } else {
+ anim.done(function() {
+ jQuery( elem ).hide();
+ });
+ }
+ anim.done(function() {
+ var prop;
+ jQuery._removeData( elem, "fxshow" );
+ for ( prop in orig ) {
+ jQuery.style( elem, prop, orig[ prop ] );
+ }
+ });
+ for ( index = 0 ; index < length ; index++ ) {
+ prop = handled[ index ];
+ tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
+ orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
+
+ if ( !( prop in dataShow ) ) {
+ dataShow[ prop ] = tween.start;
+ if ( hidden ) {
+ tween.end = tween.start;
+ tween.start = prop === "width" || prop === "height" ? 1 : 0;
+ }
+ }
+ }
+ }
+}
+
+function Tween( elem, options, prop, end, easing ) {
+ return new Tween.prototype.init( elem, options, prop, end, easing );
+}
+jQuery.Tween = Tween;
+
+Tween.prototype = {
+ constructor: Tween,
+ init: function( elem, options, prop, end, easing, unit ) {
+ this.elem = elem;
+ this.prop = prop;
+ this.easing = easing || "swing";
+ this.options = options;
+ this.start = this.now = this.cur();
+ this.end = end;
+ this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+ },
+ cur: function() {
+ var hooks = Tween.propHooks[ this.prop ];
+
+ return hooks && hooks.get ?
+ hooks.get( this ) :
+ Tween.propHooks._default.get( this );
+ },
+ run: function( percent ) {
+ var eased,
+ hooks = Tween.propHooks[ this.prop ];
+
+ if ( this.options.duration ) {
+ this.pos = eased = jQuery.easing[ this.easing ](
+ percent, this.options.duration * percent, 0, 1, this.options.duration
+ );
+ } else {
+ this.pos = eased = percent;
+ }
+ this.now = ( this.end - this.start ) * eased + this.start;
+
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
+ }
+
+ if ( hooks && hooks.set ) {
+ hooks.set( this );
+ } else {
+ Tween.propHooks._default.set( this );
+ }
+ return this;
+ }
+};
+
+Tween.prototype.init.prototype = Tween.prototype;
+
+Tween.propHooks = {
+ _default: {
+ get: function( tween ) {
+ var result;
+
+ if ( tween.elem[ tween.prop ] != null &&
+ (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
+ return tween.elem[ tween.prop ];
+ }
+
+ // passing an empty string as a 3rd parameter to .css will automatically
+ // attempt a parseFloat and fallback to a string if the parse fails
+ // so, simple values such as "10px" are parsed to Float.
+ // complex values such as "rotate(1rad)" are returned as is.
+ result = jQuery.css( tween.elem, tween.prop, "" );
+ // Empty strings, null, undefined and "auto" are converted to 0.
+ return !result || result === "auto" ? 0 : result;
+ },
+ set: function( tween ) {
+ // use step hook for back compat - use cssHook if its there - use .style if its
+ // available and use plain properties where available
+ if ( jQuery.fx.step[ tween.prop ] ) {
+ jQuery.fx.step[ tween.prop ]( tween );
+ } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
+ jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
+ } else {
+ tween.elem[ tween.prop ] = tween.now;
+ }
+ }
+ }
+};
+
+// Remove in 2.0 - this supports IE8's panic based approach
+// to setting things on disconnected nodes
+
+Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
+ set: function( tween ) {
+ if ( tween.elem.nodeType && tween.elem.parentNode ) {
+ tween.elem[ tween.prop ] = tween.now;
+ }
+ }
+};
+
+jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
+ var cssFn = jQuery.fn[ name ];
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return speed == null || typeof speed === "boolean" ?
+ cssFn.apply( this, arguments ) :
+ this.animate( genFx( name, true ), speed, easing, callback );
+ };
+});
+
+jQuery.fn.extend({
+ fadeTo: function( speed, to, easing, callback ) {
+
+ // show any hidden elements after setting opacity to 0
+ return this.filter( isHidden ).css( "opacity", 0 ).show()
+
+ // animate to the value specified
+ .end().animate({ opacity: to }, speed, easing, callback );
+ },
+ animate: function( prop, speed, easing, callback ) {
+ var empty = jQuery.isEmptyObject( prop ),
+ optall = jQuery.speed( speed, easing, callback ),
+ doAnimation = function() {
+ // Operate on a copy of prop so per-property easing won't be lost
+ var anim = Animation( this, jQuery.extend( {}, prop ), optall );
+ doAnimation.finish = function() {
+ anim.stop( true );
+ };
+ // Empty animations, or finishing resolves immediately
+ if ( empty || jQuery._data( this, "finish" ) ) {
+ anim.stop( true );
+ }
+ };
+ doAnimation.finish = doAnimation;
+
+ return empty || optall.queue === false ?
+ this.each( doAnimation ) :
+ this.queue( optall.queue, doAnimation );
+ },
+ stop: function( type, clearQueue, gotoEnd ) {
+ var stopQueue = function( hooks ) {
+ var stop = hooks.stop;
+ delete hooks.stop;
+ stop( gotoEnd );
+ };
+
+ if ( typeof type !== "string" ) {
+ gotoEnd = clearQueue;
+ clearQueue = type;
+ type = undefined;
+ }
+ if ( clearQueue && type !== false ) {
+ this.queue( type || "fx", [] );
+ }
+
+ return this.each(function() {
+ var dequeue = true,
+ index = type != null && type + "queueHooks",
+ timers = jQuery.timers,
+ data = jQuery._data( this );
+
+ if ( index ) {
+ if ( data[ index ] && data[ index ].stop ) {
+ stopQueue( data[ index ] );
+ }
+ } else {
+ for ( index in data ) {
+ if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
+ stopQueue( data[ index ] );
+ }
+ }
+ }
+
+ for ( index = timers.length; index--; ) {
+ if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+ timers[ index ].anim.stop( gotoEnd );
+ dequeue = false;
+ timers.splice( index, 1 );
+ }
+ }
+
+ // start the next in the queue if the last step wasn't forced
+ // timers currently will call their complete callbacks, which will dequeue
+ // but only if they were gotoEnd
+ if ( dequeue || !gotoEnd ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ finish: function( type ) {
+ if ( type !== false ) {
+ type = type || "fx";
+ }
+ return this.each(function() {
+ var index,
+ data = jQuery._data( this ),
+ queue = data[ type + "queue" ],
+ hooks = data[ type + "queueHooks" ],
+ timers = jQuery.timers,
+ length = queue ? queue.length : 0;
+
+ // enable finishing flag on private data
+ data.finish = true;
+
+ // empty the queue first
+ jQuery.queue( this, type, [] );
+
+ if ( hooks && hooks.cur && hooks.cur.finish ) {
+ hooks.cur.finish.call( this );
+ }
+
+ // look for any active animations, and finish them
+ for ( index = timers.length; index--; ) {
+ if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
+ timers[ index ].anim.stop( true );
+ timers.splice( index, 1 );
+ }
+ }
+
+ // look for any animations in the old queue and finish them
+ for ( index = 0; index < length; index++ ) {
+ if ( queue[ index ] && queue[ index ].finish ) {
+ queue[ index ].finish.call( this );
+ }
+ }
+
+ // turn off finishing flag
+ delete data.finish;
+ });
+ }
+});
+
+// Generate parameters to create a standard animation
+function genFx( type, includeWidth ) {
+ var which,
+ attrs = { height: type },
+ i = 0;
+
+ // if we include width, step value is 1 to do all cssExpand values,
+ // if we don't include width, step value is 2 to skip over Left and Right
+ includeWidth = includeWidth? 1 : 0;
+ for( ; i < 4 ; i += 2 - includeWidth ) {
+ which = cssExpand[ i ];
+ attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
+ }
+
+ if ( includeWidth ) {
+ attrs.opacity = attrs.width = type;
+ }
+
+ return attrs;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx("show"),
+ slideUp: genFx("hide"),
+ slideToggle: genFx("toggle"),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" },
+ fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return this.animate( props, speed, easing, callback );
+ };
+});
+
+jQuery.speed = function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+ // normalize opt.queue - true/undefined/null -> "fx"
+ if ( opt.queue == null || opt.queue === true ) {
+ opt.queue = "fx";
+ }
+
+ // Queueing
+ opt.old = opt.complete;
+
+ opt.complete = function() {
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
+ }
+
+ if ( opt.queue ) {
+ jQuery.dequeue( this, opt.queue );
+ }
+ };
+
+ return opt;
+};
+
+jQuery.easing = {
+ linear: function( p ) {
+ return p;
+ },
+ swing: function( p ) {
+ return 0.5 - Math.cos( p*Math.PI ) / 2;
+ }
+};
+
+jQuery.timers = [];
+jQuery.fx = Tween.prototype.init;
+jQuery.fx.tick = function() {
+ var timer,
+ timers = jQuery.timers,
+ i = 0;
+
+ fxNow = jQuery.now();
+
+ for ( ; i < timers.length; i++ ) {
+ timer = timers[ i ];
+ // Checks the timer has not already been removed
+ if ( !timer() && timers[ i ] === timer ) {
+ timers.splice( i--, 1 );
+ }
+ }
+
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+ fxNow = undefined;
+};
+
+jQuery.fx.timer = function( timer ) {
+ if ( timer() && jQuery.timers.push( timer ) ) {
+ jQuery.fx.start();
+ }
+};
+
+jQuery.fx.interval = 13;
+
+jQuery.fx.start = function() {
+ if ( !timerId ) {
+ timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
+ }
+};
+
+jQuery.fx.stop = function() {
+ clearInterval( timerId );
+ timerId = null;
+};
+
+jQuery.fx.speeds = {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+};
+
+// Back Compat <1.8 extension point
+jQuery.fx.step = {};
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+ };
+}
+jQuery.fn.offset = function( options ) {
+ if ( arguments.length ) {
+ return options === undefined ?
+ this :
+ this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ var docElem, win,
+ box = { top: 0, left: 0 },
+ elem = this[ 0 ],
+ doc = elem && elem.ownerDocument;
+
+ if ( !doc ) {
+ return;
+ }
+
+ docElem = doc.documentElement;
+
+ // Make sure it's not a disconnected DOM node
+ if ( !jQuery.contains( docElem, elem ) ) {
+ return box;
+ }
+
+ // If we don't have gBCR, just use 0,0 rather than error
+ // BlackBerry 5, iOS 3 (original iPhone)
+ if ( typeof elem.getBoundingClientRect !== core_strundefined ) {
+ box = elem.getBoundingClientRect();
+ }
+ win = getWindow( doc );
+ return {
+ top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ),
+ left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
+ };
+};
+
+jQuery.offset = {
+
+ setOffset: function( elem, options, i ) {
+ var position = jQuery.css( elem, "position" );
+
+ // set position first, in-case top/left are set even on static elem
+ if ( position === "static" ) {
+ elem.style.position = "relative";
+ }
+
+ var curElem = jQuery( elem ),
+ curOffset = curElem.offset(),
+ curCSSTop = jQuery.css( elem, "top" ),
+ curCSSLeft = jQuery.css( elem, "left" ),
+ calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+ props = {}, curPosition = {}, curTop, curLeft;
+
+ // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+ if ( calculatePosition ) {
+ curPosition = curElem.position();
+ curTop = curPosition.top;
+ curLeft = curPosition.left;
+ } else {
+ curTop = parseFloat( curCSSTop ) || 0;
+ curLeft = parseFloat( curCSSLeft ) || 0;
+ }
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ if ( options.top != null ) {
+ props.top = ( options.top - curOffset.top ) + curTop;
+ }
+ if ( options.left != null ) {
+ props.left = ( options.left - curOffset.left ) + curLeft;
+ }
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+
+jQuery.fn.extend({
+
+ position: function() {
+ if ( !this[ 0 ] ) {
+ return;
+ }
+
+ var offsetParent, offset,
+ parentOffset = { top: 0, left: 0 },
+ elem = this[ 0 ];
+
+ // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
+ if ( jQuery.css( elem, "position" ) === "fixed" ) {
+ // we assume that getBoundingClientRect is available when computed position is fixed
+ offset = elem.getBoundingClientRect();
+ } else {
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent();
+
+ // Get correct offsets
+ offset = this.offset();
+ if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
+ parentOffset = offsetParent.offset();
+ }
+
+ // Add offsetParent borders
+ parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
+ parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
+ }
+
+ // Subtract parent offsets and element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ return {
+ top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
+ left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || document.documentElement;
+ while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent || document.documentElement;
+ });
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
+ var top = /Y/.test( prop );
+
+ jQuery.fn[ method ] = function( val ) {
+ return jQuery.access( this, function( elem, method, val ) {
+ var win = getWindow( elem );
+
+ if ( val === undefined ) {
+ return win ? (prop in win) ? win[ prop ] :
+ win.document.documentElement[ method ] :
+ elem[ method ];
+ }
+
+ if ( win ) {
+ win.scrollTo(
+ !top ? val : jQuery( win ).scrollLeft(),
+ top ? val : jQuery( win ).scrollTop()
+ );
+
+ } else {
+ elem[ method ] = val;
+ }
+ }, method, val, arguments.length, null );
+ };
+});
+
+function getWindow( elem ) {
+ return jQuery.isWindow( elem ) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+ jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+ // margin is only for outerHeight, outerWidth
+ jQuery.fn[ funcName ] = function( margin, value ) {
+ var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+ extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+
+ return jQuery.access( this, function( elem, type, value ) {
+ var doc;
+
+ if ( jQuery.isWindow( elem ) ) {
+ // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+ // isn't a whole lot we can do. See pull request at this URL for discussion:
+ // https://github.com/jquery/jquery/pull/764
+ return elem.document.documentElement[ "client" + name ];
+ }
+
+ // Get document width or height
+ if ( elem.nodeType === 9 ) {
+ doc = elem.documentElement;
+
+ // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
+ // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
+ return Math.max(
+ elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+ elem.body[ "offset" + name ], doc[ "offset" + name ],
+ doc[ "client" + name ]
+ );
+ }
+
+ return value === undefined ?
+ // Get width or height on the element, requesting but not forcing parseFloat
+ jQuery.css( elem, type, extra ) :
+
+ // Set width or height on the element
+ jQuery.style( elem, type, value, extra );
+ }, type, chainable ? margin : undefined, chainable, null );
+ };
+ });
+});
+// Limit scope pollution from any deprecated API
+// (function() {
+
+// })();
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+// Expose jQuery as an AMD module, but only for AMD loaders that
+// understand the issues with loading multiple versions of jQuery
+// in a page that all might call define(). The loader will indicate
+// they have special allowances for multiple jQuery versions by
+// specifying define.amd.jQuery = true. Register as a named module,
+// since jQuery can be concatenated with other files that may use define,
+// but not use a proper concatenation script that understands anonymous
+// AMD modules. A named AMD is safest and most robust way to register.
+// Lowercase jquery is used because AMD module names are derived from
+// file names, and jQuery is normally delivered in a lowercase file name.
+// Do this after creating the global so that if an AMD module wants to call
+// noConflict to hide this version of jQuery, it will work.
+if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
+ define( "jquery", [], function () { return jQuery; } );
+}
+
+})( window );
diff --git a/dev/docs/source/_themes/bootstrap/static/js/jquery-1.9.1.min.js b/dev/docs/source/_themes/bootstrap/static/js/jquery-1.9.1.min.js
new file mode 100644
index 0000000..006e953
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/static/js/jquery-1.9.1.min.js
@@ -0,0 +1,5 @@
+/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license
+//@ sourceMappingURL=jquery.min.map
+*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|tru [...]
+return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:fu [...]
+}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b. [...]
\ No newline at end of file
diff --git a/dev/docs/source/_themes/bootstrap/static/js/jquery-fix.js b/dev/docs/source/_themes/bootstrap/static/js/jquery-fix.js
new file mode 100644
index 0000000..2d2f84f
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/static/js/jquery-fix.js
@@ -0,0 +1,2 @@
+// No Conflict in later (our) version of jQuery
+var $jqTheme = jQuery.noConflict(true);
diff --git a/dev/docs/source/_themes/bootstrap/static/js/jquery.min.map b/dev/docs/source/_themes/bootstrap/static/js/jquery.min.map
new file mode 100644
index 0000000..3b19b1a
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/static/js/jquery.min.map
@@ -0,0 +1 @@
+{"version":3,"file":"jquery-1.9.1.min.js","sources":["jquery-1.9.1.js"],"names":["window","undefined","readyList","rootjQuery","core_strundefined","document","location","_jQuery","jQuery","_$","$","class2type","core_deletedIds","core_version","core_concat","concat","core_push","push","core_slice","slice","core_indexOf","indexOf","core_toString","toString","core_hasOwn","hasOwnProperty","core_trim","trim","selector","context","fn","init","core_pnum","source","core_rnotwhite","rtrim","rqui [...]
\ No newline at end of file
diff --git a/dev/docs/source/_themes/bootstrap/theme.conf b/dev/docs/source/_themes/bootstrap/theme.conf
new file mode 100644
index 0000000..d980ead
--- /dev/null
+++ b/dev/docs/source/_themes/bootstrap/theme.conf
@@ -0,0 +1,60 @@
+# Twitter Bootstrap Theme
+[theme]
+inherit = basic
+stylesheet = basic.css
+pygments_style = tango
+
+# Configurable options.
+[options]
+# Navigation bar title. (Default: ``project`` value)
+navbar_title =
+
+# Tab name for entire site. (Default: "Site")
+navbar_site_name = Contents
+
+# A list of tuples containting pages to link to. The value should be
+# in the form [(name, page), ..]
+navbar_links =
+
+# Render the next and previous page links in navbar. (Default: true)
+navbar_sidebarrel = true
+
+# Render the current pages TOC in the navbar. (Default: true)
+navbar_pagenav = false
+
+# Global TOC depth for "site" navbar tab. (Default: 1)
+# Switching to -1 shows all levels.
+globaltoc_depth = 1
+
+# Include hidden TOCs in Site navbar?
+#
+# Note: If this is "false", you cannot have mixed ``:hidden:`` and
+# non-hidden ``toctree`` directives in the same page, or else the build
+# will break.
+#
+# Values: "true" (default) or "false"
+globaltoc_includehidden = true
+
+# HTML navbar class (Default: "navbar") to attach to <div> element.
+# For black navbar, do "navbar navbar-inverse"
+navbar_class = navbar
+
+# Fix navigation bar to top of page?
+# Values: "true" (default) or "false"
+navbar_fixed_top = true
+
+# Location of link to source.
+# Options are "nav" (default), "footer" or anything else to exclude.
+source_link_position = none
+
+# Bootswatch (http://bootswatch.com/) theme.
+#
+# Options are nothing with "" (default) or the name of a valid theme such as
+# "amelia" or "cosmo".
+#
+# Note that this is served off CDN, so won't be available offline.
+bootswatch_theme = ""
+
+# Switch Bootstrap version?
+# Values: "3" (default) or "2"
+bootstrap_version = 3
diff --git a/dev/docs/source/alternatives.rst b/dev/docs/source/alternatives.rst
new file mode 100644
index 0000000..08bd402
--- /dev/null
+++ b/dev/docs/source/alternatives.rst
@@ -0,0 +1,52 @@
+.. _alternatives:
+
+Alternative modules for handling Excel files
+============================================
+
+The following are some Python alternatives to XlsxWriter.
+
+
+XLWT
+----
+
+From the `xlwt <http://pypi.python.org/pypi/xlwt>`_ page on PyPI:
+
+ Library to create spreadsheet files compatible with MS Excel
+ 97/2000/XP/2003 XLS files, on any platform, with Python 2.3 to 2.7.
+
+ xlwt is a library for generating spreadsheet files that are compatible
+ with Excel 97/2000/XP/2003, OpenOffice.org Calc, and Gnumeric. xlwt has
+ full support for Unicode. Excel spreadsheets can be generated on any
+ platform without needing Excel or a COM server. The only requirement is
+ Python 2.3 to 2.7.
+
+
+
+XLRD
+----
+
+From the `xlrd <http://pypi.python.org/pypi/xlrd>`_ page on PyPI:
+
+ Library for developers to extract data from Microsoft Excel (tm)
+ spreadsheet files Extract data from Excel spreadsheets (.xls and .xlsx,
+ versions 2.0 onwards) on any platform. Pure Python (2.6, 2.7, 3.2+). Strong
+ support for Excel dates. Unicode-aware.
+
+
+OpenPyXL
+--------
+From the `openpyxl <http://pypi.python.org/pypi/openpyxl>`_ page on PyPI:
+
+ A Python library to read/write Excel 2007 xlsx/xlsm files. Openpyxl is a
+ pure python reader and writer of Excel OpenXML files. It is ported from the
+ PHPExcel project.
+
+
+Xlwings
+-------
+
+From the `xlwings <http://xlwings.org>`_ webpage:
+
+ Replace your VBA code with Python, a powerful yet easy-to-use programming
+ language that is highly suited for numerical analysis. Supports Windows &
+ Mac.
diff --git a/dev/docs/source/author.rst b/dev/docs/source/author.rst
new file mode 100644
index 0000000..2dbad5b
--- /dev/null
+++ b/dev/docs/source/author.rst
@@ -0,0 +1,62 @@
+.. _author:
+
+Author
+======
+
+XlsxWriter was written by John McNamara.
+
+* `GitHub <https://github.com/jmcnamara>`_
+* `Twitter @jmcnamara13 <https://twitter.com/jmcnamara13>`_
+
+
+Asking questions
+----------------
+
+If you have questions about XlsxWriter here are some ways to deal with them:
+
+* **Bug Reports**:
+
+ See the :ref:`reporting_bugs` section of the docs.
+
+* **Feature Requests**:
+
+ Open a Feature Request issue on
+ `Github issues <https://github.com/jmcnamara/XlsxWriter/issues>`_.
+
+* **Pull Requests**:
+
+ See the `Contributing Guide
+ <https://github.com/jmcnamara/XlsxWriter/blob/master/CONTRIBUTING.md>`_.
+ Note, all Pull Requests must start with an Issue Tracker.
+
+* **General Questions**:
+
+ General questions about how to use the module should be asked on
+ `StackOverflow <http://stackoverflow.com/search?tab=newest&q=xlsxwriter>`_.
+ Add the ``xlsxwriter`` tag to the question.
+
+ Questions on StackOverflow have the advantage of (usually) getting several
+ answers and it also leaves a searchable question for someone else.
+
+* **Email**:
+
+ If none of the above apply you can contact me at jmcnamara at cpan.org.
+
+
+If you found XlsxWriter useful
+------------------------------
+
+If you have found XlsxWriter useful, then
+`donations <https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RRZCPSL65X858>`_
+are always welcome.
+
+.. raw:: html
+
+ <center>
+ <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
+ <input type="hidden" name="cmd" value="_s-xclick">
+ <input type="hidden" name="hosted_button_id" value="RRZCPSL65X858">
+ <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal">
+ <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
+ </form>
+ </center>
diff --git a/dev/docs/source/bugs.rst b/dev/docs/source/bugs.rst
new file mode 100644
index 0000000..8c3696e
--- /dev/null
+++ b/dev/docs/source/bugs.rst
@@ -0,0 +1,179 @@
+.. _bugs:
+
+Known Issues and Bugs
+=====================
+
+This section lists known issues and bugs and gives some information on how to
+submit bug reports.
+
+"Content is Unreadable. Open and Repair"
+----------------------------------------
+
+You may occasionally see an Excel warning when opening an XlsxWriter file
+like:
+
+ Excel could not open file.xlsx because some content is unreadable. Do you
+ want to open and repair this workbook.
+
+This ominous sounding message is Excel's default warning for any validation
+error in the XML used for the components of the XLSX file.
+
+The error message and the actual file aren't helpful in debugging issues like
+this. If you do encounter this warning you should open an issue on GitHub with
+a program to replicate it (see :ref:`reporting_bugs`).
+
+
+"Exception caught in workbook destructor. Explicit close() may be required"
+---------------------------------------------------------------------------
+
+The following exception, or similar, can occur if the :func:`close` method
+isn't used at the end of the program::
+
+ Exception Exception: Exception('Exception caught in workbook destructor.
+ Explicit close() may be required for workbook.',)
+ in <bound method Workbook.__del__ of <xlsxwriter.workbook.Workbookobject
+ at 0x103297d50>>
+
+Note, it is possible that this exception will also be raised as part of
+another exception that occurs during workbook destruction. In either case
+ensure that there is an explicit ``workbook.close()`` in the program.
+
+
+Formulas displayed as ``#NAME?`` until edited
+---------------------------------------------
+
+There are a few reasons why a formula written by XlsxWriter would generate a
+``#NAME?`` error in Excel:
+
+* Invalid formula syntax.
+* Non-English function names.
+* Semi-colon separators instead of commas.
+* Use of Excel 2010 and later functions without a prefix.
+
+See :ref:`working_with_formulas` and :ref:`formula_errors` for a more details
+and a explanation of how to debug the issue.
+
+
+Formula results displaying as zero in non-Excel applications
+------------------------------------------------------------
+
+Due to wide range of possible formulas and interdependencies between them
+XlsxWriter doesn't, and realistically cannot, calculate the result of a
+formula when it is written to an XLSX file. Instead, it stores the value 0 as
+the formula result. It then sets a global flag in the XLSX file to say that
+all formulas and functions should be recalculated when the file is opened.
+
+This is the method recommended in the Excel documentation and in general it
+works fine with spreadsheet applications. However, applications that don’t
+have a facility to calculate formulas, such as Excel Viewer, or several mobile
+applications, will only display the 0 results.
+
+See :ref:`formula_result` for more details and a workaround.
+
+
+Strings aren't displayed in Apple Numbers in 'constant_memory' mode
+-------------------------------------------------------------------
+
+In :func:`Workbook` ``'constant_memory'`` mode XlsxWriter uses an optimization
+where cell strings aren't stored in an Excel structure call "shared strings"
+and instead are written "in-line".
+
+This is a documented Excel feature that is supported by most spreadsheet
+applications. One known exception is Apple Numbers for Mac where the string
+data isn't displayed.
+
+
+Images not displayed correctly in Excel 2001 for Mac and non-Excel applications
+-------------------------------------------------------------------------------
+
+Images inserted into worksheets via :func:`insert_image` may not display
+correctly in Excel 2011 for Mac and non-Excel applications such as OpenOffice
+and LibreOffice. Specifically the images may looked stretched or squashed.
+
+This is not specifically an XlsxWriter issue. It also occurs with files created
+in Excel 2007 and Excel 2010.
+
+
+Charts series created from Worksheet Tables cannot have user defined names
+--------------------------------------------------------------------------
+
+In Excel, charts created from :ref:`Worksheet Tables <tables>` have a
+limitation where the data series name, if specified, must refer to a cell
+within the table.
+
+To workaround this Excel limitation you can specify a user defined name in the
+table and refer to that from the chart. See :ref:`charts_from_tables`.
+
+
+.. _reporting_bugs:
+
+Reporting Bugs
+==============
+
+Here are some tips on reporting bugs in XlsxWriter.
+
+
+Upgrade to the latest version of the module
+-------------------------------------------
+
+The bug you are reporting may already be fixed in the latest version of the
+module. You can check which version of XlsxWriter that you are using as
+follows::
+
+ python -c 'import xlsxwriter; print(xlsxwriter.__version__)'
+
+Check the :ref:`changes` section to see what has changed in the latest versions.
+
+
+Read the documentation
+----------------------
+
+Read or search the XlsxWriter documentation to see if the issue you are
+encountering is already explained.
+
+Look at the example programs
+----------------------------
+
+There are many :ref:`examples` in the distribution. Try to identify an example
+program that corresponds to your query and adapt it to use as a bug report.
+
+Use the official XlsxWriter Issue tracker on GitHub
+---------------------------------------------------
+
+The official XlsxWriter
+`Issue tracker is on GitHub <https://github.com/jmcnamara/XlsxWriter/issues>`_.
+
+
+Pointers for submitting a bug report
+------------------------------------
+
+#. Describe the problem as clearly and as concisely as possible.
+
+#. Include a sample program. This is probably the most important step. It is
+ generally easier to describe a problem in code than in written prose.
+
+#. The sample program should be as small as possible to demonstrate the
+ problem. Don't copy and paste large non-relevant sections of your program.
+
+A sample bug report is shown below. This format helps to analyze and respond to
+the bug report more quickly.
+
+ **Issue with SOMETHING**
+
+ I am using XlsxWriter to do SOMETHING but it appears to do SOMETHING ELSE.
+
+ I am using Python version X.Y.Z and XlsxWriter x.y.z.
+
+ Here is some code that demonstrates the problem::
+
+ import xlsxwriter
+
+ workbook = xlsxwriter.Workbook('hello.xlsx')
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Hello world')
+
+ workbook.close()
+
+See also how `How to create a Minimal, Complete, and Verifiable example
+<http://stackoverflow.com/help/mcve>`_ from StackOverflow.
diff --git a/dev/docs/source/changes.rst b/dev/docs/source/changes.rst
new file mode 100644
index 0000000..bdf5e0e
--- /dev/null
+++ b/dev/docs/source/changes.rst
@@ -0,0 +1,10 @@
+:tocdepth: 1
+
+.. _changes:
+
+Changes in XlsxWriter
+=====================
+
+This section shows changes and bug fixes in the XlsxWriter module.
+
+.. include:: ../../../Changes
diff --git a/dev/docs/source/chart.rst b/dev/docs/source/chart.rst
new file mode 100644
index 0000000..de7b91c
--- /dev/null
+++ b/dev/docs/source/chart.rst
@@ -0,0 +1,1126 @@
+.. _chart_class:
+
+The Chart Class
+===============
+
+The ``Chart`` module is a base class for modules that implement charts in
+XlsxWriter. The information in this section is applicable to all of the
+available chart subclasses, such as Area, Bar, Column, Doughnut, Line, Pie,
+Scatter, Stock and Radar.
+
+A chart object is created via the Workbook :func:`add_chart()` method where the
+chart type is specified::
+
+ chart = workbook.add_chart({'type': 'column'})
+
+It is then inserted into a worksheet as an embedded chart using the
+:func:`insert_chart` Worksheet method::
+
+ worksheet.insert_chart('A7', chart)
+
+Or it can be set in a chartsheet using the :func:`set_chart` Chartsheet method::
+
+ chartsheet = workbook.add_chartsheet()
+ # ...
+ chartsheet.set_chart(chart)
+
+
+The following is a small working example or adding an embedded chart::
+
+ import xlsxwriter
+
+ workbook = xlsxwriter.Workbook('chart.xlsx')
+ worksheet = workbook.add_worksheet()
+
+ # Create a new Chart object.
+ chart = workbook.add_chart({'type': 'column'})
+
+ # Write some data to add to plot on the chart.
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ # Configure the chart. In simplest case we add one or more data series.
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ # Insert the chart into the worksheet.
+ worksheet.insert_chart('A7', chart)
+
+ workbook.close()
+
+.. image:: _images/chart_simple.png
+ :scale: 75 %
+
+
+The supported chart types are:
+
+* ``area``: Creates an Area (filled line) style chart.
+
+* ``bar``: Creates a Bar style (transposed histogram) chart.
+
+* ``column``: Creates a column style (histogram) chart.
+
+* ``line``: Creates a Line style chart.
+
+* ``pie``: Creates a Pie style chart.
+
+* ``doughnut``: Creates a Doughnut style chart.
+
+* ``scatter``: Creates a Scatter style chart.
+
+* ``stock``: Creates a Stock style chart.
+
+* ``radar``: Creates a Radar style chart.
+
+
+Chart subtypes are also supported for some chart types::
+
+ workbook.add_chart({'type': 'bar', 'subtype': 'stacked'})
+
+The available subtypes are::
+
+ area
+ stacked
+ percent_stacked
+
+ bar
+ stacked
+ percent_stacked
+
+ column
+ stacked
+ percent_stacked
+
+ scatter
+ straight_with_markers
+ straight
+ smooth_with_markers
+ smooth
+
+ radar
+ with_markers
+ filled
+
+
+Methods that are common to all chart types are documented below. See
+:ref:`working_with_charts` for chart specific information.
+
+
+chart.add_series()
+------------------
+
+.. py:function:: add_series(options)
+
+ Add a data series to a chart.
+
+ :param dict options: A dictionary of chart series options.
+
+In Excel a chart **series** is a collection of information that defines which
+data is plotted such as values, axis labels and formatting.
+
+For an XlsxWriter chart object the ``add_series()`` method is used to set the
+properties for a series::
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'line': {'color': 'red'},
+ })
+
+ # Or using a list of values instead of category/value formulas:
+ # [sheetname, first_row, first_col, last_row, last_col]
+ chart.add_series({
+ 'categories': ['Sheet1', 0, 0, 4, 0],
+ 'values': ['Sheet1', 0, 1, 4, 1],
+ 'line': {'color': 'red'},
+ })
+
+As shown above the ``categories`` and ``values`` can take either a range
+formula such as ``=Sheet1!$A$2:$A$7`` or, more usefully when generating the
+range programmatically, a list with zero indexed row/column values.
+
+The series options that can be set are:
+
+* ``values``: This is the most important property of a series and is the only
+ mandatory option for every chart object. This option links the chart with
+ the worksheet data that it displays. The data range can be set using a
+ formula as shown in the first example above or using a list of values as
+ shown in the second example.
+
+* ``categories``: This sets the chart category labels. The category is more
+ or less the same as the X axis. In most chart types the ``categories``
+ property is optional and the chart will just assume a sequential series from
+ ``1..n``.
+
+* ``name``: Set the name for the series. The name is displayed in the chart
+ legend and in the formula bar. The name property is optional and if it isn't
+ supplied it will default to ``Series 1..n``. The name can also be a formula
+ such as ``=Sheet1!$A$1`` or a list with a sheetname, row and column such as
+ ``['Sheet1', 0, 0]``.
+
+* ``line``: Set the properties of the series line type such as color and
+ width. See :ref:`chart_formatting_line`.
+
+* ``border``: Set the border properties of the series such as color and
+ style. See :ref:`chart_formatting_border`.
+
+* ``fill``: Set the solid fill properties of the series such as color. See
+ :ref:`chart_formatting_fill`.
+
+* ``pattern``: Set the pattern fill properties of the series. See
+ :ref:`chart_formatting_pattern`.
+
+* ``gradient``: Set the gradient fill properties of the series. See
+ :ref:`chart_formatting_gradient`.
+
+* ``marker``: Set the properties of the series marker such as style and
+ color. See :ref:`chart_series_option_marker`.
+
+* ``trendline``: Set the properties of the series trendline such as linear,
+ polynomial and moving average types. See
+ :ref:`chart_series_option_trendline`.
+
+* ``smooth``: Set the smooth property of a line series.
+
+* ``y_error_bars``: Set vertical error bounds for a chart series. See
+ :ref:`chart_series_option_error_bars`.
+
+* ``x_error_bars``: Set horizontal error bounds for a chart series. See
+ :ref:`chart_series_option_error_bars`.
+
+* ``data_labels``: Set data labels for the series. See
+ :ref:`chart_series_option_data_labels`.
+
+* ``points``: Set properties for individual points in a series. See
+ :ref:`chart_series_option_points`.
+
+* ``invert_if_negative``: Invert the fill color for negative values. Usually
+ only applicable to column and bar charts.
+
+* ``overlap``: Set the overlap between series in a Bar/Column chart. The
+ range is +/- 100. The default is 0::
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'overlap': 10,
+ })
+
+ Note, it is only necessary to apply the ``overlap`` property to one series
+ in the chart.
+
+* ``gap``: Set the gap between series in a Bar/Column chart. The range is 0
+ to 500. The default is 150::
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'gap': 200,
+ })
+
+ Note, it is only necessary to apply the ``gap`` property to one series in
+ the chart.
+
+
+More than one series can be added to a chart. In fact, some chart types such as
+``stock`` require it. The series numbering and order in the Excel chart will
+be the same as the order in which they are added in XlsxWriter.
+
+It is also possible to specify non-contiguous ranges::
+
+ chart.add_series({
+ 'categories': '=(Sheet1!$A$1:$A$9,Sheet1!$A$14:$A$25)',
+ 'values': '=(Sheet1!$B$1:$B$9,Sheet1!$B$14:$B$25)',
+ })
+
+
+chart.set_x_axis()
+------------------
+
+.. py:function:: set_x_axis(options)
+
+ Set the chart X axis options.
+
+ :param dict options: A dictionary of axis options.
+
+The ``set_x_axis()`` method is used to set properties of the X axis::
+
+ chart.set_x_axis({
+ 'name': 'Earnings per Quarter',
+ 'name_font': {'size': 14, 'bold': True},
+ 'num_font': {'italic': True },
+ })
+
+.. image:: _images/chart_x_axis.png
+ :scale: 75 %
+
+The options that can be set are::
+
+ name
+ name_font
+ name_layout
+ num_font
+ num_format
+ line
+ fill
+ pattern
+ gradient
+ min
+ max
+ minor_unit
+ major_unit
+ interval_unit
+ interval_tick
+ crossing
+ position_axis
+ reverse
+ log_base
+ label_position
+ major_gridlines
+ minor_gridlines
+ visible
+ date_axis
+ text_axis
+ minor_unit_type
+ major_unit_type
+ minor_tick_mark
+ major_tick_mark
+ display_units
+ display_units_visible
+
+These options are explained below. Some properties are only applicable to
+**value**, **category** or **date** axes (this is noted in each case). See
+:ref:`chart_val_cat_axes` for an explanation of Excel's distinction between
+the axis types.
+
+* ``name``: Set the name (also known as title or caption) for the axis. The
+ name is displayed below the X axis. (Applicable to category, date and value
+ axes.)::
+
+ chart.set_x_axis({'name': 'Earnings per Quarter'})
+
+ This property is optional. The default is to have no axis name.
+
+ The name can also be a formula such as ``=Sheet1!$A$1`` or a list with a
+ sheetname, row and column such as ``['Sheet1', 0, 0]``.
+
+* ``name_font``: Set the font properties for the axis name. (Applicable to
+ category, date and value axes.)::
+
+ chart.set_x_axis({'name_font': {'bold': True, 'italic': True}})
+
+ See the :ref:`chart_fonts` section for more details on font properties.
+
+* ``name_layout``: Set the ``(x, y)`` position of the axis caption in chart
+ relative units. (Applicable to category, date and value axes.)::
+
+ chart.set_x_axis({
+ 'name': 'X axis',
+ 'name_layout': {
+ 'x': 0.34,
+ 'y': 0.85,
+ }
+ })
+
+ See the :ref:`chart_layout` section for more details.
+
+* ``num_font``: Set the font properties for the axis numbers. (Applicable to
+ category, date and value axes.)::
+
+ chart.set_x_axis({'name_font': {'bold': True, 'italic': True}})
+
+ See the :ref:`chart_fonts` section for more details on font properties.
+
+* ``num_format``: Set the number format for the axis. (Applicable to
+ category, date and value axes.)::
+
+ chart.set_x_axis({'num_format': '#,##0.00'})
+ chart.set_y_axis({'num_format': '0.00%'})
+
+ The number format is similar to the Worksheet Cell Format ``num_format``
+ apart from the fact that a format index cannot be used. An explicit format
+ string must be used as shown above. See :func:`set_num_format()` for more
+ information.
+
+* ``line``: Set the properties of the axis line type such as color and
+ width. See :ref:`chart_formatting_line`::
+
+ chart.set_x_axis({'line': {'none': True}})
+
+* ``fill``: Set the solid fill properties of the axis such as color. See
+ :ref:`chart_formatting_fill`. Note, in Excel the axis fill is applied to
+ the area of the numbers of the axis and not to the area of the axis bounding
+ box. That background is set from the chartarea fill.
+
+* ``pattern``: Set the pattern fill properties of the axis. See
+ :ref:`chart_formatting_pattern`.
+
+* ``gradient``: Set the gradient fill properties of the axis. See
+ :ref:`chart_formatting_gradient`.
+
+* ``min``: Set the minimum value for the axis range. (Applicable to value and
+ date axes only.)::
+
+ chart.set_x_axis({'min': 3, 'max': 6})
+
+ .. image:: _images/chart_max_min.png
+ :scale: 75 %
+
+* ``max``: Set the maximum value for the axis range. (Applicable to value and
+ date axes only.)
+
+* ``minor_unit``: Set the increment of the minor units in the axis range.
+ (Applicable to value and date axes only.)::
+
+ chart.set_x_axis({'minor_unit': 0.4, 'major_unit': 2})
+
+* ``major_unit``: Set the increment of the major units in the axis range.
+ (Applicable to value and date axes only.)
+
+* ``interval_unit``: Set the interval unit for a category axis. Should be an
+ integer value. (Applicable to category axes only.)::
+
+ chart.set_x_axis({'interval_unit': 5})
+
+* ``interval_tick``: Set the tick interval for a category axis. Should be an
+ integer value. (Applicable to category axes only.)::
+
+ chart.set_x_axis({'interval_tick': 2})
+
+* ``crossing``: Set the position where the y axis will cross the x axis.
+ (Applicable to all axes.)
+
+ The ``crossing`` value can either be the string ``'max'`` to set the
+ crossing at the maximum axis value or a numeric value::
+
+ chart.set_x_axis({'crossing': 3})
+ chart.set_y_axis({'crossing': 'max'})
+
+ **For category axes the numeric value must be an integer** to represent the
+ category number that the axis crosses at. For value and date axes it can
+ have any value associated with the axis. See also :ref:`chart_val_cat_axes`.
+
+ If crossing is omitted (the default) the crossing will be set automatically
+ by Excel based on the chart data.
+
+* ``position_axis``: Position the axis on or between the axis tick marks.
+ (Applicable to category axes only.)
+
+ There are two allowable values ``on_tick`` and ``between``::
+
+ chart.set_x_axis({'position_axis': 'on_tick'})
+ chart.set_x_axis({'position_axis': 'between'})
+
+* ``reverse``: Reverse the order of the axis categories or values.
+ (Applicable to category, date and value axes.)::
+
+ chart.set_y_axis({'reverse': True})
+
+ .. image:: _images/chart_reverse.png
+ :scale: 75 %
+
+* ``log_base``: Set the log base of the axis range. (Applicable to value axes
+ only.)::
+
+ chart.set_y_axis({'log_base': 10})
+
+* ``label_position``: Set the "Axis labels" position for the axis. The
+ following positions are available::
+
+ next_to (the default)
+ high
+ low
+ none
+
+ For example::
+
+ chart.set_x_axis({'label_position': 'high'})
+ chart.set_y_axis({'label_position': 'low'})
+
+* ``major_gridlines``: Configure the major gridlines for the axis. The
+ available properties are::
+
+ visible
+ line
+
+ For example::
+
+ chart.set_x_axis({
+ 'major_gridlines': {
+ 'visible': True,
+ 'line': {'width': 1.25, 'dash_type': 'dash'}
+ },
+ })
+
+ .. image:: _images/chart_gridlines.png
+ :scale: 75 %
+
+ The ``visible`` property is usually on for the X axis but it depends on the
+ type of chart.
+
+ The ``line`` property sets the gridline properties such as color and
+ width. See :ref:`chart_formatting`.
+
+* ``minor_gridlines``: This takes the same options as ``major_gridlines``
+ above.
+
+ The minor gridline ``visible`` property is off by default for all chart
+ types.
+
+* ``visible``: Configure the visibility of the axis::
+
+ chart.set_y_axis({'visible': False})
+
+ Axes are visible by default.
+
+* ``date_axis``: This option is used to treat a category axis with date or
+ time data as a Date Axis. (Applicable to date category axes only.)::
+
+ chart.set_x_axis({'date_axis': True})
+
+ This option also allows you to set ``max`` and ``min`` values for a
+ category axis which isn't allowed by Excel for non-date category axes.
+
+ See :ref:`date_category_axes` for more details.
+
+* ``text_axis``: This option is used to treat a category axis explicitly
+ as a Text Axis. (Applicable to category axes only.)::
+
+ chart.set_x_axis({'text_axis': True})
+
+* ``minor_unit_type``: For ``date_axis`` axes, see above, this option is used
+ to set the type of the minor units. (Applicable to date category axes
+ only.)::
+
+ chart.set_x_axis({
+ 'date_axis': True,
+ 'minor_unit': 4,
+ 'minor_unit_type': 'months',
+ })
+
+* ``major_unit_type``: Same as ``minor_unit_type``, see above, but for major
+ axes unit types.
+
+* ``minor_tick_mark``: Set the axis minor tick mark type/position to one of
+ the following values::
+
+ none
+ inside
+ outside
+ cross (inside and outside)
+
+ For example::
+
+ chart.set_x_axis({'major_tick_mark': 'none',
+ 'minor_tick_mark': 'inside'})
+
+* ``major_tick_mark``: Same as ``minor_tick_mark``, see above, but for major
+ axes ticks.
+
+* ``display_units``: Set the display units for the axis. This can be useful if
+ the axis numbers are very large but you don't want to represent them in
+ scientific notation. The available display units are::
+
+ hundreds
+ thousands
+ ten_thousands
+ hundred_thousands
+ millions
+ ten_millions
+ hundred_millions
+ billions
+ trillions
+
+ Applicable to value axes only.::
+
+ chart.set_x_axis({'display_units': 'thousands'})
+ chart.set_y_axis({'display_units': 'millions'})
+
+ .. image:: _images/chart_display_units.png
+ :scale: 75 %
+
+
+* ``display_units_visible``: Control the visibility of the display units
+ turned on by the previous option. This option is on by default. (Applicable
+ to value axes only.)::
+
+ chart.set_x_axis({'display_units': 'hundreds',
+ 'display_units_visible': False})
+
+
+chart.set_y_axis()
+------------------
+
+.. py:function:: set_y_axis(options)
+
+ Set the chart Y axis options.
+
+ :param dict options: A dictionary of axis options.
+
+The ``set_y_axis()`` method is used to set properties of the Y axis.
+
+The properties that can be set are the same as for ``set_x_axis``, see above.
+
+
+chart.set_x2_axis()
+-------------------
+
+.. py:function:: set_x2_axis(options)
+
+ Set the chart secondary X axis options.
+
+ :param dict options: A dictionary of axis options.
+
+The ``set_x2_axis()`` method is used to set properties of the secondary X axis,
+see :func:`chart_secondary_axes`.
+
+The properties that can be set are the same as for ``set_x_axis``, see above.
+
+The default properties for this axis are::
+
+ 'label_position': 'none',
+ 'crossing': 'max',
+ 'visible': False,
+
+
+chart.set_y2_axis()
+-------------------
+
+.. py:function:: set_y2_axis(options)
+
+ Set the chart secondary Y axis options.
+
+ :param dict options: A dictionary of axis options.
+
+The ``set_y2_axis()`` method is used to set properties of the secondary Y axis,
+see :func:`chart_secondary_axes`.
+
+The properties that can be set are the same as for ``set_x_axis``, see above.
+
+The default properties for this axis are::
+
+ 'major_gridlines': {'visible': True}
+
+
+chart.combine()
+---------------
+
+.. py:function:: combine(chart)
+
+ Combine two charts of different types.
+
+ :param chart: A chart object created with :func:`add_chart()`.
+
+The chart ``combine()`` method is used to combine two charts of different
+types, for example a column and line chart::
+
+ # Create a primary chart.
+ column_chart = workbook.add_chart({'type': 'column'})
+ column_chart.add_series({...})
+
+ # Create a secondary chart.
+ line_chart = workbook.add_chart({'type': 'line'})
+ line_chart.add_series({...})
+
+ # Combine the charts.
+ column_chart.combine(line_chart)
+
+.. image:: _images/chart_combined1.png
+ :scale: 75 %
+
+
+See the :ref:`chart_combined_charts` section for more details.
+
+
+chart.set_size()
+----------------
+
+.. :noindex: py:function:: set_size(options)
+
+ Set the size or scale of the chart.
+
+ :param dict options: A dictionary of chart size options.
+
+The ``set_size()`` method is used to set the dimensions of the chart. The size
+properties that can be set are::
+
+ width
+ height
+ x_scale
+ y_scale
+ x_offset
+ y_offset
+
+The ``width`` and ``height`` are in pixels. The default chart width x height is
+480 x 288 pixels. The size of the chart can be modified by setting the
+``width`` and ``height`` or by setting the ``x_scale`` and ``y_scale``::
+
+ chart.set_size({'width': 720, 'height': 576})
+ # Same as:
+ chart.set_size({'x_scale': 1.5, 'y_scale': 2})
+
+The ``x_offset`` and ``y_offset`` position the top left corner of the chart in
+the cell that it is inserted into.
+
+Note: the ``x_offset`` and ``y_offset`` parameters can also be set via the
+:func:`insert_chart()` method::
+
+ worksheet.insert_chart('E2', chart, {'x_offset': 25, 'y_offset': 10})
+
+
+chart.set_title()
+-----------------
+
+.. py:function:: set_title(options)
+
+ Set the chart title options.
+
+ :param dict options: A dictionary of chart size options.
+
+The ``set_title()`` method is used to set properties of the chart title::
+
+ chart.set_title({'name': 'Year End Results'})
+
+.. image:: _images/chart_title.png
+ :scale: 75 %
+
+The properties that can be set are:
+
+* ``name``: Set the name (title) for the chart. The name is displayed above
+ the chart. The name can also be a formula such as ``=Sheet1!$A$1`` or a list
+ with a sheetname, row and column such as ``['Sheet1', 0, 0]``. The name
+ property is optional. The default is to have no chart title.
+
+* ``name_font``: Set the font properties for the chart title. See
+ :ref:`chart_fonts`.
+
+* ``overlay``: Allow the title to be overlaid on the chart. Generally used
+ with the layout property below.
+
+* ``layout``: Set the ``(x, y)`` position of the title in chart relative
+ units::
+
+ chart.set_title({
+ 'name': 'Title',
+ 'overlay': True,
+ 'layout': {
+ 'x': 0.42,
+ 'y': 0.14,
+ }
+ })
+
+ See the :ref:`chart_layout` section for more details.
+
+* ``none``: By default Excel adds an automatic chart title to charts with a
+ single series and a user defined series name. The ``none`` option turns this
+ default title off. It also turns off all other ``set_title()`` options::
+
+ chart.set_title({'none': True})
+
+chart.set_legend()
+------------------
+
+.. py:function:: set_legend(options)
+
+ Set the chart legend options.
+
+ :param dict options: A dictionary of chart legend options.
+
+The ``set_legend()`` method is used to set properties of the chart legend. For
+example it can be used to turn off the default chart legend::
+
+ chart.set_legend({'none': True})
+
+.. image:: _images/chart_legend_none.png
+ :scale: 75 %
+
+The options that can be set are::
+
+ none
+ position
+ layout
+ font
+ delete_series
+
+* ``none``: In Excel chart legends are on by default. The ``none`` option
+ turns off the chart legend::
+
+ chart.set_legend({'none': True})
+
+ For backward compatibility, it is also possible to turn off the legend via
+ the ``position`` property::
+
+ chart.set_legend({'position': 'none'})
+
+* ``position``: Set the position of the chart legend::
+
+ chart.set_legend({'position': 'bottom'})
+
+ .. image:: _images/chart_legend_bottom.png
+ :scale: 75 %
+
+ The default legend position is ``right``. The available positions are::
+
+ top
+ bottom
+ left
+ right
+ overlay_left
+ overlay_right
+ none
+
+* ``layout``: Set the ``(x, y)`` position of the legend in chart relative
+ units::
+
+ chart.set_legend({
+ 'layout': {
+ 'x': 0.80,
+ 'y': 0.37,
+ 'width': 0.12,
+ 'height': 0.25,
+ }
+ })
+
+ See the :ref:`chart_layout` section for more details.
+
+* ``font``: Set the font properties of the chart legend::
+
+ chart.set_legend({'font': {'size': 9, 'bold': 1}})
+
+ See the :ref:`chart_fonts` section for more details on font properties.
+
+* ``delete_series``: This allows you to remove one or more series from the
+ the legend (the series will still display on the chart). This property takes
+ a list as an argument and the series are zero indexed::
+
+ # Delete/hide series index 0 and 2 from the legend.
+ chart.set_legend({'delete_series': [0, 2]})
+
+ .. image:: _images/chart_legend_delete.png
+ :scale: 75 %
+
+
+chart.set_chartarea()
+---------------------
+
+.. py:function:: set_chartarea(options)
+
+ Set the chart area options.
+
+ :param dict options: A dictionary of chart area options.
+
+The ``set_chartarea()`` method is used to set the properties of the chart area.
+In Excel the chart area is the background area behind the chart::
+
+ chart.set_chartarea({
+ 'border': {'none': True},
+ 'fill': {'color': 'red'}
+ })
+
+.. image:: _images/chart_chartarea.png
+ :scale: 75 %
+
+The properties that can be set are:
+
+* ``border``: Set the border properties of the chartarea such as color and
+ style. See :ref:`chart_formatting_border`.
+
+* ``fill``: Set the solid fill properties of the chartarea such as color. See
+ :ref:`chart_formatting_fill`.
+
+* ``pattern``: Set the pattern fill properties of the chartarea. See
+ :ref:`chart_formatting_pattern`.
+
+* ``gradient``: Set the gradient fill properties of the chartarea. See
+ :ref:`chart_formatting_gradient`.
+
+
+
+chart.set_plotarea()
+--------------------
+
+.. py:function:: set_plotarea(options)
+
+ Set the plot area options.
+
+ :param dict options: A dictionary of plot area options.
+
+The ``set_plotarea()`` method is used to set properties of the plot area of a
+chart. In Excel the plot area is the area between the axes on which the chart
+series are plotted::
+
+ chart.set_plotarea({
+ 'border': {'color': 'red', 'width': 2, 'dash_type': 'dash'},
+ 'fill': {'color': '#FFFFC2'}
+ })
+
+.. image:: _images/chart_plotarea.png
+ :scale: 75 %
+
+The properties that can be set are:
+
+* ``border``: Set the border properties of the plotarea such as color and
+ style. See :ref:`chart_formatting_border`.
+
+* ``fill``: Set the solid fill properties of the plotarea such as color. See
+ :ref:`chart_formatting_fill`.
+
+* ``pattern``: Set the pattern fill properties of the plotarea. See
+ :ref:`chart_formatting_pattern`.
+
+* ``gradient``: Set the gradient fill properties of the plotarea. See
+ :ref:`chart_formatting_gradient`.
+
+* ``layout``: Set the ``(x, y)`` position of the plotarea in chart relative
+ units::
+
+ chart.set_plotarea({
+ 'layout': {
+ 'x': 0.13,
+ 'y': 0.26,
+ 'width': 0.73,
+ 'height': 0.57,
+ }
+ })
+
+ See the :ref:`chart_layout` section for more details.
+
+
+chart.set_style()
+-----------------
+
+.. py:function:: set_style(style_id)
+
+ Set the chart style type.
+
+ :param int style_id: An index representing the chart style.
+
+The ``set_style()`` method is used to set the style of the chart to one of the
+48 built-in styles available on the 'Design' tab in Excel::
+
+ chart.set_style(37)
+
+.. image:: _images/chart_style.png
+ :scale: 75 %
+
+The style index number is counted from 1 on the top left. The default style is
+2.
+
+.. Note::
+
+ In Excel 2013 the Styles section of the 'Design' tab in Excel shows what
+ were referred to as 'Layouts' in previous versions of Excel. These layouts
+ are not defined in the file format. They are a collection of modifications
+ to the base chart type. They can be replicated using the XlsxWriter Chart
+ API but they cannot be defined by the ``set_style()`` method.
+
+
+chart.set_table()
+-----------------
+
+.. py:function:: set_table(options)
+
+ Set properties for an axis data table.
+
+ :param dict options: A dictionary of axis table options.
+
+The ``set_table()`` method adds a data table below the horizontal axis with the
+data used to plot the chart::
+
+ chart.set_table()
+
+.. image:: _images/chart_table.png
+ :scale: 75 %
+
+The available options, with default values are::
+
+ 'horizontal': True # Display vertical lines in the table.
+ 'vertical': True # Display horizontal lines in the table.
+ 'outline': True # Display an outline in the table.
+ 'show_keys': False # Show the legend keys with the table data.
+ 'font': {} # Standard chart font properties.
+
+For example::
+
+ chart.set_table({'show_keys': True})
+
+The data table can only be shown with Bar, Column, Line, Area and stock
+charts. See the :ref:`chart_fonts` section for more details on font
+properties.
+
+
+chart.set_up_down_bars()
+------------------------
+
+.. py:function:: set_up_down_bars(options)
+
+ Set properties for the chart up-down bars.
+
+ :param dict options: A dictionary of options.
+
+The ``set_up_down_bars()`` method adds Up-Down bars to Line charts to indicate
+the difference between the first and last data series::
+
+ chart.set_up_down_bars()
+
+It is possible to format the up and down bars to add ``fill``, ``pattern`` or
+``gradient`` and ``border`` properties if required. See
+:ref:`chart_formatting`::
+
+ chart.set_up_down_bars({
+ 'up': {
+ 'fill': {'color': '#00B050'},
+ 'border': {'color': 'black'}
+ },
+ 'down': {
+ 'fill': {'color': 'red'},
+ 'border': {'color': 'black'},
+ },
+ })
+
+.. image:: _images/chart_up_down_bars.png
+ :scale: 75 %
+
+Up-down bars can only be applied to Line charts and to Stock charts (by
+default).
+
+
+chart.set_drop_lines()
+----------------------
+
+.. py:function:: set_drop_lines(options)
+
+ Set properties for the chart drop lines.
+
+ :param dict options: A dictionary of options.
+
+The ``set_drop_lines()`` method adds Drop Lines to charts to show the Category
+value of points in the data::
+
+ chart.set_drop_lines()
+
+.. image:: _images/chart_drop_lines.png
+ :scale: 75 %
+
+It is possible to format the Drop Line ``line`` properties if required. See
+:ref:`chart_formatting`::
+
+ chart.set_drop_lines({'line': {'color': 'red',
+ 'dash_type': 'square_dot'}})
+
+Drop Lines are only available in Line, Area and Stock charts.
+
+
+chart.set_high_low_lines()
+--------------------------
+
+.. py:function:: set_high_low_lines(options)
+
+ Set properties for the chart high-low lines.
+
+ :param dict options: A dictionary of options.
+
+The ``set_high_low_lines()`` method adds High-Low lines to charts to show the
+maximum and minimum values of points in a Category::
+
+ chart.set_high_low_lines()
+
+.. image:: _images/chart_high_low_lines.png
+ :scale: 75 %
+
+It is possible to format the High-Low Line ``line`` properties if required. See
+:ref:`chart_formatting`::
+
+ chart.set_high_low_lines({
+ 'line': {
+ 'color': 'red',
+ 'dash_type': 'square_dot'
+ }
+ })
+
+High-Low Lines are only available in Line and Stock charts.
+
+
+chart.show_blanks_as()
+----------------------
+
+.. py:function:: show_blanks_as(option)
+
+ Set the option for displaying blank data in a chart.
+
+ :param string option: A string representing the display option.
+
+The ``show_blanks_as()`` method controls how blank data is displayed in a
+chart::
+
+ chart.show_blanks_as('span')
+
+The available options are::
+
+ 'gap' # Blank data is shown as a gap. The default.
+ 'zero' # Blank data is displayed as zero.
+ 'span' # Blank data is connected with a line.
+
+
+chart.show_hidden_data()
+------------------------
+
+.. py:function:: show_hidden_data()
+
+ Display data on charts from hidden rows or columns.
+
+
+Display data in hidden rows or columns on the chart::
+
+ chart.show_hidden_data()
+
+
+chart.set_rotation()
+--------------------
+
+.. py:function:: set_rotation(rotation)
+ :noindex:
+
+ Set the Pie/Doughnut chart rotation.
+
+ :param int rotation: The angle of rotation.
+
+The ``set_rotation()`` method is used to set the rotation of the first segment
+of a Pie/Doughnut chart. This has the effect of rotating the entire chart::
+
+ chart->set_rotation(90)
+
+The angle of rotation must be ``0 <= rotation <= 360``.
+
+This option is only available for Pie/Doughnut charts.
+
+
+
+chart.set_hole_size()
+---------------------
+
+.. py:function:: set_hole_size(size)
+
+ Set the Doughnut chart hole size.
+
+ :param int size: The hole size as a percentage.
+
+The ``set_hole_size()`` method is used to set the hole size of a Doughnut
+chart::
+
+ chart->set_hole_size(33)
+
+The angle of hole size must be ``10 <= size <= 90``.
+
+This option is only available for Doughnut charts.
+
+
+See also :ref:`working_with_charts` and :ref:`chart_examples`.
diff --git a/dev/docs/source/chart_examples.rst b/dev/docs/source/chart_examples.rst
new file mode 100644
index 0000000..22d0f96
--- /dev/null
+++ b/dev/docs/source/chart_examples.rst
@@ -0,0 +1,34 @@
+.. _chart_examples:
+
+Chart Examples
+==============
+
+The following are some of the examples included in the
+`examples <https://github.com/jmcnamara/XlsxWriter/tree/master/examples>`_
+directory of the XlsxWriter distribution.
+
+.. toctree::
+ :maxdepth: 1
+
+ example_chart_simple.rst
+ example_chart_area.rst
+ example_chart_bar.rst
+ example_chart_column.rst
+ example_chart_line.rst
+ example_chart_pie.rst
+ example_chart_doughnut.rst
+ example_chart_scatter.rst
+ example_chart_radar.rst
+ example_chart_stock.rst
+ example_chart_styles.rst
+ example_chart_pattern.rst
+ example_chart_gradient.rst
+ example_chart_secondary_axis.rst
+ example_chart_combined.rst
+ example_chart_pareto.rst
+ example_chart_clustered.rst
+ example_chart_date_axis.rst
+ example_chart_data_table.rst
+ example_chart_data_tools.rst
+ example_chartsheet.rst
+
diff --git a/dev/docs/source/chartsheet.rst b/dev/docs/source/chartsheet.rst
new file mode 100644
index 0000000..0df34c2
--- /dev/null
+++ b/dev/docs/source/chartsheet.rst
@@ -0,0 +1,99 @@
+.. _chartsheet:
+
+The Chartsheet Class
+====================
+
+In Excel a chartsheet is a worksheet that only contains a chart.
+
+.. image:: _images/chartsheet.png
+
+The **Chartsheet** class has some of the functionality of data
+:ref:`Worksheets <Worksheet>` such as tab selection, headers, footers, margins
+and print properties but its primary purpose is to display a single chart.
+This makes it different from ordinary data worksheets which can have one or
+more *embedded* charts.
+
+Like a data worksheet a chartsheet object isn't instantiated directly. Instead
+a new chartsheet is created by calling the :func:`add_chartsheet()` method
+from a :ref:`Workbook <Workbook>` object::
+
+ workbook = xlsxwriter.Workbook('filename.xlsx')
+ worksheet = workbook.add_worksheet() # Required for the chart data.
+ chartsheet = workbook.add_chartsheet()
+ #...
+ workbook.close()
+
+
+A chartsheet object functions as a worksheet and not as a chart. In order to
+have it display data a :ref:`Chart <chart_class>` object must be created and
+added to the chartsheet::
+
+ chartsheet = workbook.add_chartsheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ # Configure the chart.
+
+ chartsheet.set_chart(chart)
+
+The data for the chartsheet chart must be contained on a separate worksheet.
+That is why it is always created in conjunction with at least one data
+worksheet, as shown above.
+
+
+chartsheet.set_chart()
+----------------------
+
+.. py:function:: set_chart(chart)
+
+ Add a chart to a chartsheet.
+
+ :param chart: A chart object.
+
+The ``set_chart()`` method is used to insert a chart into a chartsheet. A chart
+object is created via the Workbook :func:`add_chart()` method where the chart
+type is specified::
+
+ chart = workbook.add_chart({type, 'column'})
+
+ chartsheet.set_chart(chart)
+
+Only one chart can be added to an individual chartsheet.
+
+See :ref:`chart_class`, :ref:`working_with_charts` and :ref:`chart_examples`.
+
+
+Worksheet methods
+-----------------
+
+The following :ref:`Worksheet` methods are also available through a chartsheet:
+
+
+* :func:`activate()`
+* :func:`select()`
+* :func:`hide()`
+* :func:`set_first_sheet()`
+* :func:`protect()`
+* :func:`set_zoom()`
+* :func:`set_tab_color()`
+* :func:`set_landscape()`
+* :func:`set_portrait()`
+* :func:`set_paper()`
+* :func:`set_margins()`
+* :func:`set_header()`
+* :func:`set_footer()`
+* :func:`get_name()`
+
+
+For example::
+
+ chartsheet.set_tab_color('#FF9900')
+
+The :func:`set_zoom()` method can be used to modify the displayed size of the
+chart.
+
+
+Chartsheet Example
+------------------
+
+See :ref:`ex_chartsheet`.
+
diff --git a/dev/docs/source/conf.py b/dev/docs/source/conf.py
new file mode 100644
index 0000000..8d0c24b
--- /dev/null
+++ b/dev/docs/source/conf.py
@@ -0,0 +1,306 @@
+# -*- coding: utf-8 -*-
+#
+# XlsxWriter documentation build configuration file, created by
+# sphinx-quickstart on Mon Jan 28 00:12:14 2013.
+#
+# 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
+import 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('.'))
+
+# -- 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.intersphinx']
+
+# 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'XlsxWriter'
+copyright = u'2013-2016, John McNamara'
+
+# 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 short X.Y version.
+version = '0.9.6'
+# The full version, including alpha/beta/rc tags.
+release = version
+
+# 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 = []
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {'http://docs.python.org/2/': None}
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+# html_theme = 'default'
+sys.path.append(os.path.abspath('_themes'))
+html_theme_path = ['_themes']
+html_theme = 'bootstrap'
+
+# 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 = {
+# # 'nosidebar': True,
+# 'sidebarbgcolor': '#F2F2F2',
+# 'relbarbgcolor': '#9CB640',
+# 'linkcolor': '#9CB640',
+# 'sidebarlinkcolor': '#9CB640',
+# 'footerbgcolor': '#FFFFFF',
+# 'footertextcolor': '#9CB640',
+# 'headtextcolor': '#9CB640',
+# 'codebgcolor': '#FFFFFF',
+# }
+
+# 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
+# "<project> v<release> documentation".
+html_title = "XlsxWriter Documentation"
+
+# 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 = '_images/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 <link> 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 = 'XlsxWriterdoc'
+
+# Remove permalinks.
+html_add_permalinks = ""
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+# 'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+'pointsize': '11pt',
+
+# Additional stuff for the LaTeX preamble.
+'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'XlsxWriter.tex', u'Creating Excel files with Python and XlsxWriter',
+ u'John McNamara', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+latex_logo = '_images/logo.png'
+
+# 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
+
+# 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', 'xlsxwriter', u'XlsxWriter Documentation',
+ [u'John McNamara'], 1)
+]
+
+# If true, show URL addresses after external links.
+# man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'XlsxWriter', u'XlsxWriter Documentation',
+ u'John McNamara', 'XlsxWriter', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+# texinfo_appendices = []
+
+# If false, no module index is generated.
+# texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+# texinfo_show_urls = 'footnote'
+
+
+# -- Options for Epub output ---------------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = u'XlsxWriter'
+epub_author = u'John McNamara'
+epub_publisher = u'John McNamara'
+epub_copyright = u'2013-2016, John McNamara'
+
+# The language of the text. It defaults to the language option
+# or en if the language is not set.
+# epub_language = ''
+
+# The scheme of the identifier. Typical schemes are ISBN or URL.
+# epub_scheme = ''
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+# epub_identifier = ''
+
+# A unique identification for the text.
+# epub_uid = ''
+
+# A tuple containing the cover image and cover page html template filenames.
+# epub_cover = ()
+
+# HTML files that should be inserted before the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+# epub_pre_files = []
+
+# HTML files shat should be inserted after the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+# epub_post_files = []
+
+# A list of files that should not be packed into the epub file.
+# epub_exclude_files = []
+
+# The depth of the table of contents in toc.ncx.
+# epub_tocdepth = 3
+
+# Allow duplicate toc entries.
+# epub_tocdup = True
+
diff --git a/dev/docs/source/contents.rst b/dev/docs/source/contents.rst
new file mode 100644
index 0000000..8960dd2
--- /dev/null
+++ b/dev/docs/source/contents.rst
@@ -0,0 +1,42 @@
+Contents
+========
+
+.. toctree::
+ :maxdepth: 2
+
+ introduction.rst
+ getting_started.rst
+ tutorial01.rst
+ tutorial02.rst
+ tutorial03.rst
+ workbook.rst
+ worksheet.rst
+ page_setup.rst
+ format.rst
+ chart.rst
+ chartsheet.rst
+ working_with_cell_notation.rst
+ working_with_formulas.rst
+ working_with_dates_and_time.rst
+ working_with_colors.rst
+ working_with_charts.rst
+ working_with_autofilters.rst
+ working_with_data_validation.rst
+ working_with_conditional_formats.rst
+ working_with_tables.rst
+ working_with_textboxes.rst
+ working_with_sparklines.rst
+ working_with_cell_comments.rst
+ working_with_outlines.rst
+ working_with_memory.rst
+ working_with_macros.rst
+ working_with_pandas.rst
+ examples.rst
+ chart_examples.rst
+ pandas_examples.rst
+ alternatives.rst
+ bugs.rst
+ faq.rst
+ changes.rst
+ author.rst
+ license.rst
diff --git a/dev/docs/source/example_array_formula.rst b/dev/docs/source/example_array_formula.rst
new file mode 100644
index 0000000..d421ed3
--- /dev/null
+++ b/dev/docs/source/example_array_formula.rst
@@ -0,0 +1,13 @@
+.. _ex_array_formula:
+
+Example: Array formulas
+=======================
+
+This program is an example of writing array formulas with one or more return
+values. See the :func:`write_array_formula` method for more details.
+
+.. image:: _images/array_formula.png
+
+
+.. literalinclude:: ../../../examples/array_formula.py
+
diff --git a/dev/docs/source/example_autofilter.rst b/dev/docs/source/example_autofilter.rst
new file mode 100644
index 0000000..5e0a474
--- /dev/null
+++ b/dev/docs/source/example_autofilter.rst
@@ -0,0 +1,12 @@
+.. _ex_autofilter:
+
+Example: Applying Autofilters
+=============================
+
+This program is an example of using autofilters in a worksheet. See
+:ref:`working_with_autofilters` for more details.
+
+.. image:: _images/autofilter3.png
+
+.. literalinclude:: ../../../examples/autofilter.py
+
diff --git a/dev/docs/source/example_chart_area.rst b/dev/docs/source/example_chart_area.rst
new file mode 100644
index 0000000..5231563
--- /dev/null
+++ b/dev/docs/source/example_chart_area.rst
@@ -0,0 +1,23 @@
+.. _ex_chart_area:
+
+Example: Area Chart
+===================
+
+Example of creating Excel Area charts.
+
+Chart 1 in the following example is a default area chart:
+
+.. image:: _images/chart_area1.png
+ :scale: 75 %
+
+Chart 2 is a stacked area chart:
+
+.. image:: _images/chart_area2.png
+ :scale: 75 %
+
+Chart 3 is a percentage stacked area chart:
+
+.. image:: _images/chart_area3.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_area.py
diff --git a/dev/docs/source/example_chart_bar.rst b/dev/docs/source/example_chart_bar.rst
new file mode 100644
index 0000000..83ebf91
--- /dev/null
+++ b/dev/docs/source/example_chart_bar.rst
@@ -0,0 +1,23 @@
+.. _ex_chart_bar:
+
+Example: Bar Chart
+==================
+
+Example of creating Excel Bar charts.
+
+Chart 1 in the following example is a default bar chart:
+
+.. image:: _images/chart_bar1.png
+ :scale: 75 %
+
+Chart 2 is a stacked bar chart:
+
+.. image:: _images/chart_bar2.png
+ :scale: 75 %
+
+Chart 3 is a percentage stacked bar chart:
+
+.. image:: _images/chart_bar3.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_bar.py
diff --git a/dev/docs/source/example_chart_clustered.rst b/dev/docs/source/example_chart_clustered.rst
new file mode 100644
index 0000000..3956339
--- /dev/null
+++ b/dev/docs/source/example_chart_clustered.rst
@@ -0,0 +1,16 @@
+.. _ex_chart_clustered:
+
+Example: Clustered Chart
+========================
+
+Example of creating a clustered Excel chart where there are two levels of
+category on the X axis.
+
+.. image:: _images/chart_clustered.png
+ :scale: 75 %
+
+The categories in clustered charts are 2D ranges, instead of the more normal
+1D ranges. The series are shown as formula strings for clarity but you can also
+use the a list syntax.
+
+.. literalinclude:: ../../../examples/chart_clustered.py
diff --git a/dev/docs/source/example_chart_column.rst b/dev/docs/source/example_chart_column.rst
new file mode 100644
index 0000000..3b070e3
--- /dev/null
+++ b/dev/docs/source/example_chart_column.rst
@@ -0,0 +1,23 @@
+.. _ex_chart_column:
+
+Example: Column Chart
+=====================
+
+Example of creating Excel Column charts.
+
+Chart 1 in the following example is a default column chart:
+
+.. image:: _images/chart_column1.png
+ :scale: 75 %
+
+Chart 2 is a stacked column chart:
+
+.. image:: _images/chart_column2.png
+ :scale: 75 %
+
+Chart 3 is a percentage stacked column chart:
+
+.. image:: _images/chart_column3.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_column.py
diff --git a/dev/docs/source/example_chart_combined.rst b/dev/docs/source/example_chart_combined.rst
new file mode 100644
index 0000000..a5ca77e
--- /dev/null
+++ b/dev/docs/source/example_chart_combined.rst
@@ -0,0 +1,20 @@
+.. _ex_chart_combined:
+
+Example: Combined Chart
+=======================
+
+Example of creating combined Excel charts with two chart types.
+
+In the first example we create a combined column and line chart that share the
+same X and Y axes.
+
+.. image:: _images/chart_combined1.png
+ :scale: 75 %
+
+In the second example we create a similar combined column and line chart
+except that the secondary chart has a secondary Y axis.
+
+.. image:: _images/chart_combined2.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_combined.py
diff --git a/dev/docs/source/example_chart_data_table.rst b/dev/docs/source/example_chart_data_table.rst
new file mode 100644
index 0000000..2298ab4
--- /dev/null
+++ b/dev/docs/source/example_chart_data_table.rst
@@ -0,0 +1,18 @@
+.. _ex_chart_data_table:
+
+Example: Charts with Data Tables
+================================
+
+Example of creating charts with data tables.
+
+Chart 1 in the following example is a column chart with default data table:
+
+.. image:: _images/chart_data_table1.png
+ :scale: 75 %
+
+Chart 2 is a column chart with default data table with legend keys:
+
+.. image:: _images/chart_data_table2.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_data_table.py
diff --git a/dev/docs/source/example_chart_data_tools.rst b/dev/docs/source/example_chart_data_tools.rst
new file mode 100644
index 0000000..2aefd0c
--- /dev/null
+++ b/dev/docs/source/example_chart_data_tools.rst
@@ -0,0 +1,40 @@
+.. _ex_chart_data_tools:
+
+Example: Charts with Data Tools
+===============================
+
+A demo of an various Excel chart data tools that are available via an
+XlsxWriter chart. These include, Trendlines, Data Labels, Error Bars, Drop
+Lines, High-Low Lines and Up-Down Bars.
+
+Chart 1 in the following example is a chart with trendlines:
+
+.. image:: _images/chart_data_tools1.png
+ :scale: 75 %
+
+Chart 2 is a chart with data labels and markers:
+
+.. image:: _images/chart_data_tools2.png
+ :scale: 75 %
+
+Chart 3 is a chart with error bars:
+
+.. image:: _images/chart_data_tools3.png
+ :scale: 75 %
+
+Chart 4 is a chart with up-down bars:
+
+.. image:: _images/chart_data_tools4.png
+ :scale: 75 %
+
+Chart 5 is a chart with hi-low lines:
+
+.. image:: _images/chart_data_tools5.png
+ :scale: 75 %
+
+Chart 6 is a chart with drop lines:
+
+.. image:: _images/chart_data_tools6.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_data_tools.py
diff --git a/dev/docs/source/example_chart_date_axis.rst b/dev/docs/source/example_chart_date_axis.rst
new file mode 100644
index 0000000..bd2fad9
--- /dev/null
+++ b/dev/docs/source/example_chart_date_axis.rst
@@ -0,0 +1,22 @@
+.. _ex_chart_date_axis:
+
+Example: Date Axis Chart
+========================
+
+Date Category Axes are a special case of Category axes in Excel which give them
+some of the properties of Values axes.
+
+For example, Excel doesn't normally allow minimum and maximum values to be set
+for category axes. However, date axes are an exception.
+
+.. image:: _images/chart_date_axis.png
+ :scale: 75 %
+
+In XlsxWriter Date Category Axes are set using the ``date_axis`` option in
+:func:`set_x_axis` or :func:`set_y_axis`::
+
+ chart.set_x_axis({'date_axis': True})
+
+If used, the ``min`` and ``max`` values should be set as Excel times or dates.
+
+.. literalinclude:: ../../../examples/chart_date_axis.py
diff --git a/dev/docs/source/example_chart_doughnut.rst b/dev/docs/source/example_chart_doughnut.rst
new file mode 100644
index 0000000..cb64b78
--- /dev/null
+++ b/dev/docs/source/example_chart_doughnut.rst
@@ -0,0 +1,21 @@
+.. _ex_chart_doughnut:
+
+Example: Doughnut Chart
+=======================
+
+Example of creating Excel Doughnut charts. Chart 1 in the following example is:
+
+.. image:: _images/chart_doughnut1.png
+ :scale: 75 %
+
+Chart 4 shows how to set segment colors and other options.
+
+It is possible to define chart colors for most types of XlsxWriter charts via
+the :func:`add_series()` method. However, Pie/Doughnut charts are a special
+case since each segment is represented as a point and as such it is necessary
+to assign formatting to each point in the series.
+
+.. image:: _images/chart_doughnut2.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_doughnut.py
diff --git a/dev/docs/source/example_chart_gradient.rst b/dev/docs/source/example_chart_gradient.rst
new file mode 100644
index 0000000..9b9dd89
--- /dev/null
+++ b/dev/docs/source/example_chart_gradient.rst
@@ -0,0 +1,12 @@
+.. _ex_chart_gradient:
+
+Example: Chart with Gradient Fills
+==================================
+
+Example of creating an Excel chart with gradient fills, in the columns and
+in the plot area.
+
+.. image:: _images/chart_gradient.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_gradient.py
diff --git a/dev/docs/source/example_chart_line.rst b/dev/docs/source/example_chart_line.rst
new file mode 100644
index 0000000..41c5b5a
--- /dev/null
+++ b/dev/docs/source/example_chart_line.rst
@@ -0,0 +1,15 @@
+.. _ex_chart_line:
+
+Example: Line Chart
+===================
+
+Example of creating an Excel line chart. The X axis of a line chart is a
+category axis with fixed point spacing. For a line chart with arbitrary point
+spacing see the Scatter chart type.
+
+Chart 1 in the following example is:
+
+.. image:: _images/chart_line1.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_line.py
diff --git a/dev/docs/source/example_chart_pareto.rst b/dev/docs/source/example_chart_pareto.rst
new file mode 100644
index 0000000..b9f798f
--- /dev/null
+++ b/dev/docs/source/example_chart_pareto.rst
@@ -0,0 +1,11 @@
+.. _ex_chart_pareto:
+
+Example: Pareto Chart
+=====================
+
+Example of creating a Pareto chart with a secondary chart and axis.
+
+.. image:: _images/chart_pareto.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_pareto.py
diff --git a/dev/docs/source/example_chart_pattern.rst b/dev/docs/source/example_chart_pattern.rst
new file mode 100644
index 0000000..496614e
--- /dev/null
+++ b/dev/docs/source/example_chart_pattern.rst
@@ -0,0 +1,11 @@
+.. _ex_chart_pattern:
+
+Example: Chart with Pattern Fills
+=================================
+
+Example of creating an Excel chart with pattern fills, in the columns.
+
+.. image:: _images/chart_pattern.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_pattern.py
diff --git a/dev/docs/source/example_chart_pie.rst b/dev/docs/source/example_chart_pie.rst
new file mode 100644
index 0000000..9040b83
--- /dev/null
+++ b/dev/docs/source/example_chart_pie.rst
@@ -0,0 +1,21 @@
+.. _ex_chart_pie:
+
+Example: Pie Chart
+==================
+
+Example of creating Excel Pie charts. Chart 1 in the following example is:
+
+.. image:: _images/chart_pie1.png
+ :scale: 75 %
+
+Chart 2 shows how to set segment colors.
+
+It is possible to define chart colors for most types of XlsxWriter charts via
+the :func:`add_series()` method. However, Pie charts are a special case since
+each segment is represented as a point and as such it is necessary to assign
+formatting to each point in the series.
+
+.. image:: _images/chart_pie2.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_pie.py
diff --git a/dev/docs/source/example_chart_radar.rst b/dev/docs/source/example_chart_radar.rst
new file mode 100644
index 0000000..5532916
--- /dev/null
+++ b/dev/docs/source/example_chart_radar.rst
@@ -0,0 +1,24 @@
+.. _ex_chart_radar:
+
+Example: Radar Chart
+====================
+
+Example of creating Excel Column charts.
+
+Chart 1 in the following example is a default radar chart:
+
+.. image:: _images/chart_radar1.png
+ :scale: 75 %
+
+Chart 2 in the following example is a radar chart with markers:
+
+.. image:: _images/chart_radar2.png
+ :scale: 75 %
+
+Chart 3 in the following example is a filled radar chart:
+
+.. image:: _images/chart_radar3.png
+ :scale: 75 %
+
+
+.. literalinclude:: ../../../examples/chart_radar.py
diff --git a/dev/docs/source/example_chart_scatter.rst b/dev/docs/source/example_chart_scatter.rst
new file mode 100644
index 0000000..6b69829
--- /dev/null
+++ b/dev/docs/source/example_chart_scatter.rst
@@ -0,0 +1,34 @@
+.. _ex_chart_scatter:
+
+Example: Scatter Chart
+======================
+
+Example of creating Excel Scatter charts.
+
+Chart 1 in the following example is a default scatter chart:
+
+.. image:: _images/chart_scatter1.png
+ :scale: 75 %
+
+Chart 2 is a scatter chart with straight lines and markers:
+
+.. image:: _images/chart_scatter2.png
+ :scale: 75 %
+
+Chart 3 is a scatter chart with straight lines and no markers:
+
+.. image:: _images/chart_scatter3.png
+ :scale: 75 %
+
+Chart 4 is a scatter chart with smooth lines and markers:
+
+.. image:: _images/chart_scatter4.png
+ :scale: 75 %
+
+Chart 5 is a scatter chart with smooth lines and no markers:
+
+.. image:: _images/chart_scatter5.png
+ :scale: 75 %
+
+
+.. literalinclude:: ../../../examples/chart_scatter.py
diff --git a/dev/docs/source/example_chart_secondary_axis.rst b/dev/docs/source/example_chart_secondary_axis.rst
new file mode 100644
index 0000000..a744932
--- /dev/null
+++ b/dev/docs/source/example_chart_secondary_axis.rst
@@ -0,0 +1,13 @@
+.. _ex_chart_secondary_axis:
+
+Example: Secondary Axis Chart
+=============================
+
+Example of creating an Excel Line chart with a secondary axis. Note, the
+primary and secondary chart type are the same. The next example shows a
+secondary chart of a different type.
+
+.. image:: _images/chart_secondary_axis1.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_secondary_axis.py
diff --git a/dev/docs/source/example_chart_simple.rst b/dev/docs/source/example_chart_simple.rst
new file mode 100644
index 0000000..a48ca8d
--- /dev/null
+++ b/dev/docs/source/example_chart_simple.rst
@@ -0,0 +1,14 @@
+.. _ex_chart_simple:
+
+Example: Chart (Simple)
+=======================
+
+Example of a simple column chart with 3 data series:
+
+.. image:: _images/chart_simple.png
+ :scale: 75 %
+
+
+See the :ref:`chart_class` and :ref:`working_with_charts` for more details.
+
+.. literalinclude:: ../../../examples/chart.py
diff --git a/dev/docs/source/example_chart_stock.rst b/dev/docs/source/example_chart_stock.rst
new file mode 100644
index 0000000..9e121d5
--- /dev/null
+++ b/dev/docs/source/example_chart_stock.rst
@@ -0,0 +1,13 @@
+.. _ex_chart_stock:
+
+Example: Stock Chart
+====================
+
+Example of creating and Excel HiLow-Close Stock chart.
+
+Chart 1 in the following example is:
+
+.. image:: _images/chart_stock1.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/chart_stock.py
diff --git a/dev/docs/source/example_chart_styles.rst b/dev/docs/source/example_chart_styles.rst
new file mode 100644
index 0000000..96e4c70
--- /dev/null
+++ b/dev/docs/source/example_chart_styles.rst
@@ -0,0 +1,13 @@
+.. _ex_chart_styles:
+
+Example: Styles Chart
+=====================
+
+An example showing all 48 default chart styles available in Excel 2007 using
+the chart :func:`set_style()` method.
+
+.. image:: _images/chart_styles.png
+
+Note, these styles are not the same as the styles available in Excel 2013.
+
+.. literalinclude:: ../../../examples/chart_styles.py
diff --git a/dev/docs/source/example_chartsheet.rst b/dev/docs/source/example_chartsheet.rst
new file mode 100644
index 0000000..95ac67c
--- /dev/null
+++ b/dev/docs/source/example_chartsheet.rst
@@ -0,0 +1,10 @@
+.. _ex_chartsheet:
+
+Example: Chartsheet
+===================
+
+Example of creating an Excel Bar chart on a :ref:`chartsheet <Chartsheet>`.
+
+.. image:: _images/chartsheet.png
+
+.. literalinclude:: ../../../examples/chartsheet.py
diff --git a/dev/docs/source/example_comments1.rst b/dev/docs/source/example_comments1.rst
new file mode 100644
index 0000000..309c335
--- /dev/null
+++ b/dev/docs/source/example_comments1.rst
@@ -0,0 +1,11 @@
+.. _ex_comments1:
+
+Example: Adding Cell Comments to Worksheets (Simple)
+====================================================
+
+A simple example of adding cell comments to a worksheet. For more details see
+:ref:`cell_comments`.
+
+.. image:: _images/comments1.png
+
+.. literalinclude:: ../../../examples/comments1.py
diff --git a/dev/docs/source/example_comments2.rst b/dev/docs/source/example_comments2.rst
new file mode 100644
index 0000000..66fe12f
--- /dev/null
+++ b/dev/docs/source/example_comments2.rst
@@ -0,0 +1,12 @@
+.. _ex_comments2:
+
+Example: Adding Cell Comments to Worksheets (Advanced)
+======================================================
+
+Another example of adding cell comments to a worksheet. This example
+demonstrates most of the available comment formatting options. For more
+details see :ref:`cell_comments`.
+
+.. image:: _images/comments2.png
+
+.. literalinclude:: ../../../examples/comments2.py
diff --git a/dev/docs/source/example_conditional_format.rst b/dev/docs/source/example_conditional_format.rst
new file mode 100644
index 0000000..941d3b8
--- /dev/null
+++ b/dev/docs/source/example_conditional_format.rst
@@ -0,0 +1,12 @@
+.. _ex_cond_format:
+
+Example: Conditional Formatting
+===============================
+
+Example of how to add conditional formatting to an XlsxWriter file.
+Conditional formatting allows you to apply a format to a cell or a
+range of cells based on certain criteria.
+
+.. image:: _images/conditional_format1.png
+
+.. literalinclude:: ../../../examples/conditional_format.py
diff --git a/dev/docs/source/example_data_validate.rst b/dev/docs/source/example_data_validate.rst
new file mode 100644
index 0000000..ca1b5f1
--- /dev/null
+++ b/dev/docs/source/example_data_validate.rst
@@ -0,0 +1,12 @@
+.. _ex_data_valid:
+
+Example: Data Validation and Drop Down Lists
+============================================
+
+Example of how to add data validation and drop down lists to an XlsxWriter
+file. Data validation is a way of limiting user input to certain ranges
+or to allow a selection from a drop down list.
+
+.. image:: _images/data_validate1.png
+
+.. literalinclude:: ../../../examples/data_validate.py
diff --git a/dev/docs/source/example_datetimes.rst b/dev/docs/source/example_datetimes.rst
new file mode 100644
index 0000000..d60af10
--- /dev/null
+++ b/dev/docs/source/example_datetimes.rst
@@ -0,0 +1,13 @@
+.. _ex_datetimes:
+
+Example: Dates and Times in Excel
+=================================
+
+This program is an example of writing some of the features of the
+XlsxWriter module. See the :ref:`working_with_dates_and_time` section for
+more details on this example.
+
+.. image:: _images/working_with_dates_and_times02.png
+
+.. literalinclude:: ../../../examples/datetimes.py
+
diff --git a/dev/docs/source/example_defined_name.rst b/dev/docs/source/example_defined_name.rst
new file mode 100644
index 0000000..d0e9cb4
--- /dev/null
+++ b/dev/docs/source/example_defined_name.rst
@@ -0,0 +1,14 @@
+.. _ex_defined_name:
+
+Example: Defined names/Named ranges
+===================================
+
+Example of how to create defined names (named ranges) with XlsxWriter.
+
+Defined names are used to define descriptive names to represent a value, a
+single cell or a range of cells in a workbook or worksheet. See
+:func:`define_name`.
+
+.. image:: _images/defined_name.png
+
+.. literalinclude:: ../../../examples/defined_name.py
diff --git a/dev/docs/source/example_demo.rst b/dev/docs/source/example_demo.rst
new file mode 100644
index 0000000..6873eb9
--- /dev/null
+++ b/dev/docs/source/example_demo.rst
@@ -0,0 +1,19 @@
+.. _ex_demo:
+
+Example: Simple Feature Demonstration
+=====================================
+
+This program is an example of writing some of the features of the XlsxWriter
+module.
+
+.. image:: _images/demo.png
+
+.. literalinclude:: ../../../examples/demo.py
+
+Notes:
+
+* This example includes the use of cell formatting via the :ref:`format`.
+* Strings and numbers can be written with the same worksheet :func:`write`
+ method.
+* Data can be written to cells using Row-Column notation or 'A1' style
+ notation, see :ref:`cell_notation`.
diff --git a/dev/docs/source/example_diagonal_border.rst b/dev/docs/source/example_diagonal_border.rst
new file mode 100644
index 0000000..be2bead
--- /dev/null
+++ b/dev/docs/source/example_diagonal_border.rst
@@ -0,0 +1,15 @@
+.. _ex_diagonal_border:
+
+Example: Diagonal borders in cells
+==================================
+
+Example of how to set diagonal borders in a cell.
+
+.. image:: _images/diagonal_border.png
+
+
+See :func:`set_diag_border`, :func:`set_diag_type` and :func:`set_diag_border`
+for details.
+
+
+.. literalinclude:: ../../../examples/diagonal_border.py
diff --git a/dev/docs/source/example_doc_properties.rst b/dev/docs/source/example_doc_properties.rst
new file mode 100644
index 0000000..361baba
--- /dev/null
+++ b/dev/docs/source/example_doc_properties.rst
@@ -0,0 +1,12 @@
+.. _ex_doc_properties:
+
+Example: Setting Document Properties
+====================================
+
+This program is an example setting document properties. See the
+:func:`set_properties` workbook method for more details.
+
+.. image:: _images/doc_properties.png
+
+.. literalinclude:: ../../../examples/doc_properties.py
+
diff --git a/dev/docs/source/example_headers_footers.rst b/dev/docs/source/example_headers_footers.rst
new file mode 100644
index 0000000..1a4e800
--- /dev/null
+++ b/dev/docs/source/example_headers_footers.rst
@@ -0,0 +1,12 @@
+.. _ex_headers_footers:
+
+Example: Adding Headers and Footers to Worksheets
+=================================================
+
+This program is an example of adding headers and footers to worksheets.
+See the :func:`set_header` and :func:`set_footer` methods for more details.
+
+.. image:: _images/header_image.png
+
+.. literalinclude:: ../../../examples/headers_footers.py
+
diff --git a/dev/docs/source/example_hello_world.rst b/dev/docs/source/example_hello_world.rst
new file mode 100644
index 0000000..8611e18
--- /dev/null
+++ b/dev/docs/source/example_hello_world.rst
@@ -0,0 +1,11 @@
+.. _ex_hello_world:
+
+Example: Hello World
+====================
+
+The simplest possible spreadsheet. This is a good place to start to see if
+the XlsxWriter module is installed correctly.
+
+.. image:: _images/hello01.png
+
+.. literalinclude:: ../../../examples/hello_world.py
diff --git a/dev/docs/source/example_hide_row_col.rst b/dev/docs/source/example_hide_row_col.rst
new file mode 100644
index 0000000..1956dd1
--- /dev/null
+++ b/dev/docs/source/example_hide_row_col.rst
@@ -0,0 +1,23 @@
+.. _ex_hide_row_col:
+
+Example: Hiding Rows and Columns
+================================
+
+This program is an example of how to hide rows and columns in XlsxWriter.
+
+An individual row can be hidden using the :func:`set_row` method::
+
+ worksheet.set_row(0, None, None, {'hidden': True})
+
+However, in order to hide a large number of rows, for example all the rows
+after row 8, we need to use an Excel optimization to hide rows without setting
+each one, (of approximately 1 million rows). To do this we use the
+:func:`set_default_row` method.
+
+Columns don't require this optimization and can be hidden using
+:func:`set_column`.
+
+.. image:: _images/hide_row_col.png
+
+.. literalinclude:: ../../../examples/hide_row_col.py
+
diff --git a/dev/docs/source/example_hide_sheet.rst b/dev/docs/source/example_hide_sheet.rst
new file mode 100644
index 0000000..d20afac
--- /dev/null
+++ b/dev/docs/source/example_hide_sheet.rst
@@ -0,0 +1,12 @@
+.. _ex_hide_sheet:
+
+Example: Hiding Worksheets
+==========================
+
+This program is an example of how to hide a worksheet using the
+:func:`hide` method.
+
+.. image:: _images/hide_sheet.png
+
+.. literalinclude:: ../../../examples/hide_sheet.py
+
diff --git a/dev/docs/source/example_http_server.rst b/dev/docs/source/example_http_server.rst
new file mode 100644
index 0000000..6f7030e
--- /dev/null
+++ b/dev/docs/source/example_http_server.rst
@@ -0,0 +1,19 @@
+.. _ex_http_server:
+
+Example: Simple HTTP Server (Python 2)
+======================================
+
+Example of using Python and XlsxWriter to create an Excel XLSX file in an in
+memory string suitable for serving via SimpleHTTPServer or Django or with the
+Google App Engine.
+
+Even though the final file will be in memory, via the StringIO object, the
+module uses temp files during assembly for efficiency. To avoid this on
+servers that don't allow temp files, for example the Google APP Engine, set
+the ``in_memory`` constructor option to ``True``.
+
+For a Python 3 example see :ref:`ex_http_server3`.
+
+
+.. literalinclude:: ../../../examples/http_server_py2.py
+
diff --git a/dev/docs/source/example_http_server3.rst b/dev/docs/source/example_http_server3.rst
new file mode 100644
index 0000000..dd1f968
--- /dev/null
+++ b/dev/docs/source/example_http_server3.rst
@@ -0,0 +1,18 @@
+.. _ex_http_server3:
+
+Example: Simple HTTP Server (Python 3)
+======================================
+
+Example of using Python and XlsxWriter to create an Excel XLSX file in an in
+memory string suitable for serving via SimpleHTTPRequestHandler or Django or with the
+Google App Engine.
+
+Even though the final file will be in memory, via the BytesIO object, the
+module uses temp files during assembly for efficiency. To avoid this on
+servers that don't allow temp files, for example the Google APP Engine, set
+the ``in_memory`` constructor option to ``True``.
+
+For a Python 2 example see :ref:`ex_http_server`.
+
+.. literalinclude:: ../../../examples/http_server_py3.py
+
diff --git a/dev/docs/source/example_hyperlink.rst b/dev/docs/source/example_hyperlink.rst
new file mode 100644
index 0000000..0e6036e
--- /dev/null
+++ b/dev/docs/source/example_hyperlink.rst
@@ -0,0 +1,12 @@
+.. _ex_hyperlink:
+
+Example: Adding hyperlinks
+==========================
+
+This program is an example of writing hyperlinks to a worksheet. See the
+:func:`write_url` method for more details.
+
+.. image:: _images/hyperlink.png
+
+.. literalinclude:: ../../../examples/hyperlink.py
+
diff --git a/dev/docs/source/example_images.rst b/dev/docs/source/example_images.rst
new file mode 100644
index 0000000..8cbf38a
--- /dev/null
+++ b/dev/docs/source/example_images.rst
@@ -0,0 +1,12 @@
+.. _ex_insert_image:
+
+Example: Inserting images into a worksheet
+==========================================
+
+This program is an example of inserting images into a worksheet. See the
+:func:`insert_image` method for more details.
+
+.. image:: _images/images.png
+
+.. literalinclude:: ../../../examples/images.py
+
diff --git a/dev/docs/source/example_images_bytesio.rst b/dev/docs/source/example_images_bytesio.rst
new file mode 100644
index 0000000..dbf5608
--- /dev/null
+++ b/dev/docs/source/example_images_bytesio.rst
@@ -0,0 +1,16 @@
+.. _ex_images_bytesio:
+
+Example: Inserting images from a URL or byte stream into a worksheet
+====================================================================
+
+This program is an example of inserting images from a Python
+:class:`io.BytesIO` byte stream into a worksheet.
+
+The example byte streams are populated from a URL and from a local file.
+
+See the :func:`insert_image` method for more details.
+
+.. image:: _images/images_bytesio.png
+
+.. literalinclude:: ../../../examples/images_bytesio.py
+
diff --git a/dev/docs/source/example_macros.rst b/dev/docs/source/example_macros.rst
new file mode 100644
index 0000000..dacfccc
--- /dev/null
+++ b/dev/docs/source/example_macros.rst
@@ -0,0 +1,14 @@
+.. _ex_macros:
+
+Example: Adding a VBA macro to a Workbook
+=========================================
+
+This program is an example of how to add a button connected to a VBA macro to
+a worksheet.
+
+See :ref:`macros` for more details.
+
+
+.. image:: _images/macros.png
+
+.. literalinclude:: ../../../examples/macros.py
diff --git a/dev/docs/source/example_merge1.rst b/dev/docs/source/example_merge1.rst
new file mode 100644
index 0000000..a215e45
--- /dev/null
+++ b/dev/docs/source/example_merge1.rst
@@ -0,0 +1,12 @@
+.. _ex_merge1:
+
+Example: Merging Cells
+======================
+
+This program is an example of merging cells in a worksheet. See the
+:func:`merge_range` method for more details.
+
+.. image:: _images/merge1.png
+
+.. literalinclude:: ../../../examples/merge1.py
+
diff --git a/dev/docs/source/example_merge_rich.rst b/dev/docs/source/example_merge_rich.rst
new file mode 100644
index 0000000..b4ae090
--- /dev/null
+++ b/dev/docs/source/example_merge_rich.rst
@@ -0,0 +1,21 @@
+.. _ex_merge_rich:
+
+Example: Merging Cells with a Rich String
+=========================================
+
+This program is an example of merging cells that contain a rich string.
+
+Using the standard XlsxWriter API we can only write simple types to merged
+ranges so we first write a blank string to the merged range. We then overwrite
+the first merged cell with a rich string.
+
+Note that we must also pass the cell format used in the merged cells format at
+the end
+
+See the :func:`merge_range` and :func:`write_rich_string` methods for more
+details.
+
+.. image:: _images/merge_rich.png
+
+.. literalinclude:: ../../../examples/merge_rich_string.py
+
diff --git a/dev/docs/source/example_outline1.rst b/dev/docs/source/example_outline1.rst
new file mode 100644
index 0000000..c5caaf9
--- /dev/null
+++ b/dev/docs/source/example_outline1.rst
@@ -0,0 +1,11 @@
+.. _ex_outline1:
+
+Example: Outline and Grouping
+=============================
+
+Examples of how use XlsxWriter to generate Excel outlines and grouping. See
+also :ref:`outlines`.
+
+.. image:: _images/outline1.png
+
+.. literalinclude:: ../../../examples/outline.py
diff --git a/dev/docs/source/example_outline2.rst b/dev/docs/source/example_outline2.rst
new file mode 100644
index 0000000..1878617
--- /dev/null
+++ b/dev/docs/source/example_outline2.rst
@@ -0,0 +1,12 @@
+.. _ex_outline2:
+
+Example: Collapsed Outline and Grouping
+=======================================
+
+Examples of how use XlsxWriter to generate Excel outlines and
+grouping. These examples focus mainly on collapsed outlines. See also
+:ref:`outlines`.
+
+.. image:: _images/outline2.png
+
+.. literalinclude:: ../../../examples/outline_collapsed.py
diff --git a/dev/docs/source/example_pandas_chart.rst b/dev/docs/source/example_pandas_chart.rst
new file mode 100644
index 0000000..8302e1b
--- /dev/null
+++ b/dev/docs/source/example_pandas_chart.rst
@@ -0,0 +1,11 @@
+.. _ex_pandas_chart:
+
+Example: Pandas Excel output with a chart
+=========================================
+
+A simple example of converting a Pandas dataframe to an Excel file with a
+chart using Pandas and XlsxWriter.
+
+.. image:: _images/pandas_chart.png
+
+.. literalinclude:: ../../../examples/pandas_chart.py
diff --git a/dev/docs/source/example_pandas_chart_columns.rst b/dev/docs/source/example_pandas_chart_columns.rst
new file mode 100644
index 0000000..69f0ec0
--- /dev/null
+++ b/dev/docs/source/example_pandas_chart_columns.rst
@@ -0,0 +1,12 @@
+.. _ex_pandas_chart_columns:
+
+Example: Pandas Excel output with a column chart
+================================================
+
+An example of converting a Pandas dataframe to an Excel file with a column
+chart using Pandas and XlsxWriter.
+
+.. image:: _images/pandas_chart_columns.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/pandas_chart_columns.py
diff --git a/dev/docs/source/example_pandas_chart_line.rst b/dev/docs/source/example_pandas_chart_line.rst
new file mode 100644
index 0000000..fa336e0
--- /dev/null
+++ b/dev/docs/source/example_pandas_chart_line.rst
@@ -0,0 +1,12 @@
+.. _ex_pandas_chart_line:
+
+Example: Pandas Excel output with a line chart
+==============================================
+
+A simple example of converting a Pandas dataframe to an Excel file with a
+line chart using Pandas and XlsxWriter.
+
+.. image:: _images/pandas_chart_line.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/pandas_chart_line.py
diff --git a/dev/docs/source/example_pandas_chart_stock.rst b/dev/docs/source/example_pandas_chart_stock.rst
new file mode 100644
index 0000000..48af83f
--- /dev/null
+++ b/dev/docs/source/example_pandas_chart_stock.rst
@@ -0,0 +1,16 @@
+.. _ex_pandas_chart_stock:
+
+Example: Pandas Excel output with a stock chart
+===============================================
+
+An example of converting a Pandas dataframe with stock data taken from the web
+to an Excel file with a line chart using Pandas and XlsxWriter.
+
+Note: occasionally the Yahoo source for the data used in the chart is down or
+under maintenance. If there are any issues running this program check the
+source data first.
+
+.. image:: _images/pandas_chart_stock.png
+ :scale: 75 %
+
+.. literalinclude:: ../../../examples/pandas_chart_stock.py
diff --git a/dev/docs/source/example_pandas_column_formats.rst b/dev/docs/source/example_pandas_column_formats.rst
new file mode 100644
index 0000000..de41a00
--- /dev/null
+++ b/dev/docs/source/example_pandas_column_formats.rst
@@ -0,0 +1,16 @@
+.. _ex_pandas_column_formats:
+
+Example: Pandas Excel output with column formatting
+===================================================
+
+An example of converting a Pandas dataframe to an Excel file with column
+formats using Pandas and XlsxWriter.
+
+It isn't possible to format any cells that already have a format such as
+the index or headers or any cells that contain dates or datetimes.
+
+Note: This feature requires Pandas >= 0.16.
+
+.. image:: _images/pandas_column_formats.png
+
+.. literalinclude:: ../../../examples/pandas_column_formats.py
diff --git a/dev/docs/source/example_pandas_conditional.rst b/dev/docs/source/example_pandas_conditional.rst
new file mode 100644
index 0000000..cb1c86d
--- /dev/null
+++ b/dev/docs/source/example_pandas_conditional.rst
@@ -0,0 +1,11 @@
+.. _ex_pandas_conditional:
+
+Example: Pandas Excel output with conditional formatting
+========================================================
+
+An example of converting a Pandas dataframe to an Excel file with a
+conditional formatting using Pandas and XlsxWriter.
+
+.. image:: _images/pandas_conditional.png
+
+.. literalinclude:: ../../../examples/pandas_conditional_format.py
diff --git a/dev/docs/source/example_pandas_datetime.rst b/dev/docs/source/example_pandas_datetime.rst
new file mode 100644
index 0000000..63681aa
--- /dev/null
+++ b/dev/docs/source/example_pandas_datetime.rst
@@ -0,0 +1,12 @@
+.. _ex_pandas_datetime:
+
+Example: Pandas Excel output with datetimes
+===========================================
+
+An example of converting a Pandas dataframe with datetimes to an Excel file
+with a default datetime and date format using Pandas and XlsxWriter.
+
+
+.. image:: _images/pandas_datetime.png
+
+.. literalinclude:: ../../../examples/pandas_datetime.py
diff --git a/dev/docs/source/example_pandas_multiple.rst b/dev/docs/source/example_pandas_multiple.rst
new file mode 100644
index 0000000..4440b4a
--- /dev/null
+++ b/dev/docs/source/example_pandas_multiple.rst
@@ -0,0 +1,11 @@
+.. _ex_pandas_multiple:
+
+Example: Pandas Excel with multiple dataframes
+==============================================
+
+An example of writing multiple dataframes to worksheets using Pandas and
+XlsxWriter.
+
+.. image:: _images/pandas_multiple.png
+
+.. literalinclude:: ../../../examples/pandas_multiple.py
diff --git a/dev/docs/source/example_pandas_positioning.rst b/dev/docs/source/example_pandas_positioning.rst
new file mode 100644
index 0000000..3085c4e
--- /dev/null
+++ b/dev/docs/source/example_pandas_positioning.rst
@@ -0,0 +1,12 @@
+.. _ex_pandas_positioning:
+
+Example: Pandas Excel dataframe positioning
+===========================================
+
+An example of positioning dataframes in a worksheet using Pandas and
+XlsxWriter. It also demonstrates how to write a dataframe without the header
+and index.
+
+.. image:: _images/pandas_positioning.png
+
+.. literalinclude:: ../../../examples/pandas_positioning.py
diff --git a/dev/docs/source/example_pandas_simple.rst b/dev/docs/source/example_pandas_simple.rst
new file mode 100644
index 0000000..d6db0d4
--- /dev/null
+++ b/dev/docs/source/example_pandas_simple.rst
@@ -0,0 +1,11 @@
+.. _ex_pandas_simple:
+
+Example: Pandas Excel example
+=============================
+
+A simple example of converting a Pandas dataframe to an Excel file using
+Pandas and XlsxWriter. See :ref:`ewx_pandas` for more details.
+
+.. image:: _images/pandas_simple.png
+
+.. literalinclude:: ../../../examples/pandas_simple.py
diff --git a/dev/docs/source/example_panes.rst b/dev/docs/source/example_panes.rst
new file mode 100644
index 0000000..7d040c4
--- /dev/null
+++ b/dev/docs/source/example_panes.rst
@@ -0,0 +1,13 @@
+.. _ex_panes:
+
+Example: Freeze Panes and Split Panes
+=====================================
+
+An example of how to create panes in a worksheet, both "freeze" panes and
+"split" panes. See the :func:`freeze_panes` and :func:`split_panes` methods
+for more details.
+
+.. image:: _images/panes.png
+
+.. literalinclude:: ../../../examples/panes.py
+
diff --git a/dev/docs/source/example_protection.rst b/dev/docs/source/example_protection.rst
new file mode 100644
index 0000000..6b2e8a9
--- /dev/null
+++ b/dev/docs/source/example_protection.rst
@@ -0,0 +1,12 @@
+.. _ex_protection:
+
+Example: Enabling Cell protection in Worksheets
+===============================================
+
+This program is an example cell locking and formula hiding in an Excel
+worksheet using the :func:`protect` worksheet method.
+
+.. image:: _images/worksheet_protection.png
+
+.. literalinclude:: ../../../examples/worksheet_protection.py
+
diff --git a/dev/docs/source/example_rich_strings.rst b/dev/docs/source/example_rich_strings.rst
new file mode 100644
index 0000000..0a2fd52
--- /dev/null
+++ b/dev/docs/source/example_rich_strings.rst
@@ -0,0 +1,12 @@
+.. _ex_rich_strings:
+
+Example: Writing "Rich" strings with multiple formats
+=====================================================
+
+This program is an example of writing rich stings with multiple format to a
+cell in a worksheet. See the :func:`write_rich_string` method for more details.
+
+.. image:: _images/rich_strings.png
+
+.. literalinclude:: ../../../examples/rich_strings.py
+
diff --git a/dev/docs/source/example_sparklines1.rst b/dev/docs/source/example_sparklines1.rst
new file mode 100644
index 0000000..ce28f25
--- /dev/null
+++ b/dev/docs/source/example_sparklines1.rst
@@ -0,0 +1,16 @@
+.. _ex_sparklines1:
+
+Example: Sparklines (Simple)
+============================
+
+Example of how to add sparklines to a XlsxWriter worksheet.
+
+Sparklines are small charts that fit in a single cell and are used to show
+trends in data.
+
+See the :ref:`sparklines` method for more details.
+
+.. image:: _images/sparklines1.png
+
+.. literalinclude:: ../../../examples/sparklines1.py
+
diff --git a/dev/docs/source/example_sparklines2.rst b/dev/docs/source/example_sparklines2.rst
new file mode 100644
index 0000000..7106d32
--- /dev/null
+++ b/dev/docs/source/example_sparklines2.rst
@@ -0,0 +1,16 @@
+.. _ex_sparklines2:
+
+Example: Sparklines (Advanced)
+==============================
+
+This example shows the majority of options that can be applied to sparklines.
+
+Sparklines are small charts that fit in a single cell and are used to show
+trends in data.
+
+See the :ref:`sparklines` method for more details.
+
+.. image:: _images/sparklines2.png
+
+.. literalinclude:: ../../../examples/sparklines2.py
+
diff --git a/dev/docs/source/example_tab_colors.rst b/dev/docs/source/example_tab_colors.rst
new file mode 100644
index 0000000..b890fe2
--- /dev/null
+++ b/dev/docs/source/example_tab_colors.rst
@@ -0,0 +1,12 @@
+.. _ex_tab_colors:
+
+Example: Setting Worksheet Tab Colors
+=====================================
+
+This program is an example of setting worksheet tab colors. See the
+:func:`set_tab_color` method for more details.
+
+.. image:: _images/tab_colors.png
+
+.. literalinclude:: ../../../examples/tab_colors.py
+
diff --git a/dev/docs/source/example_tables.rst b/dev/docs/source/example_tables.rst
new file mode 100644
index 0000000..3b036d1
--- /dev/null
+++ b/dev/docs/source/example_tables.rst
@@ -0,0 +1,15 @@
+.. _ex_tables:
+
+Example: Worksheet Tables
+=========================
+
+Example of how to add tables to an XlsxWriter worksheet.
+
+Tables in Excel are used to group rows and columns of data into a single
+structure that can be referenced in a formula or formatted collectively.
+
+See also :ref:`tables`.
+
+.. image:: _images/tables12.png
+
+.. literalinclude:: ../../../examples/tables.py
diff --git a/dev/docs/source/example_textbox.rst b/dev/docs/source/example_textbox.rst
new file mode 100644
index 0000000..39bf6bc
--- /dev/null
+++ b/dev/docs/source/example_textbox.rst
@@ -0,0 +1,12 @@
+.. _ex_textbox:
+
+Example: Insert Textboxes into a Worksheet
+==========================================
+
+The following is an example of how to insert and format textboxes in a
+worksheet, see :func:`insert_textbox()` and :ref:`working_with_textboxes` for
+more details.
+
+.. image:: _images/textbox01.png
+
+.. literalinclude:: ../../../examples/textbox.py
diff --git a/dev/docs/source/example_unicode_polish_utf8.rst b/dev/docs/source/example_unicode_polish_utf8.rst
new file mode 100644
index 0000000..46dc906
--- /dev/null
+++ b/dev/docs/source/example_unicode_polish_utf8.rst
@@ -0,0 +1,19 @@
+.. _ex_unicode_polish_utf8:
+
+Example: Unicode - Polish in UTF-8
+==================================
+
+This program is an example of reading in data from a UTF-8 encoded text file
+and converting it to a worksheet.
+
+The main trick is to ensure that the data read in is converted to UTF-8
+within the Python program. The XlsxWriter module will then take care of
+writing the encoding to the Excel file.
+
+The encoding of the input data shouldn't matter once it can be converted
+to UTF-8 via the :mod:`codecs` module.
+
+.. image:: _images/unicode_polish_utf8.png
+
+.. literalinclude:: ../../../examples/unicode_polish_utf8.py
+
diff --git a/dev/docs/source/example_unicode_python2.rst b/dev/docs/source/example_unicode_python2.rst
new file mode 100644
index 0000000..1117ed4
--- /dev/null
+++ b/dev/docs/source/example_unicode_python2.rst
@@ -0,0 +1,17 @@
+.. _ex_unicode_python2:
+
+Example: Simple Unicode with Python 2
+=====================================
+
+To write Unicode text in UTF-8 to a xlsxwriter file in Python 2:
+
+#. Encode the file as UTF-8.
+#. Include a "coding" directive at the start of the file.
+#. Use ``u''`` to indicate a Unicode string.
+
+.. image:: _images/unicode_python2.png
+
+.. only:: html
+
+ .. literalinclude:: ../../../examples/unicode_python2.py
+
diff --git a/dev/docs/source/example_unicode_python3.rst b/dev/docs/source/example_unicode_python3.rst
new file mode 100644
index 0000000..5891a3d
--- /dev/null
+++ b/dev/docs/source/example_unicode_python3.rst
@@ -0,0 +1,15 @@
+.. _ex_unicode_python3:
+
+Example: Simple Unicode with Python 3
+=====================================
+
+To write Unicode text in UTF-8 to a xlsxwriter file in Python 3:
+
+#. Encode the file as UTF-8.
+
+.. image:: _images/unicode_python3.png
+
+.. only:: html
+
+ .. literalinclude:: ../../../examples/unicode_python3.py
+
diff --git a/dev/docs/source/example_unicode_shift_jis.rst b/dev/docs/source/example_unicode_shift_jis.rst
new file mode 100644
index 0000000..6afcef2
--- /dev/null
+++ b/dev/docs/source/example_unicode_shift_jis.rst
@@ -0,0 +1,19 @@
+.. _ex_unicode_shift_jis:
+
+Example: Unicode - Shift JIS
+============================
+
+This program is an example of reading in data from a Shift JIS encoded text
+file and converting it to a worksheet.
+
+The main trick is to ensure that the data read in is converted to UTF-8
+within the Python program. The XlsxWriter module will then take care of
+writing the encoding to the Excel file.
+
+The encoding of the input data shouldn't matter once it can be converted
+to UTF-8 via the :mod:`codecs` module.
+
+.. image:: _images/unicode_shift_jis.png
+
+.. literalinclude:: ../../../examples/unicode_shift_jis.py
+
diff --git a/dev/docs/source/examples.rst b/dev/docs/source/examples.rst
new file mode 100644
index 0000000..6179e7a
--- /dev/null
+++ b/dev/docs/source/examples.rst
@@ -0,0 +1,47 @@
+Examples
+========
+
+The following are some of the examples included in the
+`examples <https://github.com/jmcnamara/XlsxWriter/tree/master/examples>`_
+directory of the XlsxWriter distribution.
+
+.. toctree::
+ :maxdepth: 1
+
+ example_hello_world.rst
+ example_demo.rst
+ example_datetimes.rst
+ example_hyperlink.rst
+ example_array_formula.rst
+ example_autofilter.rst
+ example_data_validate.rst
+ example_conditional_format.rst
+ example_defined_name.rst
+ example_merge1.rst
+ example_rich_strings.rst
+ example_merge_rich.rst
+ example_images.rst
+ example_images_bytesio.rst
+ example_http_server.rst
+ example_http_server3.rst
+ example_headers_footers.rst
+ example_panes.rst
+ example_tables.rst
+ example_sparklines1.rst
+ example_sparklines2.rst
+ example_comments1.rst
+ example_comments2.rst
+ example_textbox.rst
+ example_outline1.rst
+ example_outline2.rst
+ example_doc_properties.rst
+ example_unicode_python2.rst
+ example_unicode_python3.rst
+ example_unicode_polish_utf8.rst
+ example_unicode_shift_jis.rst
+ example_tab_colors.rst
+ example_diagonal_border.rst
+ example_protection.rst
+ example_hide_sheet.rst
+ example_hide_row_col.rst
+ example_macros.rst
diff --git a/dev/docs/source/faq.rst b/dev/docs/source/faq.rst
new file mode 100644
index 0000000..1df115f
--- /dev/null
+++ b/dev/docs/source/faq.rst
@@ -0,0 +1,67 @@
+:tocdepth: 1
+
+.. _faq:
+
+Frequently Asked Questions
+==========================
+
+The section outlines some answers to frequently asked questions.
+
+Q. Can XlsxWriter use an existing Excel file as a template?
+-----------------------------------------------------------
+
+No.
+
+XlsxWriter is designed only as a file *writer*. It cannot read or modify an
+existing Excel file.
+
+
+Q. Why do my formulas show a zero result in some, non-Excel applications?
+-------------------------------------------------------------------------
+
+Due to wide range of possible formulas and interdependencies between them
+XlsxWriter doesn't, and realistically cannot, calculate the result of a
+formula when it is written to an XLSX file. Instead, it stores the value 0 as
+the formula result. It then sets a global flag in the XLSX file to say that
+all formulas and functions should be recalculated when the file is opened.
+
+This is the method recommended in the Excel documentation and in general it
+works fine with spreadsheet applications. However, applications that don't
+have a facility to calculate formulas, such as Excel Viewer, or several mobile
+applications, will only display the 0 results.
+
+If required, it is also possible to specify the calculated result of the
+formula using the optional ``value`` parameter in :func:`write_formula()`::
+
+ worksheet.write_formula('A1', '=2+2', num_format, 4)
+
+See also :ref:`formula_result`,
+
+
+Q. Can I apply a format to a range of cells in one go?
+------------------------------------------------------
+
+Currently no. However, it is a planned features to allow cell formats and data
+to be written separately.
+
+
+Q. Is feature X supported or will it be supported?
+--------------------------------------------------
+
+All supported features are documented.
+
+
+Q. Is there an "AutoFit" option for columns?
+--------------------------------------------
+
+Unfortunately, there is no way to specify "AutoFit" for a column in the Excel
+file format. This feature is only available at runtime from within Excel. It
+is possible to simulate "AutoFit" in your application by tracking the maximum
+width of the data in the column as your write it and then adjusting the column
+width at the end.
+
+
+Q. Do people actually ask these questions frequently, or at all?
+----------------------------------------------------------------
+
+Apart from this question, yes.
diff --git a/dev/docs/source/format.rst b/dev/docs/source/format.rst
new file mode 100644
index 0000000..8294b99
--- /dev/null
+++ b/dev/docs/source/format.rst
@@ -0,0 +1,1037 @@
+.. _format:
+
+The Format Class
+================
+
+This section describes the methods and properties that are available for
+formatting cells in Excel.
+
+The properties of a cell that can be formatted include: fonts, colors,
+patterns, borders, alignment and number formatting.
+
+.. image:: _images/formats_intro.png
+
+Creating and using a Format object
+----------------------------------
+
+Cell formatting is defined through a Format object. Format objects are created
+by calling the workbook ``add_format()`` method as follows::
+
+ format1 = workbook.add_format() # Set properties later.
+ format2 = workbook.add_format(props) # Set properties at creation.
+
+There are two ways of setting Format properties: by using the object interface
+or by setting the property as a dictionary of key/value pairs in the
+constructor. For example, a typical use of the object interface would be as
+follows::
+
+ format = workbook.add_format()
+ format.set_bold()
+ format.set_font_color('red')
+
+By comparison the properties can be set by passing a dictionary of properties
+to the `add_format()` constructor::
+
+ format = workbook.add_format({'bold': True, 'font_color': 'red'})
+
+In general the key/value interface is more flexible and clearer than the
+object method and is the recommended method for setting format
+properties. However, both methods produce the same result.
+
+Once a Format object has been constructed and its properties have been set it
+can be passed as an argument to the worksheet ``write`` methods as follows::
+
+ worksheet.write (0, 0, 'Foo', format)
+ worksheet.write_string(1, 0, 'Bar', format)
+ worksheet.write_number(2, 0, 3, format)
+ worksheet.write_blank (3, 0, '', format)
+
+Formats can also be passed to the worksheet ``set_row()`` and ``set_column()``
+methods to define the default formatting properties for a row or column::
+
+ worksheet.set_row(0, 18, format)
+ worksheet.set_column('A:D', 20, format)
+
+Format Defaults
+---------------
+
+The default Excel 2007+ cell format is Calibri 11 with all other properties off.
+
+In general a format method call without an argument will turn a property on,
+for example::
+
+ format = workbook.add_format()
+
+ format.set_bold() # Turns bold on.
+ format.set_bold(True) # Also turns bold on.
+
+
+Since most properties are already off by default it isn't generally required to
+turn them off. However, it is possible if required::
+
+ format.set_bold(False) # Turns bold off.
+
+
+Modifying Formats
+-----------------
+
+Each unique cell format in an XlsxWriter spreadsheet must have a corresponding
+Format object. It isn't possible to use a Format with a ``write()`` method and
+then redefine it for use at a later stage. This is because a Format is applied
+to a cell not in its current state but in its final state. Consider the
+following example::
+
+ format = workbook.add_format({'bold': True, 'font_color': 'red'})
+ worksheet.write('A1', 'Cell A1', format)
+
+ # Later...
+ format.set_font_color('green')
+ worksheet.write('B1', 'Cell B1', format)
+
+Cell A1 is assigned a format which initially has the font set to the color
+red. However, the color is subsequently set to green. When Excel displays
+Cell A1 it will display the final state of the Format which in this case will
+be the color green.
+
+
+
+Format methods and Format properties
+------------------------------------
+
+The following table shows the Excel format categories, the formatting
+properties that can be applied and the equivalent object method:
+
++------------+------------------+----------------------+------------------------------+
+| Category | Description | Property | Method Name |
++============+==================+======================+==============================+
+| Font | Font type | ``'font_name'`` | :func:`set_font_name()` |
++------------+------------------+----------------------+------------------------------+
+| | Font size | ``'font_size'`` | :func:`set_font_size()` |
++------------+------------------+----------------------+------------------------------+
+| | Font color | ``'font_color'`` | :func:`set_font_color()` |
++------------+------------------+----------------------+------------------------------+
+| | Bold | ``'bold'`` | :func:`set_bold()` |
++------------+------------------+----------------------+------------------------------+
+| | Italic | ``'italic'`` | :func:`set_italic()` |
++------------+------------------+----------------------+------------------------------+
+| | Underline | ``'underline'`` | :func:`set_underline()` |
++------------+------------------+----------------------+------------------------------+
+| | Strikeout | ``'font_strikeout'`` | :func:`set_font_strikeout()` |
++------------+------------------+----------------------+------------------------------+
+| | Super/Subscript | ``'font_script'`` | :func:`set_font_script()` |
++------------+------------------+----------------------+------------------------------+
+| Number | Numeric format | ``'num_format'`` | :func:`set_num_format()` |
++------------+------------------+----------------------+------------------------------+
+| Protection | Lock cells | ``'locked'`` | :func:`set_locked()` |
++------------+------------------+----------------------+------------------------------+
+| | Hide formulas | ``'hidden'`` | :func:`set_hidden()` |
++------------+------------------+----------------------+------------------------------+
+| Alignment | Horizontal align | ``'align'`` | :func:`set_align()` |
++------------+------------------+----------------------+------------------------------+
+| | Vertical align | ``'valign'`` | :func:`set_align()` |
++------------+------------------+----------------------+------------------------------+
+| | Rotation | ``'rotation'`` | :func:`set_rotation()` |
++------------+------------------+----------------------+------------------------------+
+| | Text wrap | ``'text_wrap'`` | :func:`set_text_wrap()` |
++------------+------------------+----------------------+------------------------------+
+| | Justify last | ``'text_justlast'`` | :func:`set_text_justlast()` |
++------------+------------------+----------------------+------------------------------+
+| | Center across | ``'center_across'`` | :func:`set_center_across()` |
++------------+------------------+----------------------+------------------------------+
+| | Indentation | ``'indent'`` | :func:`set_indent()` |
++------------+------------------+----------------------+------------------------------+
+| | Shrink to fit | ``'shrink'`` | :func:`set_shrink()` |
++------------+------------------+----------------------+------------------------------+
+| Pattern | Cell pattern | ``'pattern'`` | :func:`set_pattern()` |
++------------+------------------+----------------------+------------------------------+
+| | Background color | ``'bg_color'`` | :func:`set_bg_color()` |
++------------+------------------+----------------------+------------------------------+
+| | Foreground color | ``'fg_color'`` | :func:`set_fg_color()` |
++------------+------------------+----------------------+------------------------------+
+| Border | Cell border | ``'border'`` | :func:`set_border()` |
++------------+------------------+----------------------+------------------------------+
+| | Bottom border | ``'bottom'`` | :func:`set_bottom()` |
++------------+------------------+----------------------+------------------------------+
+| | Top border | ``'top'`` | :func:`set_top()` |
++------------+------------------+----------------------+------------------------------+
+| | Left border | ``'left'`` | :func:`set_left()` |
++------------+------------------+----------------------+------------------------------+
+| | Right border | ``'right'`` | :func:`set_right()` |
++------------+------------------+----------------------+------------------------------+
+| | Border color | ``'border_color'`` | :func:`set_border_color()` |
++------------+------------------+----------------------+------------------------------+
+| | Bottom color | ``'bottom_color'`` | :func:`set_bottom_color()` |
++------------+------------------+----------------------+------------------------------+
+| | Top color | ``'top_color'`` | :func:`set_top_color()` |
++------------+------------------+----------------------+------------------------------+
+| | Left color | ``'left_color'`` | :func:`set_left_color()` |
++------------+------------------+----------------------+------------------------------+
+| | Right color | ``'right_color'`` | :func:`set_right_color()` |
++------------+------------------+----------------------+------------------------------+
+
+
+The format properties and methods are explained in the following sections.
+
+
+format.set_font_name()
+----------------------
+
+.. py:function:: set_font_name(fontname)
+
+ Set the font used in the cell.
+
+ :param string fontname: Cell font.
+
+Specify the font used used in the cell format::
+
+ cell_format.set_font_name('Times New Roman')
+
+.. image:: _images/format_font_name.png
+
+Excel can only display fonts that are installed on the system that it is
+running on. Therefore it is best to use the fonts that come as standard such
+as 'Calibri', 'Times New Roman' and 'Courier New'.
+
+The default font for an unformatted cell in Excel 2007+ is 'Calibri'.
+
+
+format.set_font_size()
+----------------------
+
+.. py:function:: set_font_size(size)
+
+ Set the size of the font used in the cell.
+
+ :param int size: The cell font size.
+
+Set the font size of the cell format::
+
+ format = workbook.add_format()
+ format.set_font_size(30)
+
+.. image:: _images/format_font_size.png
+
+Excel adjusts the height of a row to accommodate the largest font size in the
+row. You can also explicitly specify the height of a row using the
+:func:`set_row()` worksheet method.
+
+
+format.set_font_color()
+-----------------------
+
+.. py:function:: set_font_color(color)
+
+ Set the color of the font used in the cell.
+
+ :param string color: The cell font color.
+
+
+Set the font color::
+
+ format = workbook.add_format()
+
+ format.set_font_color('red')
+
+ worksheet.write(0, 0, 'wheelbarrow', format)
+
+.. image:: _images/format_font_color.png
+
+The color can be a Html style ``#RRGGBB`` string or a limited number of named
+colors, see :ref:`colors`.
+
+Note: The ``set_font_color()`` method is used to set the color of the font in
+a cell. To set the color of a cell use the :func:`set_bg_color()` and
+:func:`set_pattern()` methods.
+
+
+format.set_bold()
+-----------------
+
+.. py:function:: set_bold()
+
+ Turn on bold for the format font.
+
+Set the bold property of the font::
+
+ format.set_bold()
+
+.. image:: _images/format_font_bold.png
+
+
+format.set_italic()
+-------------------
+
+.. py:function:: set_italic()
+
+ Turn on italic for the format font.
+
+Set the italic property of the font::
+
+ format.set_italic()
+
+.. image:: _images/format_font_italic.png
+
+
+format.set_underline()
+----------------------
+
+.. py:function:: set_underline()
+
+ Turn on underline for the format.
+
+ :param int style: Underline style.
+
+Set the underline property of the format::
+
+ format.set_underline()
+
+.. image:: _images/format_font_underlined.png
+
+The available underline styles are:
+
+* 1 = Single underline (the default)
+* 2 = Double underline
+* 33 = Single accounting underline
+* 34 = Double accounting underline
+
+
+format.set_font_strikeout()
+---------------------------
+
+.. py:function:: set_font_strikeout()
+
+ Set the strikeout property of the font.
+
+.. image:: _images/format_font_strikeout.png
+
+
+format.set_font_script()
+------------------------
+
+.. py:function:: set_font_script()
+
+ Set the superscript/subscript property of the font.
+
+The available options are:
+
+* 1 = Superscript
+* 2 = Subscript
+
+.. image:: _images/format_font_script.png
+
+
+This property is generally only useful when used in conjunction with :func:`write_rich_string()`.
+
+
+format.set_num_format()
+-----------------------
+
+.. py:function:: set_num_format(format_string)
+
+ Set the number format for a cell.
+
+ :param string format_string: The cell number format.
+
+This method is used to define the numerical format of a number in Excel. It
+controls whether a number is displayed as an integer, a floating point number,
+a date, a currency value or some other user defined format.
+
+The numerical format of a cell can be specified by using a format string or an
+index to one of Excel's built-in formats::
+
+ format1 = workbook.add_format()
+ format2 = workbook.add_format()
+
+ format1.set_num_format('d mmm yyyy') # Format string.
+ format2.set_num_format(0x0F) # Format index.
+
+Format strings can control any aspect of number formatting allowed by Excel::
+
+ format01.set_num_format('0.000')
+ worksheet.write(1, 0, 3.1415926, format01) # -> 3.142
+
+ format02.set_num_format('#,##0')
+ worksheet.write(2, 0, 1234.56, format02) # -> 1,235
+
+ format03.set_num_format('#,##0.00')
+ worksheet.write(3, 0, 1234.56, format03) # -> 1,234.56
+
+ format04.set_num_format('0.00')
+ worksheet.write(4, 0, 49.99, format04) # -> 49.99
+
+ format05.set_num_format('mm/dd/yy')
+ worksheet.write(5, 0, 36892.521, format05) # -> 01/01/01
+
+ format06.set_num_format('mmm d yyyy')
+ worksheet.write(6, 0, 36892.521, format06) # -> Jan 1 2001
+
+ format07.set_num_format('d mmmm yyyy')
+ worksheet.write(7, 0, 36892.521, format07) # -> 1 January 2001
+
+ format08.set_num_format('dd/mm/yyyy hh:mm AM/PM')
+ worksheet.write(8, 0, 36892.521, format08) # -> 01/01/2001 12:30 AM
+
+ format09.set_num_format('0 "dollar and" .00 "cents"')
+ worksheet.write(9, 0, 1.87, format09) # -> 1 dollar and .87 cents
+
+ # Conditional numerical formatting.
+ format10.set_num_format('[Green]General;[Red]-General;General')
+ worksheet.write(10, 0, 123, format10) # > 0 Green
+ worksheet.write(11, 0, -45, format10) # < 0 Red
+ worksheet.write(12, 0, 0, format10) # = 0 Default color
+
+ # Zip code.
+ format11.set_num_format('00000')
+ worksheet.write(13, 0, 1209, format11)
+
+.. image:: _images/formats_num_str.png
+
+The number system used for dates is described in
+:ref:`working_with_dates_and_time`.
+
+The color format should have one of the following values::
+
+ [Black] [Blue] [Cyan] [Green] [Magenta] [Red] [White] [Yellow]
+
+For more information refer to the
+`Microsoft documentation on cell formats <http://office.microsoft.com/en-gb/assistance/HP051995001033.aspx>`_.
+
+Excel's built-in formats are shown in the following table:
+
++-------+-------+--------------------------------------------------------+
+| Index | Index | Format String |
++=======+=======+========================================================+
+| 0 | 0x00 | ``General`` |
++-------+-------+--------------------------------------------------------+
+| 1 | 0x01 | ``0`` |
++-------+-------+--------------------------------------------------------+
+| 2 | 0x02 | ``0.00`` |
++-------+-------+--------------------------------------------------------+
+| 3 | 0x03 | ``#,##0`` |
++-------+-------+--------------------------------------------------------+
+| 4 | 0x04 | ``#,##0.00`` |
++-------+-------+--------------------------------------------------------+
+| 5 | 0x05 | ``($#,##0_);($#,##0)`` |
++-------+-------+--------------------------------------------------------+
+| 6 | 0x06 | ``($#,##0_);[Red]($#,##0)`` |
++-------+-------+--------------------------------------------------------+
+| 7 | 0x07 | ``($#,##0.00_);($#,##0.00)`` |
++-------+-------+--------------------------------------------------------+
+| 8 | 0x08 | ``($#,##0.00_);[Red]($#,##0.00)`` |
++-------+-------+--------------------------------------------------------+
+| 9 | 0x09 | ``0%`` |
++-------+-------+--------------------------------------------------------+
+| 10 | 0x0a | ``0.00%`` |
++-------+-------+--------------------------------------------------------+
+| 11 | 0x0b | ``0.00E+00`` |
++-------+-------+--------------------------------------------------------+
+| 12 | 0x0c | ``# ?/?`` |
++-------+-------+--------------------------------------------------------+
+| 13 | 0x0d | ``# ??/??`` |
++-------+-------+--------------------------------------------------------+
+| 14 | 0x0e | ``m/d/yy`` |
++-------+-------+--------------------------------------------------------+
+| 15 | 0x0f | ``d-mmm-yy`` |
++-------+-------+--------------------------------------------------------+
+| 16 | 0x10 | ``d-mmm`` |
++-------+-------+--------------------------------------------------------+
+| 17 | 0x11 | ``mmm-yy`` |
++-------+-------+--------------------------------------------------------+
+| 18 | 0x12 | ``h:mm AM/PM`` |
++-------+-------+--------------------------------------------------------+
+| 19 | 0x13 | ``h:mm:ss AM/PM`` |
++-------+-------+--------------------------------------------------------+
+| 20 | 0x14 | ``h:mm`` |
++-------+-------+--------------------------------------------------------+
+| 21 | 0x15 | ``h:mm:ss`` |
++-------+-------+--------------------------------------------------------+
+| 22 | 0x16 | ``m/d/yy h:mm`` |
++-------+-------+--------------------------------------------------------+
+| ... | ... | ... |
++-------+-------+--------------------------------------------------------+
+| 37 | 0x25 | ``(#,##0_);(#,##0)`` |
++-------+-------+--------------------------------------------------------+
+| 38 | 0x26 | ``(#,##0_);[Red](#,##0)`` |
++-------+-------+--------------------------------------------------------+
+| 39 | 0x27 | ``(#,##0.00_);(#,##0.00)`` |
++-------+-------+--------------------------------------------------------+
+| 40 | 0x28 | ``(#,##0.00_);[Red](#,##0.00)`` |
++-------+-------+--------------------------------------------------------+
+| 41 | 0x29 | ``_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)`` |
++-------+-------+--------------------------------------------------------+
+| 42 | 0x2a | ``_($* #,##0_);_($* (#,##0);_($* "-"_);_(@_)`` |
++-------+-------+--------------------------------------------------------+
+| 43 | 0x2b | ``_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)`` |
++-------+-------+--------------------------------------------------------+
+| 44 | 0x2c | ``_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)`` |
++-------+-------+--------------------------------------------------------+
+| 45 | 0x2d | ``mm:ss`` |
++-------+-------+--------------------------------------------------------+
+| 46 | 0x2e | ``[h]:mm:ss`` |
++-------+-------+--------------------------------------------------------+
+| 47 | 0x2f | ``mm:ss.0`` |
++-------+-------+--------------------------------------------------------+
+| 48 | 0x30 | ``##0.0E+0`` |
++-------+-------+--------------------------------------------------------+
+| 49 | 0x31 | ``@`` |
++-------+-------+--------------------------------------------------------+
+
+.. note::
+
+ Numeric formats 23 to 36 are not documented by Microsoft and may differ
+ in international versions. The listed date and currency formats may also
+ vary depending on system settings.
+
+.. note::
+
+ The dollar sign in the above format appears as the defined local currency
+ symbol.
+
+
+format.set_locked()
+-------------------
+
+.. py:function:: set_locked(state)
+
+ Set the cell locked state.
+
+ :param bool state: Turn cell locking on or off. Defaults to True.
+
+This property can be used to prevent modification of a cells contents.
+Following Excel's convention, cell locking is turned on by default. However,
+it only has an effect if the worksheet has been protected using the worksheet
+:func:`protect()` method::
+
+ locked = workbook.add_format()
+ locked.set_locked(True)
+
+ unlocked = workbook.add_format()
+ locked.set_locked(False)
+
+ # Enable worksheet protection
+ worksheet.protect()
+
+ # This cell cannot be edited.
+ worksheet.write('A1', '=1+2', locked)
+
+ # This cell can be edited.
+ worksheet.write('A2', '=1+2', unlocked)
+
+
+format.set_hidden()
+-------------------
+
+.. py:function:: set_hidden()
+
+ Hide formulas in a cell.
+
+
+This property is used to hide a formula while still displaying its result. This
+is generally used to hide complex calculations from end users who are only
+interested in the result. It only has an effect if the worksheet has been
+protected using the worksheet :func:`protect()` method::
+
+ hidden = workbook.add_format()
+ hidden.set_hidden()
+
+ # Enable worksheet protection
+ worksheet.protect()
+
+ # The formula in this cell isn't visible
+ worksheet.write('A1', '=1+2', hidden)
+
+
+format.set_align()
+------------------
+
+.. py:function:: set_align(alignment)
+
+ Set the alignment for data in the cell.
+
+ :param string alignment: The vertical and or horizontal alignment direction.
+
+This method is used to set the horizontal and vertical text alignment within a
+cell. The following are the available horizontal alignments:
+
++----------------------+
+| Horizontal alignment |
++======================+
+| center |
++----------------------+
+| right |
++----------------------+
+| fill |
++----------------------+
+| justify |
++----------------------+
+| center_across |
++----------------------+
+
+The following are the available vertical alignments:
+
++--------------------+
+| Vertical alignment |
++====================+
+| top |
++--------------------+
+| vcenter |
++--------------------+
+| bottom |
++--------------------+
+| vjustify |
++--------------------+
+
+
+As in Excel, vertical and horizontal alignments can be combined::
+
+ format = workbook.add_format()
+
+ format.set_align('center')
+ format.set_align('vcenter')
+
+ worksheet.set_row(0, 70)
+ worksheet.set_column('A:A', 30)
+
+ worksheet.write(0, 0, 'Some Text', format)
+
+.. image:: _images/format_font_align.png
+
+Text can be aligned across two or more adjacent cells using the
+``'center_across'`` property. However, for genuine merged cells it is better
+to use the ``merge_range()`` worksheet method.
+
+The ``'vjustify'`` (vertical justify) option can be used to provide automatic
+text wrapping in a cell. The height of the cell will be adjusted to
+accommodate the wrapped text. To specify where the text wraps use the
+``set_text_wrap()`` method.
+
+
+format.set_center_across()
+--------------------------
+
+.. py:function:: set_center_across()
+
+ Center text across adjacent cells.
+
+Text can be aligned across two or more adjacent cells using the
+``set_center_across()`` method. This is an alias for the
+``set_align('center_across')`` method call.
+
+Only one cell should contain the text, the other cells should be blank::
+
+ format = workbook.add_format()
+ format.set_center_across()
+
+ worksheet.write(1, 1, 'Center across selection', format)
+ worksheet.write_blank(1, 2, '', format)
+
+For actual merged cells it is better to use the ``merge_range()`` worksheet
+method.
+
+
+format.set_text_wrap()
+----------------------
+
+.. py:function:: set_text_wrap()
+
+ Wrap text in a cell.
+
+Turn text wrapping on for text in a cell::
+
+ format = workbook.add_format()
+ format.set_text_wrap()
+
+ worksheet.write(0, 0, "Some long text to wrap in a cell", format)
+
+If you wish to control where the text is wrapped you can add newline characters
+to the string::
+
+ worksheet.write(2, 0, "It's\na bum\nwrap", format)
+
+.. image:: _images/format_font_text_wrap.png
+
+Excel will adjust the height of the row to accommodate the wrapped text. A
+similar effect can be obtained without newlines using the
+``set_align('vjustify')`` method.
+
+
+format.set_rotation()
+---------------------
+
+.. py:function:: set_rotation(angle)
+
+ Set the rotation of the text in a cell.
+
+ :param int angle: Rotation angle in the range -90 to 90 and 270.
+
+Set the rotation of the text in a cell. The rotation can be any angle in the
+range -90 to 90 degrees::
+
+ format = workbook.add_format()
+ format.set_rotation(30)
+
+ worksheet.write(0, 0, 'This text is rotated', format)
+
+.. image:: _images/format_font_text_rotated.png
+
+The angle 270 is also supported. This indicates text where the letters run from
+top to bottom.
+
+
+format.set_indent()
+-------------------
+
+.. py:function:: set_indent(level)
+
+ Set the cell text indentation level.
+
+ :param int level: Indentation level.
+
+This method can be used to indent text in a cell. The argument, which should be
+an integer, is taken as the level of indentation::
+
+ format1 = workbook.add_format()
+ format2 = workbook.add_format()
+
+ format1.set_indent(1)
+ format2.set_indent(2)
+
+ worksheet.write('A1', 'This text is indented 1 level', format1)
+ worksheet.write('A2', 'This text is indented 2 levels', format2)
+
+.. image:: _images/text_indent.png
+
+Indentation is a horizontal alignment property. It will override any other
+horizontal properties but it can be used in conjunction with vertical
+properties.
+
+
+format.set_shrink()
+-------------------
+
+.. py:function:: set_shrink()
+
+ Turn on the text "shrink to fit" for a cell.
+
+This method can be used to shrink text so that it fits in a cell::
+
+ format = workbook.add_format()
+ format.set_shrink()
+
+ worksheet.write(0, 0, 'Honey, I shrunk the text!', format)
+
+
+format.set_text_justlast()
+--------------------------
+
+.. py:function:: set_text_justlast()
+
+ Turn on the justify last text property.
+
+Only applies to Far Eastern versions of Excel.
+
+
+format.set_pattern()
+--------------------
+
+.. py:function:: set_pattern(index)
+
+ :param int index: Pattern index. 0 - 18.
+
+Set the background pattern of a cell.
+
+The most common pattern is 1 which is a solid fill of the background color.
+
+
+format.set_bg_color()
+---------------------
+
+.. py:function:: set_bg_color(color)
+
+ Set the color of the background pattern in a cell.
+
+ :param string color: The cell font color.
+
+The ``set_bg_color()`` method can be used to set the background color of a
+pattern. Patterns are defined via the ``set_pattern()`` method. If a pattern
+hasn't been defined then a solid fill pattern is used as the default.
+
+Here is an example of how to set up a solid fill in a cell::
+
+ format = workbook.add_format()
+
+ format.set_pattern(1) # This is optional when using a solid fill.
+ format.set_bg_color('green')
+
+ worksheet.write('A1', 'Ray', format)
+
+.. image:: _images/formats_set_bg_color.png
+
+The color can be a Html style ``#RRGGBB`` string or a limited number of named
+colors, see :ref:`colors`.
+
+
+
+format.set_fg_color()
+---------------------
+
+.. py:function:: set_fg_color(color)
+
+ Set the color of the foreground pattern in a cell.
+
+ :param string color: The cell font color.
+
+The ``set_fg_color()`` method can be used to set the foreground color of a
+pattern.
+
+The color can be a Html style ``#RRGGBB`` string or a limited number of named
+colors, see :ref:`colors`.
+
+
+
+format.set_border()
+-------------------
+
+.. py:function:: set_border(style)
+
+ Set the cell border style.
+
+ :param int style: Border style index. Default is 1.
+
+Individual border elements can be configured using the following methods with
+the same parameters:
+
+* :func:`set_bottom()`
+* :func:`set_top()`
+* :func:`set_left()`
+* :func:`set_right()`
+
+A cell border is comprised of a border on the bottom, top, left and right.
+These can be set to the same value using ``set_border()`` or individually
+using the relevant method calls shown above.
+
+The following shows the border styles sorted by XlsxWriter index number:
+
++-------+---------------+--------+-----------------+
+| Index | Name | Weight | Style |
++=======+===============+========+=================+
+| 0 | None | 0 | |
++-------+---------------+--------+-----------------+
+| 1 | Continuous | 1 | ``-----------`` |
++-------+---------------+--------+-----------------+
+| 2 | Continuous | 2 | ``-----------`` |
++-------+---------------+--------+-----------------+
+| 3 | Dash | 1 | ``- - - - - -`` |
++-------+---------------+--------+-----------------+
+| 4 | Dot | 1 | ``. . . . . .`` |
++-------+---------------+--------+-----------------+
+| 5 | Continuous | 3 | ``-----------`` |
++-------+---------------+--------+-----------------+
+| 6 | Double | 3 | ``===========`` |
++-------+---------------+--------+-----------------+
+| 7 | Continuous | 0 | ``-----------`` |
++-------+---------------+--------+-----------------+
+| 8 | Dash | 2 | ``- - - - - -`` |
++-------+---------------+--------+-----------------+
+| 9 | Dash Dot | 1 | ``- . - . - .`` |
++-------+---------------+--------+-----------------+
+| 10 | Dash Dot | 2 | ``- . - . - .`` |
++-------+---------------+--------+-----------------+
+| 11 | Dash Dot Dot | 1 | ``- . . - . .`` |
++-------+---------------+--------+-----------------+
+| 12 | Dash Dot Dot | 2 | ``- . . - . .`` |
++-------+---------------+--------+-----------------+
+| 13 | SlantDash Dot | 2 | ``/ - . / - .`` |
++-------+---------------+--------+-----------------+
+
+The following shows the borders in the order shown in the Excel Dialog:
+
++-------+-----------------+-------+-----------------+
+| Index | Style | Index | Style |
++=======+=================+=======+=================+
+| 0 | None | 12 | ``- . . - . .`` |
++-------+-----------------+-------+-----------------+
+| 7 | ``-----------`` | 13 | ``/ - . / - .`` |
++-------+-----------------+-------+-----------------+
+| 4 | ``. . . . . .`` | 10 | ``- . - . - .`` |
++-------+-----------------+-------+-----------------+
+| 11 | ``- . . - . .`` | 8 | ``- - - - - -`` |
++-------+-----------------+-------+-----------------+
+| 9 | ``- . - . - .`` | 2 | ``-----------`` |
++-------+-----------------+-------+-----------------+
+| 3 | ``- - - - - -`` | 5 | ``-----------`` |
++-------+-----------------+-------+-----------------+
+| 1 | ``-----------`` | 6 | ``===========`` |
++-------+-----------------+-------+-----------------+
+
+
+format.set_bottom()
+-------------------
+
+.. py:function:: set_bottom(style)
+
+ Set the cell bottom border style.
+
+ :param int style: Border style index. Default is 1.
+
+Set the cell bottom border style. See :func:`set_border` for details on the
+border styles.
+
+
+format.set_top()
+----------------
+
+.. py:function:: set_top(style)
+
+ Set the cell top border style.
+
+ :param int style: Border style index. Default is 1.
+
+Set the cell top border style. See :func:`set_border` for details on the border
+styles.
+
+
+format.set_left()
+-----------------
+
+.. py:function:: set_left(style)
+
+ Set the cell left border style.
+
+ :param int style: Border style index. Default is 1.
+
+Set the cell left border style. See :func:`set_border` for details on the
+border styles.
+
+
+format.set_right()
+------------------
+
+.. py:function:: set_right(style)
+
+ Set the cell right border style.
+
+ :param int style: Border style index. Default is 1.
+
+Set the cell right border style. See :func:`set_border` for details on the
+border styles.
+
+
+format.set_border_color()
+-------------------------
+
+.. py:function:: set_border_color(color)
+
+ Set the color of the cell border.
+
+ :param string color: The cell border color.
+
+Individual border elements can be configured using the following methods with
+the same parameters:
+
+* :func:`set_bottom_color()`
+* :func:`set_top_color()`
+* :func:`set_left_color()`
+* :func:`set_right_color()`
+
+Set the color of the cell borders. A cell border is comprised of a border on
+the bottom, top, left and right. These can be set to the same color using
+``set_border_color()`` or individually using the relevant method calls shown
+above.
+
+The color can be a Html style ``#RRGGBB`` string or a limited number of named
+colors, see :ref:`colors`.
+
+
+format.set_bottom_color()
+-------------------------
+
+.. py:function:: set_bottom_color(color)
+
+ Set the color of the bottom cell border.
+
+ :param string color: The cell border color.
+
+See :func:`set_border_color` for details on the border colors.
+
+
+format.set_top_color()
+----------------------
+
+.. py:function:: set_top_color(color)
+
+ Set the color of the top cell border.
+
+ :param string color: The cell border color.
+
+See :func:`set_border_color` for details on the border colors.
+
+
+format.set_left_color()
+-----------------------
+
+.. py:function:: set_left_color(color)
+
+ Set the color of the left cell border.
+
+ :param string color: The cell border color.
+
+See :func:`set_border_color` for details on the border colors.
+
+
+format.set_right_color()
+------------------------
+
+.. py:function:: set_right_color(color)
+
+ Set the color of the right cell border.
+
+ :param string color: The cell border color.
+
+See :func:`set_border_color` for details on the border colors.
+
+
+format.set_diag_border()
+------------------------
+
+.. py:function:: set_diag_border(style)
+
+ Set the diagonal cell border style.
+
+ :param int style: Border style index. Default is 1.
+
+Set the style for a diagonal border. The `style` is the same as those used in
+:func:`set_border`.
+
+See :ref:`ex_diagonal_border`.
+
+.. image:: _images/diagonal_border.png
+
+
+format.set_diag_type()
+------------------------
+
+.. py:function:: set_diag_type(style)
+
+ Set the diagonal cell border type.
+
+ :param int style: Border type, 1-3. No default.
+
+Set the type of the diagonal border. The `style` should be one of the
+following values:
+
+1. From bottom left to top right.
+2. From top left to bottom right.
+3. Same as type 1 and 2 combined.
+
+
+format.set_diag_color()
+-----------------------
+
+.. py:function:: set_diag_color(color)
+
+ Set the color of the diagonal cell border.
+
+ :param string color: The cell border color.
+
+See :func:`set_border_color` for details on the border colors.
diff --git a/dev/docs/source/getting_started.rst b/dev/docs/source/getting_started.rst
new file mode 100644
index 0000000..ba356aa
--- /dev/null
+++ b/dev/docs/source/getting_started.rst
@@ -0,0 +1,122 @@
+.. _getting_started:
+
+Getting Started with XlsxWriter
+===============================
+
+Here are some easy instructions to get you up and running with the XlsxWriter
+module.
+
+
+Installing XlsxWriter
+---------------------
+
+The first step is to install the XlsxWriter module. There are several ways to
+do this.
+
+Using PIP
+*********
+
+The `pip <http://www.pip-installer.org/en/latest/index.html>`_ installer is
+the preferred method for installing Python modules from
+`PyPI <http://pypi.python.org/pypi>`_, the Python Package Index::
+
+ $ sudo pip install XlsxWriter
+
+.. Note::
+ Windows users can omit ``sudo`` at the start of the command.
+
+
+Using Easy_Install
+******************
+
+If ``pip`` doesn't work you can try
+`easy_install <http://peak.telecommunity.com/DevCenter/EasyInstall>`_::
+
+ $ sudo easy_install XlsxWriter
+
+Installing from a tarball
+*************************
+
+If you download a tarball of the latest version of XlsxWriter you can install
+it as follows (change the version number to suit)::
+
+ $ tar -zxvf XlsxWriter-1.2.3.tar.gz
+
+ $ cd XlsxWriter-1.2.3
+ $ sudo python setup.py install
+
+A tarball of the latest code can be downloaded from GitHub as follows::
+
+ $ curl -O -L http://github.com/jmcnamara/XlsxWriter/archive/master.tar.gz
+
+ $ tar zxvf master.tar.gz
+ $ cd XlsxWriter-master/
+ $ sudo python setup.py install
+
+
+Cloning from GitHub
+*******************
+
+The XlsxWriter source code and bug tracker is in the
+`XlsxWriter repository <http://github.com/jmcnamara/XlsxWriter>`_ on GitHub.
+You can clone the repository and install from it as follows::
+
+ $ git clone https://github.com/jmcnamara/XlsxWriter.git
+
+ $ cd XlsxWriter
+ $ sudo python setup.py install
+
+
+Running a sample program
+------------------------
+
+If the installation went correctly you can create a small sample program like
+the following to verify that the module works correctly:
+
+.. code-block:: python
+
+ import xlsxwriter
+
+ workbook = xlsxwriter.Workbook('hello.xlsx')
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Hello world')
+
+ workbook.close()
+
+Save this to a file called ``hello.py`` and run it as follows::
+
+ $ python hello.py
+
+This will output a file called ``hello.xlsx`` which should look something like
+the following:
+
+.. image:: _images/hello01.png
+
+If you downloaded a tarball or cloned the repo, as shown above, you should also
+have a directory called
+`examples <https://github.com/jmcnamara/XlsxWriter/tree/master/examples>`_
+with some sample applications that demonstrate different features of
+XlsxWriter.
+
+
+Documentation
+-------------
+
+The latest version of this document is hosted on
+`Read The Docs <https://xlsxwriter.readthedocs.io>`_. It is also
+available as a
+`PDF <https://github.com/jmcnamara/XlsxWriter/raw/master/docs/XlsxWriter.pdf>`_.
+
+Once you are happy that the module is installed and operational you can have a
+look at the rest of the XlsxWriter documentation. :ref:`tutorial1` is a good
+place to start.
+
+
+
+
+
+
+
+
+
diff --git a/dev/docs/source/index.rst b/dev/docs/source/index.rst
new file mode 100644
index 0000000..0def01b
--- /dev/null
+++ b/dev/docs/source/index.rst
@@ -0,0 +1,112 @@
+Creating Excel files with Python and XlsxWriter
+===============================================
+
+XlsxWriter is a Python module for creating Excel XLSX files.
+
+.. image:: _images/demo.png
+
+.. only:: html
+
+ (:ref:`Sample code to create the above spreadsheet <ex_demo>`.)
+
+ **XlsxWriter**
+
+XlsxWriter is a Python module that can be used to write text, numbers, formulas
+and hyperlinks to multiple worksheets in an Excel 2007+ XLSX file. It supports
+features such as formatting and many more, including:
+
+* 100% compatible Excel XLSX files.
+* Full formatting.
+* Merged cells.
+* Defined names.
+* Charts.
+* Autofilters.
+* Data validation and drop down lists.
+* Conditional formatting.
+* Worksheet PNG/JPEG images.
+* Rich multi-format strings.
+* Cell comments.
+* Textboxes.
+* Integration with Pandas.
+* Memory optimization mode for writing large files.
+
+It supports Python 2.5, 2.6, 2.7, 3.1, 3.2, 3.3, 3.4, 3.5, Jython and PyPy and
+uses standard libraries only.
+
+.. only:: html
+
+ This document explains how to use the XlsxWriter module. See the following
+ sections for more information, or jump straight to the :ref:`intro`.
+
+ .. toctree::
+ :maxdepth: 1
+
+ contents.rst
+
+.. toctree::
+ :maxdepth: 1
+
+ introduction.rst
+ getting_started.rst
+
+.. toctree::
+ :maxdepth: 1
+
+ tutorial01.rst
+ tutorial02.rst
+ tutorial03.rst
+
+.. toctree::
+ :maxdepth: 1
+
+ workbook.rst
+ worksheet.rst
+ page_setup.rst
+ format.rst
+ chart.rst
+ chartsheet.rst
+
+.. toctree::
+ :maxdepth: 1
+
+ working_with_cell_notation.rst
+ working_with_formulas.rst
+ working_with_dates_and_time.rst
+ working_with_colors.rst
+ working_with_charts.rst
+ working_with_autofilters.rst
+ working_with_data_validation.rst
+ working_with_conditional_formats.rst
+ working_with_tables.rst
+ working_with_textboxes.rst
+ working_with_sparklines.rst
+ working_with_cell_comments.rst
+ working_with_outlines.rst
+ working_with_memory.rst
+ working_with_macros.rst
+ working_with_pandas.rst
+
+.. toctree::
+ :maxdepth: 1
+
+ examples.rst
+
+.. toctree::
+ :maxdepth: 1
+
+ chart_examples.rst
+
+.. toctree::
+ :maxdepth: 1
+
+ pandas_examples.rst
+
+.. toctree::
+ :maxdepth: 1
+
+ alternatives.rst
+ bugs.rst
+ faq.rst
+ changes.rst
+ author.rst
+ license.rst
diff --git a/dev/docs/source/introduction.rst b/dev/docs/source/introduction.rst
new file mode 100644
index 0000000..f567cc1
--- /dev/null
+++ b/dev/docs/source/introduction.rst
@@ -0,0 +1,36 @@
+.. _intro:
+
+Introduction
+============
+
+**XlsxWriter** is a Python module for writing files in the Excel 2007+ XLSX
+file format.
+
+It can be used to write text, numbers, and formulas to multiple worksheets and
+it supports features such as formatting, images, charts, page setup,
+autofilters, conditional formatting and many others.
+
+XlsxWriter has some advantages and disadvantages over the
+:ref:`alternative Python modules <alternatives>` for writing Excel files.
+
+* Advantages:
+
+ * It supports more Excel features than any of the alternative modules.
+
+ * It has a high degree of fidelity with files produced by Excel. In most
+ cases the files produced are 100% equivalent to files produced by Excel.
+
+ * It has extensive documentation, example files and tests.
+
+ * It is fast and can be configured to use very little memory even for very
+ large output files.
+
+* Disadvantages:
+
+ * It cannot read or modify existing Excel XLSX files.
+
+
+XlsxWriter is licensed under a BSD :ref:`License` and the source code is
+available on `GitHub <http://github.com/jmcnamara/XlsxWriter>`_.
+
+To try out the module see the next section on :ref:`getting_started`.
diff --git a/dev/docs/source/license.rst b/dev/docs/source/license.rst
new file mode 100644
index 0000000..cf9c980
--- /dev/null
+++ b/dev/docs/source/license.rst
@@ -0,0 +1,9 @@
+.. _license:
+
+License
+=======
+
+XlsxWriter is released under a BSD license.
+
+
+.. include:: ../../../LICENSE.txt
diff --git a/dev/docs/source/page_setup.rst b/dev/docs/source/page_setup.rst
new file mode 100644
index 0000000..90a38f4
--- /dev/null
+++ b/dev/docs/source/page_setup.rst
@@ -0,0 +1,726 @@
+.. _page_setup:
+
+The Worksheet Class (Page Setup)
+================================
+
+Page set-up methods affect the way that a worksheet looks when it is printed.
+They control features such as paper size, orientation, page headers and
+margins.
+
+These methods are really just standard :ref:`worksheet <worksheet>` methods.
+They are documented separately for the sake of clarity.
+
+
+worksheet.set_landscape()
+-------------------------
+
+.. py:function:: set_landscape()
+
+ Set the page orientation as landscape.
+
+This method is used to set the orientation of a worksheet's printed page to
+landscape::
+
+ worksheet.set_landscape()
+
+
+worksheet.set_portrait()
+------------------------
+
+.. py:function:: set_portrait()
+
+ Set the page orientation as portrait.
+
+This method is used to set the orientation of a worksheet's printed page to
+portrait. The default worksheet orientation is portrait, so you won't
+generally need to call this method::
+
+ worksheet.set_portrait()
+
+
+worksheet.set_page_view()
+-------------------------
+
+.. py:function:: set_page_view()
+
+ Set the page view mode.
+
+This method is used to display the worksheet in "Page View/Layout" mode::
+
+ worksheet.set_page_view()
+
+
+worksheet.set_paper()
+---------------------
+
+.. py:function:: set_paper(index)
+
+ Set the paper type.
+
+ :param int index: The Excel paper format index.
+
+This method is used to set the paper format for the printed output of a
+worksheet. The following paper styles are available:
+
++-------+----------------------+-------------------+
+| Index | Paper format | Paper size |
++=======+======================+===================+
+| 0 | Printer default | Printer default |
++-------+----------------------+-------------------+
+| 1 | Letter | 8 1/2 x 11 in |
++-------+----------------------+-------------------+
+| 2 | Letter Small | 8 1/2 x 11 in |
++-------+----------------------+-------------------+
+| 3 | Tabloid | 11 x 17 in |
++-------+----------------------+-------------------+
+| 4 | Ledger | 17 x 11 in |
++-------+----------------------+-------------------+
+| 5 | Legal | 8 1/2 x 14 in |
++-------+----------------------+-------------------+
+| 6 | Statement | 5 1/2 x 8 1/2 in |
++-------+----------------------+-------------------+
+| 7 | Executive | 7 1/4 x 10 1/2 in |
++-------+----------------------+-------------------+
+| 8 | A3 | 297 x 420 mm |
++-------+----------------------+-------------------+
+| 9 | A4 | 210 x 297 mm |
++-------+----------------------+-------------------+
+| 10 | A4 Small | 210 x 297 mm |
++-------+----------------------+-------------------+
+| 11 | A5 | 148 x 210 mm |
++-------+----------------------+-------------------+
+| 12 | B4 | 250 x 354 mm |
++-------+----------------------+-------------------+
+| 13 | B5 | 182 x 257 mm |
++-------+----------------------+-------------------+
+| 14 | Folio | 8 1/2 x 13 in |
++-------+----------------------+-------------------+
+| 15 | Quarto | 215 x 275 mm |
++-------+----------------------+-------------------+
+| 16 | --- | 10x14 in |
++-------+----------------------+-------------------+
+| 17 | --- | 11x17 in |
++-------+----------------------+-------------------+
+| 18 | Note | 8 1/2 x 11 in |
++-------+----------------------+-------------------+
+| 19 | Envelope 9 | 3 7/8 x 8 7/8 |
++-------+----------------------+-------------------+
+| 20 | Envelope 10 | 4 1/8 x 9 1/2 |
++-------+----------------------+-------------------+
+| 21 | Envelope 11 | 4 1/2 x 10 3/8 |
++-------+----------------------+-------------------+
+| 22 | Envelope 12 | 4 3/4 x 11 |
++-------+----------------------+-------------------+
+| 23 | Envelope 14 | 5 x 11 1/2 |
++-------+----------------------+-------------------+
+| 24 | C size sheet | --- |
++-------+----------------------+-------------------+
+| 25 | D size sheet | --- |
++-------+----------------------+-------------------+
+| 26 | E size sheet | --- |
++-------+----------------------+-------------------+
+| 27 | Envelope DL | 110 x 220 mm |
++-------+----------------------+-------------------+
+| 28 | Envelope C3 | 324 x 458 mm |
++-------+----------------------+-------------------+
+| 29 | Envelope C4 | 229 x 324 mm |
++-------+----------------------+-------------------+
+| 30 | Envelope C5 | 162 x 229 mm |
++-------+----------------------+-------------------+
+| 31 | Envelope C6 | 114 x 162 mm |
++-------+----------------------+-------------------+
+| 32 | Envelope C65 | 114 x 229 mm |
++-------+----------------------+-------------------+
+| 33 | Envelope B4 | 250 x 353 mm |
++-------+----------------------+-------------------+
+| 34 | Envelope B5 | 176 x 250 mm |
++-------+----------------------+-------------------+
+| 35 | Envelope B6 | 176 x 125 mm |
++-------+----------------------+-------------------+
+| 36 | Envelope | 110 x 230 mm |
++-------+----------------------+-------------------+
+| 37 | Monarch | 3.875 x 7.5 in |
++-------+----------------------+-------------------+
+| 38 | Envelope | 3 5/8 x 6 1/2 in |
++-------+----------------------+-------------------+
+| 39 | Fanfold | 14 7/8 x 11 in |
++-------+----------------------+-------------------+
+| 40 | German Std Fanfold | 8 1/2 x 12 in |
++-------+----------------------+-------------------+
+| 41 | German Legal Fanfold | 8 1/2 x 13 in |
++-------+----------------------+-------------------+
+
+
+Note, it is likely that not all of these paper types will be available to the
+end user since it will depend on the paper formats that the user's printer
+supports. Therefore, it is best to stick to standard paper types::
+
+ worksheet.set_paper(1) # US Letter
+ worksheet.set_paper(9) # A4
+
+If you do not specify a paper type the worksheet will print using the printer's
+default paper style.
+
+
+worksheet.center_horizontally()
+-------------------------------
+
+.. py:function:: center_horizontally()
+
+ Center the printed page horizontally.
+
+Center the worksheet data horizontally between the margins on the printed page::
+
+ worksheet.center_horizontally()
+
+
+worksheet.center_vertically()
+-----------------------------
+
+.. py:function:: center_vertically()
+
+ Center the printed page vertically.
+
+Center the worksheet data vertically between the margins on the printed page::
+
+ worksheet.center_vertically()
+
+worksheet.set_margins()
+-----------------------
+
+.. py:function:: set_margins([left=0.7,] right=0.7,] top=0.75,] bottom=0.75]]])
+
+ Set the worksheet margins for the printed page.
+
+ :param float left: Left margin in inches. Default 0.7.
+ :param float right: Right margin in inches. Default 0.7.
+ :param float top: Top margin in inches. Default 0.75.
+ :param float bottom: Bottom margin in inches. Default 0.75.
+
+
+The ``set_margins()`` method is used to set the margins of the worksheet when
+it is printed. The units are in inches. All parameters are optional and have
+default values corresponding to the default Excel values.
+
+
+worksheet.set_header()
+----------------------
+
+.. py:function:: set_header([header='',] options]])
+
+ Set the printed page header caption and options.
+
+ :param string header: Header string with Excel control characters.
+ :param dict options: Header options.
+
+Headers and footers are generated using a string which is a combination of
+plain text and control characters.
+
+The available control character are:
+
++---------------+---------------+-----------------------+
+| Control | Category | Description |
++===============+===============+=======================+
+| &L | Justification | Left |
++---------------+---------------+-----------------------+
+| &C | | Center |
++---------------+---------------+-----------------------+
+| &R | | Right |
++---------------+---------------+-----------------------+
+| &P | Information | Page number |
++---------------+---------------+-----------------------+
+| &N | | Total number of pages |
++---------------+---------------+-----------------------+
+| &D | | Date |
++---------------+---------------+-----------------------+
+| &T | | Time |
++---------------+---------------+-----------------------+
+| &F | | File name |
++---------------+---------------+-----------------------+
+| &A | | Worksheet name |
++---------------+---------------+-----------------------+
+| &Z | | Workbook path |
++---------------+---------------+-----------------------+
+| &fontsize | Font | Font size |
++---------------+---------------+-----------------------+
+| &"font,style" | | Font name and style |
++---------------+---------------+-----------------------+
+| &U | | Single underline |
++---------------+---------------+-----------------------+
+| &E | | Double underline |
++---------------+---------------+-----------------------+
+| &S | | Strikethrough |
++---------------+---------------+-----------------------+
+| &X | | Superscript |
++---------------+---------------+-----------------------+
+| &Y | | Subscript |
++---------------+---------------+-----------------------+
+| &[Picture] | Images | Image placeholder |
++---------------+---------------+-----------------------+
+| &G | | Same as &[Picture] |
++---------------+---------------+-----------------------+
+
+
+Text in headers and footers can be justified (aligned) to the left, center and
+right by prefixing the text with the control characters ``&L``, ``&C`` and
+``&R``.
+
+For example::
+
+ worksheet.set_header('&LHello')
+
+ ---------------------------------------------------------------
+ | |
+ | Hello |
+ | |
+
+
+ $worksheet->set_header('&CHello');
+
+ ---------------------------------------------------------------
+ | |
+ | Hello |
+ | |
+
+
+ $worksheet->set_header('&RHello');
+
+ ---------------------------------------------------------------
+ | |
+ | Hello |
+ | |
+
+
+For simple text, if you do not specify any justification the text will be
+centered. However, you must prefix the text with ``&C`` if you specify a font
+name or any other formatting::
+
+ worksheet.set_header('Hello')
+
+ ---------------------------------------------------------------
+ | |
+ | Hello |
+ | |
+
+You can have text in each of the justification regions::
+
+ worksheet.set_header('&LCiao&CBello&RCielo')
+
+ ---------------------------------------------------------------
+ | |
+ | Ciao Bello Cielo |
+ | |
+
+
+The information control characters act as variables that Excel will update as
+the workbook or worksheet changes. Times and dates are in the users default
+format::
+
+ worksheet.set_header('&CPage &P of &N')
+
+ ---------------------------------------------------------------
+ | |
+ | Page 1 of 6 |
+ | |
+
+ worksheet.set_header('&CUpdated at &T')
+
+ ---------------------------------------------------------------
+ | |
+ | Updated at 12:30 PM |
+ | |
+
+Images can be inserted using the ``options`` shown below. Each image must
+have a placeholder in header string using the ``&[Picture]`` or ``&G``
+control characters::
+
+ worksheet.set_header('&L&G', {'image_left': 'logo.jpg'})
+
+.. image:: _images/header_image.png
+
+
+You can specify the font size of a section of the text by prefixing it with the
+control character ``&n`` where ``n`` is the font size::
+
+ worksheet1.set_header('&C&30Hello Big')
+ worksheet2.set_header('&C&10Hello Small')
+
+You can specify the font of a section of the text by prefixing it with the
+control sequence ``&"font,style"`` where ``fontname`` is a font name such as
+"Courier New" or "Times New Roman" and ``style`` is one of the standard
+Windows font descriptions: "Regular", "Italic", "Bold" or "Bold Italic"::
+
+ worksheet1.set_header('&C&"Courier New,Italic"Hello')
+ worksheet2.set_header('&C&"Courier New,Bold Italic"Hello')
+ worksheet3.set_header('&C&"Times New Roman,Regular"Hello')
+
+It is possible to combine all of these features together to create
+sophisticated headers and footers. As an aid to setting up complicated headers
+and footers you can record a page set-up as a macro in Excel and look at the
+format strings that VBA produces. Remember however that VBA uses two double
+quotes ``""`` to indicate a single double quote. For the last example above
+the equivalent VBA code looks like this::
+
+ .LeftHeader = ""
+ .CenterHeader = "&""Times New Roman,Regular""Hello"
+ .RightHeader = ""
+
+Alternatively you can inspect the header and footer strings in an Excel file
+by unzipping it and grepping the XML sub-files. The following shows how to do
+that using `libxml's xmllint <http://xmlsoft.org/xmllint.html>`_ to format the
+XML for clarity::
+
+ $ unzip myfile.xlsm -d myfile
+ $ xmllint --format `find myfile -name "*.xml" | xargs` | egrep "Header|Footer"
+
+ <headerFooter scaleWithDoc="0">
+ <oddHeader>&L&P</oddHeader>
+ </headerFooter>
+
+Note that in this case you need to unescape the Html. In the above example the
+header string would be::
+
+ '&L&P'
+
+
+To include a single literal ampersand ``&`` in a header or footer you should
+use a double ampersand ``&&``::
+
+ worksheet1.set_header('&CCuriouser && Curiouser - Attorneys at Law')
+
+The available options are:
+
+* ``margin``: (float) Header margin in inches. Defaults to 0.3 inch.
+* ``image_left``: (string) The path to the image. Needs ``&G`` placeholder.
+* ``image_center``: (string) Same as above.
+* ``image_right``: (string) Same as above.
+* ``image_data_left``: (BytesIO) A byte stream of the image data.
+* ``image_data_center``: (BytesIO) Same as above.
+* ``image_data_right``: (BytesIO) Same as above.
+* ``scale_with_doc``: (boolean) Scale header with document. Defaults to True.
+* ``align_with_margins``: (boolean) Align header to margins. Defaults to True.
+
+As with the other margins the ``margin`` value should be in inches. The
+default header and footer margin is 0.3 inch. It can be changed as follows::
+
+ worksheet.set_header('&CHello', {'margin': 0.75})
+
+The header and footer margins are independent of, and should not be confused
+with, the top and bottom worksheet margins.
+
+The image options must have an accompanying ``&[Picture]`` or ``&G`` control
+character in the header string::
+
+ worksheet.set_header('&L&[Picture]&C&[Picture]&R&[Picture]',
+ {'image_left': 'red.jpg',
+ 'image_center': 'blue.jpg',
+ 'image_right': 'yellow.jpg'})
+
+
+The ``image_data_`` parameters are used to add an in-memory byte stream in
+:class:`io.BytesIO` format::
+
+ image_file = open('logo.jpg', 'rb')
+ image_data = BytesIO(image_file.read())
+
+ worksheet.set_header('&L&G',
+ {'image_left': 'logo.jpg',
+ 'image_data_left': image_data})
+
+When using the ``image_data_`` parameters a filename must still be passed to
+to the equivalent ``image_`` parameter since it is required by Excel. See also
+:func:`insert_image` for details on handling images from byte streams.
+
+Note, Excel does not allow header or footer strings longer than 255 characters,
+including control characters. Strings longer than this will not be written
+and an exception will be thrown.
+
+See also :ref:`ex_headers_footers`.
+
+worksheet.set_footer()
+----------------------
+
+.. py:function:: set_footer([footer='',] options]])
+
+ Set the printed page footer caption and options.
+
+ :param string footer: Footer string with Excel control characters.
+ :param dict options: Footer options.
+
+The syntax of the ``set_footer()`` method is the same as :func:`set_header`.
+
+
+worksheet.repeat_rows()
+-----------------------
+
+.. py:function:: repeat_rows(first_row[, last_row])
+
+ Set the number of rows to repeat at the top of each printed page.
+
+ :param int first_row: First row of repeat range.
+ :param int last_row: Last row of repeat range. Optional.
+
+For large Excel documents it is often desirable to have the first row or rows
+of the worksheet print out at the top of each page.
+
+This can be achieved by using the ``repeat_rows()`` method. The parameters
+``first_row`` and ``last_row`` are zero based. The ``last_row`` parameter is
+optional if you only wish to specify one row::
+
+ worksheet1.repeat_rows(0) # Repeat the first row.
+ worksheet2.repeat_rows(0, 1) # Repeat the first two rows.
+
+
+worksheet.repeat_columns()
+--------------------------
+
+.. py:function:: repeat_columns(first_col[, last_col])
+
+ Set the columns to repeat at the left hand side of each printed page.
+
+ :param int first_col: First column of repeat range.
+ :param int last_col: Last column of repeat range. Optional.
+
+For large Excel documents it is often desirable to have the first column or
+columns of the worksheet print out at the left hand side of each page.
+
+This can be achieved by using the ``repeat_columns()`` method. The parameters
+``first_column`` and ``last_column`` are zero based. The ``last_column``
+parameter is optional if you only wish to specify one column. You can also
+specify the columns using A1 column notation, see :ref:`cell_notation` for
+more details.::
+
+ worksheet1.repeat_columns(0) # Repeat the first column.
+ worksheet2.repeat_columns(0, 1) # Repeat the first two columns.
+ worksheet3.repeat_columns('A:A') # Repeat the first column.
+ worksheet4.repeat_columns('A:B') # Repeat the first two columns.
+
+
+worksheet.hide_gridlines()
+--------------------------
+
+.. py:function:: hide_gridlines([option=1])
+
+ Set the option to hide gridlines on the screen and the printed page.
+
+ :param int option: Hide gridline options. See below.
+
+This method is used to hide the gridlines on the screen and printed page.
+Gridlines are the lines that divide the cells on a worksheet. Screen and
+printed gridlines are turned on by default in an Excel worksheet.
+
+If you have defined your own cell borders you may wish to hide the default
+gridlines::
+
+ worksheet.hide_gridlines()
+
+The following values of ``option`` are valid:
+
+0. Don't hide gridlines.
+1. Hide printed gridlines only.
+2. Hide screen and printed gridlines.
+
+If you don't supply an argument the default option is 1, i.e. only the printed
+gridlines are hidden.
+
+
+worksheet.print_row_col_headers()
+---------------------------------
+
+.. py:function:: print_row_col_headers()
+
+ Set the option to print the row and column headers on the printed page.
+
+When you print a worksheet from Excel you get the data selected in the print
+area. By default the Excel row and column headers (the row numbers on the left
+and the column letters at the top) aren't printed.
+
+The ``print_row_col_headers()`` method sets the printer option to print these
+headers::
+
+ worksheet.print_row_col_headers()
+
+worksheet.print_area()
+----------------------
+
+.. py:function:: print_area(first_row, first_col, last_row, last_col)
+
+ Set the print area in the current worksheet.
+
+ :param first_row: The first row of the range. (All zero indexed.)
+ :param first_col: The first column of the range.
+ :param last_row: The last row of the range.
+ :param last_col: The last col of the range.
+ :type first_row: integer
+ :type first_col: integer
+ :type last_row: integer
+ :type last_col: integer
+
+This method is used to specify the area of the worksheet that will be printed.
+
+All four parameters must be specified. You can also use A1 notation, see
+:ref:`cell_notation`::
+
+ worksheet1.print_area('A1:H20') # Cells A1 to H20.
+ worksheet2.print_area(0, 0, 19, 7) # The same as above.
+
+In order to set a row or column range you must specify the entire range::
+
+ worksheet3.print_area('A1:H1048576') # Same as A:H.
+
+
+worksheet.print_across()
+------------------------
+
+.. py:function:: print_across()
+
+ Set the order in which pages are printed.
+
+The ``print_across`` method is used to change the default print direction. This
+is referred to by Excel as the sheet "page order"::
+
+ worksheet.print_across()
+
+The default page order is shown below for a worksheet that extends over 4
+pages. The order is called "down then across"::
+
+ [1] [3]
+ [2] [4]
+
+However, by using the ``print_across`` method the print order will be changed
+to "across then down"::
+
+ [1] [2]
+ [3] [4]
+
+worksheet.fit_to_pages()
+------------------------
+
+.. py:function:: fit_to_pages(width, height)
+
+ Fit the printed area to a specific number of pages both vertically and
+ horizontally.
+
+ :param int width: Number of pages horizontally.
+ :param int height: Number of pages vertically.
+
+The ``fit_to_pages()`` method is used to fit the printed area to a specific
+number of pages both vertically and horizontally. If the printed area exceeds
+the specified number of pages it will be scaled down to fit. This ensures that
+the printed area will always appear on the specified number of pages even if
+the page size or margins change::
+
+ worksheet1.fit_to_pages(1, 1) # Fit to 1x1 pages.
+ worksheet2.fit_to_pages(2, 1) # Fit to 2x1 pages.
+ worksheet3.fit_to_pages(1, 2) # Fit to 1x2 pages.
+
+The print area can be defined using the ``print_area()`` method as described
+above.
+
+A common requirement is to fit the printed output to ``n`` pages wide but have
+the height be as long as necessary. To achieve this set the ``height`` to
+zero::
+
+ worksheet1.fit_to_pages(1, 0) # 1 page wide and as long as necessary.
+
+.. Note::
+ Although it is valid to use both :func:`fit_to_pages()` and
+ :func:`set_print_scale()` on the same worksheet in Excel only allows one of
+ these options to be active at a time. The last method call made will set
+ the active option.
+
+.. Note::
+ The :func:`fit_to_pages()` will override any manual page breaks that are
+ defined in the worksheet.
+
+.. Note::
+ When using :func:`fit_to_pages()` it may also be required to set the
+ printer paper size using :func:`set_paper()` or else Excel will default
+ to "US Letter".
+
+
+worksheet.set_start_page()
+--------------------------
+
+.. py:function:: set_start_page()
+
+ Set the start page number when printing.
+
+ :param int start_page: Starting page number.
+
+The ``set_start_page()`` method is used to set the number of the starting page
+when the worksheet is printed out::
+
+ # Start print from page 2.
+ worksheet.set_start_page(2)
+
+worksheet.set_print_scale()
+---------------------------
+
+.. py:function:: set_print_scale()
+
+ Set the scale factor for the printed page.
+
+ :param int scale: Print scale of worksheet to be printed.
+
+Set the scale factor of the printed page. Scale factors in the range
+``10 <= $scale <= 400`` are valid::
+
+ worksheet1.set_print_scale(50)
+ worksheet2.set_print_scale(75)
+ worksheet3.set_print_scale(300)
+ worksheet4.set_print_scale(400)
+
+The default scale factor is 100. Note, ``set_print_scale()`` does not affect
+the scale of the visible page in Excel. For that you should use
+:func:`set_zoom()`.
+
+Note also that although it is valid to use both ``fit_to_pages()`` and
+``set_print_scale()`` on the same worksheet Excel only allows one of these
+options to be active at a time. The last method call made will set the active
+option.
+
+
+worksheet.set_h_pagebreaks()
+----------------------------
+
+.. py:function:: set_h_pagebreaks(breaks)
+
+ Set the horizontal page breaks on a worksheet.
+
+ :param list breaks: List of page break rows.
+
+The ``set_h_pagebreaks()`` method adds horizontal page breaks to a worksheet. A
+page break causes all the data that follows it to be printed on the next page.
+Horizontal page breaks act between rows.
+
+The ``set_h_pagebreaks()`` method takes a list of one or more page breaks::
+
+ worksheet1.set_v_pagebreaks([20])
+ worksheet2.set_v_pagebreaks([20, 40, 60, 80, 100])
+
+To create a page break between rows 20 and 21 you must specify the break at row
+21. However in zero index notation this is actually row 20. So you can pretend
+for a small while that you are using 1 index notation::
+
+ worksheet.set_h_pagebreaks([20]) # Break between row 20 and 21.
+
+.. Note::
+ Note: If you specify the "fit to page" option via the ``fit_to_pages()``
+ method it will override all manual page breaks.
+
+There is a silent limitation of 1023 horizontal page breaks per worksheet in
+line with an Excel internal limitation.
+
+
+worksheet.set_v_pagebreaks()
+----------------------------
+
+.. py:function:: set_v_pagebreaks(breaks)
+
+ Set the vertical page breaks on a worksheet.
+
+ :param list breaks: List of page break columns.
+
+The ``set_v_pagebreaks()`` method is the same as the above
+:func:`set_h_pagebreaks()` method except it adds page breaks between columns.
diff --git a/dev/docs/source/pandas_examples.rst b/dev/docs/source/pandas_examples.rst
new file mode 100644
index 0000000..e6ac0da
--- /dev/null
+++ b/dev/docs/source/pandas_examples.rst
@@ -0,0 +1,25 @@
+.. _pandas_examples:
+
+Pandas with XlsxWriter Examples
+===============================
+
+The following are some of the examples included in the
+`examples <https://github.com/jmcnamara/XlsxWriter/tree/master/examples>`_
+directory of the XlsxWriter distribution.
+
+They show how to use XlsxWriter with `Pandas <http://pandas.pydata.org/>`_.
+
+.. toctree::
+ :maxdepth: 1
+
+ example_pandas_simple.rst
+ example_pandas_multiple.rst
+ example_pandas_positioning.rst
+ example_pandas_chart.rst
+ example_pandas_conditional.rst
+ example_pandas_datetime.rst
+ example_pandas_column_formats.rst
+ example_pandas_chart_line.rst
+ example_pandas_chart_stock.rst
+ example_pandas_chart_columns.rst
+
diff --git a/dev/docs/source/tutorial01.rst b/dev/docs/source/tutorial01.rst
new file mode 100644
index 0000000..0bbcc2b
--- /dev/null
+++ b/dev/docs/source/tutorial01.rst
@@ -0,0 +1,119 @@
+.. _tutorial1:
+
+Tutorial 1: Create a simple XLSX file
+=====================================
+
+.. highlight:: python
+
+Let's start by creating a simple spreadsheet using Python and the XlsxWriter
+module.
+
+Say that we have some data on monthly outgoings that we want to convert into an
+Excel XLSX file::
+
+ expenses = (
+ ['Rent', 1000],
+ ['Gas', 100],
+ ['Food', 300],
+ ['Gym', 50],
+ )
+
+To do that we can start with a small program like the following:
+
+.. code-block:: python
+
+ import xlsxwriter
+
+ # Create a workbook and add a worksheet.
+ workbook = xlsxwriter.Workbook('Expenses01.xlsx')
+ worksheet = workbook.add_worksheet()
+
+ # Some data we want to write to the worksheet.
+ expenses = (
+ ['Rent', 1000],
+ ['Gas', 100],
+ ['Food', 300],
+ ['Gym', 50],
+ )
+
+ # Start from the first cell. Rows and columns are zero indexed.
+ row = 0
+ col = 0
+
+ # Iterate over the data and write it out row by row.
+ for item, cost in (expenses):
+ worksheet.write(row, col, item)
+ worksheet.write(row, col + 1, cost)
+ row += 1
+
+ # Write a total using a formula.
+ worksheet.write(row, 0, 'Total')
+ worksheet.write(row, 1, '=SUM(B1:B4)')
+
+ workbook.close()
+
+If we run this program we should get a spreadsheet that looks like this:
+
+.. image:: _images/tutorial01.png
+
+This is a simple example but the steps involved are representative of all
+programs that use XlsxWriter, so let's break it down into separate parts.
+
+The first step is to import the module::
+
+ import xlsxwriter
+
+The next step is to create a new workbook object using the ``Workbook()``
+constructor.
+
+:func:`Workbook` takes one, non-optional, argument which is the filename that
+we want to create::
+
+ workbook = xlsxwriter.Workbook('Expenses01.xlsx')
+
+.. note::
+ XlsxWriter can only create *new files*. It cannot read or modify existing
+ files.
+
+The workbook object is then used to add a new worksheet via the
+:func:`add_worksheet` method::
+
+ worksheet = workbook.add_worksheet()
+
+By default worksheet names in the spreadsheet will be `Sheet1`, `Sheet2` etc.,
+but we can also specify a name::
+
+ worksheet1 = workbook.add_worksheet() # Defaults to Sheet1.
+ worksheet2 = workbook.add_worksheet('Data') # Data.
+ worksheet3 = workbook.add_worksheet() # Defaults to Sheet3.
+
+We can then use the worksheet object to write data via the :func:`write`
+method::
+
+ worksheet.write(row, col, some_data)
+
+.. Note::
+ Throughout XlsxWriter, *rows* and *columns* are zero indexed. The
+ first cell in a worksheet, ``A1``, is ``(0, 0)``.
+
+So in our example we iterate over our data and write it out as follows::
+
+ # Iterate over the data and write it out row by row.
+ for item, cost in (expenses):
+ worksheet.write(row, col, item)
+ worksheet.write(row, col + 1, cost)
+ row += 1
+
+We then add a formula to calculate the total of the items in the second column::
+
+ worksheet.write(row, 1, '=SUM(B1:B4)')
+
+Finally, we close the Excel file via the :func:`close` method::
+
+ workbook.close()
+
+And that's it. We now have a file that can be read by Excel and other
+spreadsheet applications.
+
+In the next sections we will see how we can use the XlsxWriter module to add
+formatting and other Excel features.
diff --git a/dev/docs/source/tutorial02.rst b/dev/docs/source/tutorial02.rst
new file mode 100644
index 0000000..6b43698
--- /dev/null
+++ b/dev/docs/source/tutorial02.rst
@@ -0,0 +1,115 @@
+.. _tutorial2:
+
+Tutorial 2: Adding formatting to the XLSX File
+==============================================
+
+.. highlight:: python
+
+In the previous section we created a simple spreadsheet using Python and the
+XlsxWriter module.
+
+This converted the required data into an Excel file but it looked a little
+bare. In order to make the information clearer we would like to add some
+simple formatting, like this:
+
+.. image:: _images/tutorial02.png
+
+The differences here are that we have added **Item** and **Cost** column
+headers in a bold font, we have formatted the currency in the second column
+and we have made the **Total** string bold.
+
+To do this we can extend our program as follows:
+
+.. only:: html
+
+ (The significant changes are shown with a red line.)
+
+.. code-block:: python
+ :emphasize-lines: 7-15, 32, 36-37
+
+ import xlsxwriter
+
+ # Create a workbook and add a worksheet.
+ workbook = xlsxwriter.Workbook('Expenses02.xlsx')
+ worksheet = workbook.add_worksheet()
+
+ # Add a bold format to use to highlight cells.
+ bold = workbook.add_format({'bold': True})
+
+ # Add a number format for cells with money.
+ money = workbook.add_format({'num_format': '$#,##0'})
+
+ # Write some data headers.
+ worksheet.write('A1', 'Item', bold)
+ worksheet.write('B1', 'Cost', bold)
+
+ # Some data we want to write to the worksheet.
+ expenses = (
+ ['Rent', 1000],
+ ['Gas', 100],
+ ['Food', 300],
+ ['Gym', 50],
+ )
+
+ # Start from the first cell below the headers.
+ row = 1
+ col = 0
+
+ # Iterate over the data and write it out row by row.
+ for item, cost in (expenses):
+ worksheet.write(row, col, item)
+ worksheet.write(row, col + 1, cost, money)
+ row += 1
+
+ # Write a total using a formula.
+ worksheet.write(row, 0, 'Total', bold)
+ worksheet.write(row, 1, '=SUM(B2:B5)', money)
+
+ workbook.close()
+
+The main difference between this and the previous program is that we have added
+two :ref:`Format <Format>` objects that we can use to format cells in the
+spreadsheet.
+
+Format objects represent all of the formatting properties that can be applied
+to a cell in Excel such as fonts, number formatting, colors and borders. This
+is explained in more detail in :ref:`format` section.
+
+For now we will avoid getting into the details and just use a limited amount of
+the format functionality to add some simple formatting::
+
+ # Add a bold format to use to highlight cells.
+ bold = workbook.add_format({'bold': True})
+
+ # Add a number format for cells with money.
+ money = workbook.add_format({'num_format': '$#,##0'})
+
+We can then pass these formats as an optional third parameter to the
+:ref:`worksheet. <Worksheet>`:func:`write()` method to format the data in the
+cell::
+
+ write(row, column, token, [format])
+
+Like this::
+
+ worksheet.write(row, 0, 'Total', bold)
+
+Which leads us to another new feature in this program. To add the headers in
+the first row of the worksheet we used :func:`write()` like this::
+
+ worksheet.write('A1', 'Item', bold)
+ worksheet.write('B1', 'Cost', bold)
+
+So, instead of ``(row, col)`` we used the Excel ``'A1'`` style notation. See
+:ref:`cell_notation` for more details but don't be too concerned about it for
+now. It is just a little syntactic sugar to help with laying out worksheets.
+
+In the next section we will look at handling more data types.
+
+
+
+
+
+
+
+
diff --git a/dev/docs/source/tutorial03.rst b/dev/docs/source/tutorial03.rst
new file mode 100644
index 0000000..1101b51
--- /dev/null
+++ b/dev/docs/source/tutorial03.rst
@@ -0,0 +1,152 @@
+.. _tutorial3:
+
+Tutorial 3: Writing different types of data to the XLSX File
+============================================================
+
+.. highlight:: python
+
+In the previous section we created a simple spreadsheet with formatting using
+Python and the XlsxWriter module.
+
+This time let's extend the data we want to write to include some dates::
+
+ expenses = (
+ ['Rent', '2013-01-13', 1000],
+ ['Gas', '2013-01-14', 100],
+ ['Food', '2013-01-16', 300],
+ ['Gym', '2013-01-20', 50],
+ )
+
+The corresponding spreadsheet will look like this:
+
+.. image:: _images/tutorial03.png
+
+The differences here are that we have added a Date column with formatting and
+made that column a little wider to accommodate the dates.
+
+To do this we can extend our program as follows:
+
+.. only:: html
+
+ (The significant changes are shown with a red line.)
+
+.. code-block:: python
+ :emphasize-lines: 1, 15, 18, 27-30, 39-43
+
+ from datetime import datetime
+ import xlsxwriter
+
+ # Create a workbook and add a worksheet.
+ workbook = xlsxwriter.Workbook('Expenses03.xlsx')
+ worksheet = workbook.add_worksheet()
+
+ # Add a bold format to use to highlight cells.
+ bold = workbook.add_format({'bold': 1})
+
+ # Add a number format for cells with money.
+ money_format = workbook.add_format({'num_format': '$#,##0'})
+
+ # Add an Excel date format.
+ date_format = workbook.add_format({'num_format': 'mmmm d yyyy'})
+
+ # Adjust the column width.
+ worksheet.set_column(1, 1, 15)
+
+ # Write some data headers.
+ worksheet.write('A1', 'Item', bold)
+ worksheet.write('B1', 'Date', bold)
+ worksheet.write('C1', 'Cost', bold)
+
+ # Some data we want to write to the worksheet.
+ expenses = (
+ ['Rent', '2013-01-13', 1000],
+ ['Gas', '2013-01-14', 100],
+ ['Food', '2013-01-16', 300],
+ ['Gym', '2013-01-20', 50],
+ )
+
+ # Start from the first cell below the headers.
+ row = 1
+ col = 0
+
+ for item, date_str, cost in (expenses):
+ # Convert the date string into a datetime object.
+ date = datetime.strptime(date_str, "%Y-%m-%d")
+
+ worksheet.write_string (row, col, item )
+ worksheet.write_datetime(row, col + 1, date, date_format )
+ worksheet.write_number (row, col + 2, cost, money_format)
+ row += 1
+
+ # Write a total using a formula.
+ worksheet.write(row, 0, 'Total', bold)
+ worksheet.write(row, 2, '=SUM(C2:C5)', money_format)
+
+ workbook.close()
+
+The main difference between this and the previous program is that we have added
+a new :ref:`Format <Format>` object for dates and we have additional handling
+for data types.
+
+Excel treats different types of input data, such as strings and numbers,
+differently although it generally does it transparently to the user.
+XlsxWriter tries to emulate this in the
+:ref:`worksheet. <Worksheet>`:func:`write()` method by mapping Python data
+types to types that Excel supports.
+
+The ``write()`` method acts as a general alias for several more specific
+methods:
+
+* :func:`write_string()`
+* :func:`write_number()`
+* :func:`write_blank()`
+* :func:`write_formula()`
+* :func:`write_datetime()`
+* :func:`write_boolean()`
+* :func:`write_url()`
+
+In this version of our program we have used some of these explicit ``write_``
+methods for different types of data::
+
+ worksheet.write_string (row, col, item )
+ worksheet.write_datetime(row, col + 1, date, date_format )
+ worksheet.write_number (row, col + 2, cost, money_format)
+
+This is mainly to show that if you need more control over the type of data you
+write to a worksheet you can use the appropriate method. In this simplified
+example the :func:`write()` method would actually have worked just as well.
+
+The handling of dates is also new to our program.
+
+Dates and times in Excel are floating point numbers that have a number format
+applied to display them in the correct format. If the date and time are Python
+:mod:`datetime` objects XlsxWriter makes the required number conversion
+automatically. However, we also need to add the number format to ensure that
+Excel displays it as as date::
+
+ from datetime import datetime
+ ...
+
+ date_format = workbook.add_format({'num_format': 'mmmm d yyyy'})
+ ...
+
+ for item, date_str, cost in (expenses):
+ # Convert the date string into a datetime object.
+ date = datetime.strptime(date_str, "%Y-%m-%d")
+ ...
+ worksheet.write_datetime(row, col + 1, date, date_format )
+ ...
+
+Date handling is explained in more detail in :ref:`working_with_dates_and_time`.
+
+The last addition to our program is the :func:`set_column` method to adjust the
+width of column 'B' so that the dates are more clearly visible::
+
+ # Adjust the column width.
+ worksheet.set_column('B:B', 15)
+
+That completes the tutorial section.
+
+In the next sections we will look at the API in more detail starting with
+:ref:`workbook`.
+
diff --git a/dev/docs/source/workbook.rst b/dev/docs/source/workbook.rst
new file mode 100644
index 0000000..4110e1f
--- /dev/null
+++ b/dev/docs/source/workbook.rst
@@ -0,0 +1,600 @@
+.. _workbook:
+
+The Workbook Class
+==================
+
+The Workbook class is the main class exposed by the XlsxWriter module and it is
+the only class that you will need to instantiate directly.
+
+The Workbook class represents the entire spreadsheet as you see it in Excel and
+internally it represents the Excel file as it is written on disk.
+
+Constructor
+-----------
+
+.. py:function:: Workbook(filename [,options])
+
+ Create a new XlsxWriter Workbook object.
+
+ :param string filename: The name of the new Excel file to create.
+ :param dict options: Optional workbook parameters. See below.
+ :rtype: A Workbook object.
+
+
+The ``Workbook()`` constructor is used to create a new Excel workbook with a
+given filename::
+
+ import xlsxwriter
+
+ workbook = xlsxwriter.Workbook('filename.xlsx')
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write(0, 0, 'Hello Excel')
+
+ workbook.close()
+
+.. image:: _images/workbook01.png
+
+The constructor options are:
+
+* **constant_memory**: Reduces the amount of data stored in memory so that
+ large files can be written efficiently::
+
+ workbook = xlsxwriter.Workbook(filename, {'constant_memory': True})
+
+ Note, in this mode a row of data is written and then discarded when a cell
+ in a new row is added via one of the worksheet ``write_()`` methods.
+ Therefore, once this mode is active, data should be written in sequential
+ row order. For this reason the :func:`add_table()` and :func:`merge_range()`
+ Worksheet methods don't work in this mode.
+
+ See :ref:`memory_perf` for more details.
+
+* **tmpdir**: ``XlsxWriter`` stores workbook data in temporary files prior
+ to assembling the final XLSX file. The temporary files are created in the
+ system's temp directory. If the default temporary directory isn't accessible
+ to your application, or doesn't contain enough space, you can specify an
+ alternative location using the ``tempdir`` option::
+
+ workbook = xlsxwriter.Workbook(filename, {'tmpdir': '/home/user/tmp'})
+
+ The temporary directory must exist and will not be created.
+
+* **in_memory**: To avoid the use of temporary files in the assembly of the
+ final XLSX file, for example on servers that don't allow temp files such as
+ the Google APP Engine, set the ``in_memory`` constructor option to ``True``::
+
+ workbook = xlsxwriter.Workbook(filename, {'in_memory': True})
+
+ This option overrides the ``constant_memory`` option.
+* **strings_to_numbers**: Enable the
+ :ref:`worksheet. <Worksheet>`:func:`write()` method to convert strings to
+ numbers, where possible, using :func:`float()` in order to avoid an Excel
+ warning about "Numbers Stored as Text". The default is ``False``. To enable
+ this option use::
+
+ workbook = xlsxwriter.Workbook(filename, {'strings_to_numbers': True})
+
+* **strings_to_formulas**: Enable the
+ :ref:`worksheet. <Worksheet>`:func:`write()` method to convert strings to
+ formulas. The default is ``True``. To disable this option use::
+
+ workbook = xlsxwriter.Workbook(filename, {'strings_to_formulas': False})
+
+* **strings_to_urls**: Enable the
+ :ref:`worksheet. <Worksheet>`:func:`write()` method to convert strings to
+ urls. The default is ``True``. To disable this option use::
+
+ workbook = xlsxwriter.Workbook(filename, {'strings_to_urls': False})
+
+* **nan_inf_to_errors**: Enable the
+ :ref:`worksheet. <Worksheet>`:func:`write()` and :func:`write_number()`
+ methods to convert ``nan``, ``inf`` and ``-inf`` to Excel errors. Excel
+ doesn't handle NAN/INF as numbers so as a workaround they are mapped to
+ formulas that yield the error codes ``#NUM!`` and ``#DIV/0!``. The default
+ is ``False``. To enable this option use::
+
+ workbook = xlsxwriter.Workbook(filename, {'nan_inf_to_errors': True})
+
+* **default_date_format**: This option is used to specify a default date
+ format string for use with the
+ :ref:`worksheet. <Worksheet>`:func:`write_datetime()` method when an
+ explicit format isn't given. See :ref:`working_with_dates_and_time` for more
+ details::
+
+ xlsxwriter.Workbook(filename, {'default_date_format': 'dd/mm/yy'})
+
+* **remove_timezone**: Excel doesn't support timezones in datetimes/times so
+ there isn't any fail-safe way that XlsxWriter can map a Python timezone aware
+ datetime into an Excel datetime in functions such as
+ :func:`write_datetime`. As such the user should convert and remove the
+ timezones in some way that make sense according to their
+ requirements. Alternatively the ``remove_timezone`` option can be used to
+ strip the timezone from datetime values. The default is ``False``. To enable
+ this option use::
+
+ workbook = xlsxwriter.Workbook(filename, {'remove_timezone': True})
+
+ See also :ref:`Timezone Handling in XlsxWriter <timezone_handling>`.
+
+* **date_1904**: Excel for Windows uses a default epoch of 1900 and Excel for
+ Mac uses an epoch of 1904. However, Excel on either platform will convert
+ automatically between one system and the other. XlsxWriter stores dates in
+ the 1900 format by default. If you wish to change this you can use the
+ ``date_1904`` workbook option. This option is mainly for enhanced
+ compatibility with Excel and in general isn't required very often::
+
+ workbook = xlsxwriter.Workbook(filename, {'date_1904': True})
+
+When specifying a filename it is recommended that you use an ``.xlsx``
+extension or Excel will generate a warning when opening the file.
+
+The ``Workbook()`` method also works using the ``with`` context manager. In
+which case it doesn't need an explicit `close()` statement::
+
+ with xlsxwriter.Workbook('hello_world.xlsx') as workbook:
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Hello world')
+
+It is possible to write files to in-memory strings using StringIO as follows::
+
+ output = StringIO()
+ workbook = xlsxwriter.Workbook(output)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Hello')
+ workbook.close()
+
+ xlsx_data = output.getvalue()
+
+To avoid the use of any temporary files and keep the entire file in-memory use
+the ``in_memory`` constructor option shown above.
+
+See also :ref:`ex_http_server` and :ref:`ex_http_server3`.
+
+
+workbook.add_worksheet()
+------------------------
+
+.. function:: add_worksheet([name])
+
+ Add a new worksheet to a workbook.
+
+ :param string name: Optional worksheet name, defaults to Sheet1, etc.
+ :rtype: A :ref:`worksheet <Worksheet>` object.
+
+The ``add_worksheet()`` method adds a new worksheet to a workbook.
+
+At least one worksheet should be added to a new workbook. The
+:ref:`Worksheet <worksheet>` object is used to write data and configure a
+worksheet in the workbook.
+
+The ``name`` parameter is optional. If it is not specified the default
+Excel convention will be followed, i.e. Sheet1, Sheet2, etc.::
+
+ worksheet1 = workbook.add_worksheet() # Sheet1
+ worksheet2 = workbook.add_worksheet('Foglio2') # Foglio2
+ worksheet3 = workbook.add_worksheet('Data') # Data
+ worksheet4 = workbook.add_worksheet() # Sheet4
+
+.. image:: _images/workbook02.png
+
+The worksheet name must be a valid Excel worksheet name, i.e. it cannot contain
+any of the characters ``' [ ] : * ? / \
+'`` and it must be less than 32 characters.
+
+In addition, you cannot use the same, case insensitive, ``name`` for more
+than one worksheet.
+
+workbook.add_format()
+---------------------
+
+.. py:function:: add_format([properties])
+
+ Create a new Format object to formats cells in worksheets.
+
+ :param dictionary properties: An optional dictionary of format properties.
+ :rtype: A :ref:`format <Format>` object.
+
+The ``add_format()`` method can be used to create new :ref:`Format <Format>`
+objects which are used to apply formatting to a cell. You can either define
+the properties at creation time via a dictionary of property values or later
+via method calls::
+
+ format1 = workbook.add_format(props) # Set properties at creation.
+ format2 = workbook.add_format() # Set properties later.
+
+See the :ref:`format` section for more details about Format properties and how
+to set them.
+
+
+workbook.add_chart()
+--------------------
+
+.. py:function:: add_chart(options)
+
+ Create a chart object that can be added to a worksheet.
+
+ :param dictionary options: An dictionary of chart type options.
+ :rtype: A :ref:`Chart <chart_class>` object.
+
+This method is use to create a new chart object that can be inserted into a
+worksheet via the :func:`insert_chart()` Worksheet method::
+
+ chart = workbook.add_chart({'type': 'column'})
+
+The properties that can be set are::
+
+ type (required)
+ subtype (optional)
+
+* ``type``
+
+ This is a required parameter. It defines the type of chart that will be
+ created::
+
+ chart = workbook.add_chart({'type': 'line'})
+
+ The available types are::
+
+ area
+ bar
+ column
+ doughnut
+ line
+ pie
+ radar
+ scatter
+ stock
+
+* ``subtype``
+
+ Used to define a chart subtype where available::
+
+ workbook.add_chart({'type': 'bar', 'subtype': 'stacked'})
+
+See the :ref:`chart_class` for a list of available chart subtypes.
+
+
+.. Note::
+
+ A chart can only be inserted into a worksheet once. If several similar
+ charts are required then each one must be created separately with
+ ``add_chart()``.
+
+
+See also :ref:`working_with_charts` and :ref:`chart_examples`.
+
+workbook.add_chartsheet()
+-------------------------
+
+.. function:: add_chartsheet([sheetname])
+
+ Add a new add_chartsheet to a workbook.
+
+ :param string sheetname: Optional chartsheet name, defaults to Chart1, etc.
+ :rtype: A :ref:`chartsheet <Chartsheet>` object.
+
+The ``add_chartsheet()`` method adds a new chartsheet to a workbook.
+
+.. image:: _images/chartsheet.png
+
+See :ref:`chartsheet` for details.
+
+The ``sheetname`` parameter is optional. If it is not specified the default
+Excel convention will be followed, i.e. Chart1, Chart2, etc.
+
+The chartsheet name must be a valid Excel worksheet name, i.e. it cannot
+contain any of the characters ``' [ ] : * ? / \
+'`` and it must be less than 32 characters.
+
+In addition, you cannot use the same, case insensitive, ``sheetname`` for more
+than one chartsheet.
+
+
+workbook.close()
+----------------
+
+.. py:function:: close()
+
+ Close the Workbook object and write the XLSX file.
+
+The workbook ``close()`` method writes all data to the xlsx file and closes
+it::
+
+ workbook.close()
+
+
+The ``Workbook`` object also works using the ``with`` context manager. In
+which case it doesn't need an explicit `close()` statement::
+
+ with xlsxwriter.Workbook('hello_world.xlsx') as workbook:
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Hello world')
+
+The workbook will close automatically when exiting the scope of the ``with``
+statement.
+
+.. Note::
+
+ Unless you are using the ``with`` context manager you should always use an
+ explicit ``close()`` in your XlsxWriter application.
+
+
+workbook.set_size()
+-------------------
+
+.. py:function:: set_size(width, height)
+
+ Set the size of a workbook window.
+
+ :param int width: Width of the window in pixels.
+ :param int height: Height of the window in pixels.
+
+The ``set_size()`` method can be used to set the size of a workbook window::
+
+ workbook,set_size(1200, 800)
+
+The Excel window size was used in Excel 2007 to define the width and height of
+a workbook window within the Multiple Document Interface (MDI). In later
+versions of Excel for Windows this interface was dropped. This method is
+currently only useful when setting the window size in Excel for Mac 2011. The
+units are pixels and the default size is 1073 x 644.
+
+Note, this doesn't equate exactly to the Excel for Mac pixel size since it is
+based on the original Excel 2007 for Windows sizing. Some trial and error may
+be required to get an exact size.
+
+
+workbook.set_properties()
+-------------------------
+
+.. py:function:: set_properties(properties)
+
+ Set the document properties such as Title, Author etc.
+
+ :param dict properties: Dictionary of document properties.
+
+The ``set_properties()`` method can be used to set the document properties of the
+Excel file created by ``XlsxWriter``. These properties are visible when you
+use the ``Office Button -> Prepare -> Properties`` option in Excel and are
+also available to external applications that read or index windows files.
+
+The properties that can be set are:
+
+* ``title``
+* ``subject``
+* ``author``
+* ``manager``
+* ``company``
+* ``category``
+* ``keywords``
+* ``comments``
+* ``status``
+* ``hyperlink_base``
+
+The properties are all optional and should be passed in dictionary format as
+follows::
+
+ workbook.set_properties({
+ 'title': 'This is an example spreadsheet',
+ 'subject': 'With document properties',
+ 'author': 'John McNamara',
+ 'manager': 'Dr. Heinz Doofenshmirtz',
+ 'company': 'of Wolves',
+ 'category': 'Example spreadsheets',
+ 'keywords': 'Sample, Example, Properties',
+ 'comments': 'Created with Python and XlsxWriter'})
+
+.. image:: _images/doc_properties.png
+
+See also :ref:`ex_doc_properties`.
+
+
+workbook.set_custom_property()
+------------------------------
+
+.. py:function:: set_custom_property(name, value [, property_type])
+
+ Set a custom document property.
+
+ :param name: The name of the custom property.
+ :param value: The value of the custom property (various types).
+ :param property_type: The type of the property. Optional.
+ :type name: string
+ :type property_type: string
+
+
+The ``set_custom_property()`` method can be used to set one or more custom
+document properties not covered by the standard properties in the
+``set_properties()`` method above.
+
+For example::
+
+ date = datetime.strptime('2016-12-12', '%Y-%m-%d')
+
+ workbook.set_custom_property('Checked by', 'Eve')
+ workbook.set_custom_property('Date completed', date)
+ workbook.set_custom_property('Document number', 12345)
+ workbook.set_custom_property('Reference number', 1.2345)
+ workbook.set_custom_property('Has review', True)
+ workbook.set_custom_property('Signed off', False)
+
+.. image:: _images/custom_properties.png
+
+
+Date parameters should be :class:`datetime.datetime` objects.
+
+The optional ``property_type`` parameter can be used to set an explicit type
+for the custom property, just like in Excel. The available types are::
+
+ text
+ date
+ number
+ bool
+
+However, in almost all cases the type will be inferred correctly from the
+Python type, like in the example above.
+
+
+Note: the ``name`` and ``value`` parameters are limited to 255 characters by
+Excel.
+
+
+workbook.define_name()
+----------------------
+
+.. py:function:: define_name()
+
+ Create a defined name in the workbook to use as a variable.
+
+ :param string name: The defined name.
+ :param string formula: The cell or range that the defined name refers to.
+
+This method is used to defined a name that can be used to represent a value, a
+single cell or a range of cells in a workbook. These are sometimes referred to
+as a "Named Range".
+
+Defined names are generally used to simplify or clarify formulas by using
+descriptive variable names::
+
+ workbook.define_name('Exchange_rate', '=0.96')
+ worksheet.write('B3', '=B2*Exchange_rate')
+
+.. image:: _images/defined_name.png
+
+As in Excel a name defined like this is "global" to the workbook and can be
+referred to from any worksheet::
+
+ # Global workbook name.
+ workbook.define_name('Sales', '=Sheet1!$G$1:$H$10')
+
+It is also possible to define a local/worksheet name by prefixing it with the
+sheet name using the syntax ``'sheetname!definedname'``::
+
+ # Local worksheet name.
+ workbook.define_name('Sheet2!Sales', '=Sheet2!$G$1:$G$10')
+
+If the sheet name contains spaces or special characters you must follow the
+Excel convention and enclose it in single quotes::
+
+ workbook.define_name("'New Data'!Sales", '=Sheet2!$G$1:$G$10')
+
+The rules for names in Excel are explained in the `Microsoft Office
+documentation
+<http://office.microsoft.com/en-001/excel-help/define-and-use-names-in-formulas-HA010147120.aspx>`_.
+
+See also :ref:`ex_defined_name`.
+
+
+workbook.add_vba_project()
+--------------------------
+
+.. py:function:: add_vba_project(vba_project[, is_stream])
+
+ Add a vbaProject binary to the Excel workbook.
+
+ :param vba_project: The vbaProject binary file name.
+ :param bool is_stream: The vba_project is an in memory byte stream.
+
+The ``add_vba_project()`` method can be used to add macros or functions to a
+workbook using a binary VBA project file that has been extracted from an
+existing Excel xlsm file::
+
+ workbook.add_vba_project('./vbaProject.bin')
+
+Only one ``vbaProject.bin`` file can be added per workbook.
+
+The ``is_stream`` parameter is used to indicate that ``vba_project`` refers to
+a BytesIO byte stream rather than a physical file. This can be used when
+working with the workbook ``in_memory`` mode.
+
+See :ref:`macros` for more details.
+
+
+workbook.set_vba_name()
+-----------------------
+
+.. py:function:: set_vba_name(name)
+
+ Set the VBA name for the workbook.
+
+ :param string name: The VBA name for the workbook.
+
+The ``set_vba_name()`` method can be used to set the VBA codename for the
+workbook. This is sometimes required when a vbaProject macro included via
+``add_vba_project()`` refers to the workbook. The default Excel VBA name of
+``ThisWorkbook`` is used if a user defined name isn't specified.
+
+See :ref:`macros` for more details.
+
+
+workbook.worksheets()
+---------------------
+
+.. py:function:: worksheets()
+
+ Return a list of the worksheet objects in the workbook.
+
+ :rtype: A list of :ref:`worksheet <Worksheet>` objects.
+
+The ``worksheets()`` method returns a list of the worksheets in a workbook.
+This is useful if you want to repeat an operation on each worksheet in a
+workbook::
+
+ for worksheet in workbook.worksheets():
+ worksheet.write('A1', 'Hello')
+
+
+workbook.get_worksheet_by_name()
+--------------------------------
+
+.. function:: get_worksheet_by_name(name)
+
+ Return a worksheet object in the workbook using the sheetname.
+
+ :param string name: Name of worksheet that you wish to retrieve.
+ :rtype: A :ref:`worksheet <Worksheet>` object.
+
+The ``get_worksheet_by_name()`` method returns the worksheet or chartsheet
+object with the the given ``name`` or ``None`` if it isn't found::
+
+ worksheet = workbook.get_worksheet_by_name('Sheet1')
+
+
+workbook.set_calc_mode()
+------------------------
+
+.. py:function:: set_calc_mode(mode)
+
+ Set the Excel calculation mode for the workbook.
+
+ :param string mode: The calculation mode string
+
+Set the calculation mode for formulas in the workbook. This is mainly of use
+for workbooks with slow formulas where you want to allow the user to calculate
+them manually.
+
+The ``mode`` parameter can be:
+
+* ``auto``: The default. Excel will re-calculate formulas when a formula or a
+ value affecting the formula changes.
+
+* ``manual``: Only re-calculate formulas when the user requires it. Generally
+ by pressing F9.
+
+* ``auto_except_tables``: Excel will automatically re-calculate formulas
+ except for tables.
+
+
+workbook.use_zip64()
+--------------------
+
+.. py:function:: use_zip64()
+
+ Allow ZIP64 extensions when writing xlsx file zip container.
+
+Use ZIP64 extensions when writing the xlsx file zip container to allow files
+greater than 4 GB.
diff --git a/dev/docs/source/working_with_autofilters.rst b/dev/docs/source/working_with_autofilters.rst
new file mode 100644
index 0000000..8fe9589
--- /dev/null
+++ b/dev/docs/source/working_with_autofilters.rst
@@ -0,0 +1,168 @@
+.. _working_with_autofilters:
+
+Working with Autofilters
+========================
+
+An autofilter in Excel is a way of filtering a 2D range of data based on some
+simple criteria.
+
+.. image:: _images/autofilter1.png
+
+
+Applying an autofilter
+----------------------
+
+The first step is to apply an autofilter to a cell range in a worksheet using
+the :func:`autofilter` method::
+
+ worksheet.autofilter('A1:D11')
+
+As usual you can also use :ref:`Row-Column <cell_notation>` notation::
+
+ worksheet.autofilter(0, 0, 10, 3) # Same as above.
+
+
+Filter data in an autofilter
+----------------------------
+
+The :func:`autofilter` defines the cell range that the filter applies to and
+creates drop-down selectors in the heading row. In order to filter out data it
+is necessary to apply some criteria to the columns using either the
+:func:`filter_column()` or :func:`filter_column_list()` methods.
+
+The ``filter_column`` method is used to filter columns in a autofilter range
+based on simple criteria::
+
+ worksheet.filter_column('A', 'x > 2000')
+ worksheet.filter_column('B', 'x > 2000 and x < 5000')
+
+It isn't sufficient to just specify the filter condition. You must also hide
+any rows that don't match the filter condition. Rows are hidden using the
+:func:`set_row()` ``hidden`` parameter. ``XlsxWriter`` cannot filter rows
+automatically since this isn't part of the file format.
+
+The following is an example of how you might filter a data range to match an
+autofilter criteria::
+
+ # Set the autofilter.
+ worksheet.autofilter('A1:D51')
+
+ # Add the filter criteria. The placeholder "Region" in the filter is
+ # ignored and can be any string that adds clarity to the expression.
+ worksheet.filter_column(0, 'Region == East')
+
+ # Hide the rows that don't match the filter criteria.
+ row = 1
+ for row_data in (data):
+ region = row_data[0]
+
+ # Check for rows that match the filter.
+ if region == 'East':
+ # Row matches the filter, display the row as normal.
+ pass
+ else:
+ # We need to hide rows that don't match the filter.
+ worksheet.set_row(row, options={'hidden': True})
+
+ worksheet.write_row(row, 0, row_data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+
+Setting a filter criteria for a column
+--------------------------------------
+
+The :func:`filter_column` method can be used to filter columns in a autofilter
+range based on simple conditions::
+
+ worksheet.filter_column('A', 'x > 2000')
+
+The ``column`` parameter can either be a zero indexed column number or a string
+column name.
+
+The following operators are available for setting the filter criteria::
+
+ Operator
+ ==
+ !=
+ >
+ <
+ >=
+ <=
+
+ and
+ or
+
+An expression can comprise a single statement or two statements separated by
+the ``and`` and ``or`` operators. For example::
+
+ 'x < 2000'
+ 'x > 2000'
+ 'x == 2000'
+ 'x > 2000 and x < 5000'
+ 'x == 2000 or x == 5000'
+
+Filtering of blank or non-blank data can be achieved by using a value of
+``Blanks`` or ``NonBlanks`` in the expression::
+
+ 'x == Blanks'
+ 'x == NonBlanks'
+
+Excel also allows some simple string matching operations::
+
+ 'x == b*' # begins with b
+ 'x != b*' # doesn't begin with b
+ 'x == *b' # ends with b
+ 'x != *b' # doesn't end with b
+ 'x == *b*' # contains b
+ 'x != *b*' # doesn't contains b
+
+You can also use ``'*'`` to match any character or number and ``'?'`` to match
+any single character or number. No other regular expression quantifier is
+supported by Excel's filters. Excel's regular expression characters can be
+escaped using ``'~'``.
+
+The placeholder variable ``x`` in the above examples can be replaced by any
+simple string. The actual placeholder name is ignored internally so the
+following are all equivalent::
+
+ 'x < 2000'
+ 'col < 2000'
+ 'Price < 2000'
+
+A filter condition can only be applied to a column in a range specified by the
+:func:`autofilter()` method.
+
+
+Setting a column list filter
+----------------------------
+
+Prior to Excel 2007 it was only possible to have either 1 or 2 filter
+conditions such as the ones shown above in the :func:`filter_column` method.
+
+Excel 2007 introduced a new list style filter where it is possible to specify 1
+or more 'or' style criteria. For example if your column contained data for the
+months of the year you could filter the data based on certain months:
+
+.. image:: _images/autofilter2.png
+
+
+The :func:`filter_column_list()` method can be used to represent these types of
+filters::
+
+ worksheet.filter_column_list('A', ['March', 'April', 'May'])
+
+One or more criteria can be selected::
+
+ worksheet.filter_column_list('A', ['March'])
+ worksheet.filter_column_list('B', [100, 110, 120, 130])
+
+As explained above, it isn't sufficient to just specify filters. You must also
+hide any rows that don't match the filter condition.
+
+
+Example
+-------
+
+See :ref:`ex_autofilter` for a full example of all these features.
diff --git a/dev/docs/source/working_with_cell_comments.rst b/dev/docs/source/working_with_cell_comments.rst
new file mode 100644
index 0000000..e1465e9
--- /dev/null
+++ b/dev/docs/source/working_with_cell_comments.rst
@@ -0,0 +1,146 @@
+.. _cell_comments:
+
+Working with Cell Comments
+==========================
+
+Cell comments are a way of adding notation to cells in Excel. For example::
+
+ worksheet.write('A1', 'Hello')
+ worksheet.write_comment('A1', 'This is a comment')
+
+.. image:: _images/comments1.png
+
+
+Setting Comment Properties
+--------------------------
+
+The properties of the cell comment can be modified by passing an optional
+dictionary of key/value pairs to control the format of the comment. For
+example::
+
+ worksheet.write_comment('C3', 'Hello', {'x_scale': 1.2, 'y_scale': 0.8})
+
+The following options are available::
+
+ author
+ visible
+ x_scale
+ width
+ y_scale
+ height
+ color
+ start_cell
+ start_row
+ start_col
+ x_offset
+ y_offset
+
+The options are explained in detail below:
+
+* ``author``: This option is used to indicate who is the author of the cell
+ comment. Excel displays the author of the comment in the status bar at the
+ bottom of the worksheet. This is usually of interest in corporate
+ environments where several people might review and provide comments to a
+ workbook::
+
+ worksheet.write_comment('C3', 'Atonement', {'author': 'Ian McEwan'})
+
+ The default author for all cell comments in a worksheet can be set using
+ the :func:`set_comments_author` method::
+
+ worksheet.set_comments_author('John Smith')
+
+* ``visible``: This option is used to make a cell comment visible when the
+ worksheet is opened. The default behavior in Excel is that comments are
+ initially hidden. However, it is also possible in Excel to make individual
+ comments or all comments visible. In XlsxWriter individual comments can be
+ made visible as follows::
+
+ worksheet.write_comment('C3', 'Hello', {'visible': True})
+
+ It is possible to make all comments in a worksheet visible using the
+ :func:`show_comments()` worksheet method. Alternatively, if all of the cell
+ comments have been made visible you can hide individual comments::
+
+ worksheet.write_comment('C3', 'Hello', {'visible': False})
+
+* ``x_scale``: This option is used to set the width of the cell comment box
+ as a factor of the default width::
+
+ worksheet.write_comment('C3', 'Hello', {'x_scale': 2 })
+ worksheet.write_comment('C4', 'Hello', {'x_scale': 4.2})
+
+* ``width``: This option is used to set the width of the cell comment box
+ explicitly in pixels::
+
+ worksheet.write_comment('C3', 'Hello', {'width': 200})
+
+* ``y_scale``: This option is used to set the height of the cell comment box
+ as a factor of the default height::
+
+ worksheet.write_comment('C3', 'Hello', {'y_scale': 2 })
+ worksheet.write_comment('C4', 'Hello', {'y_scale': 4.2})
+
+* ``height``: This option is used to set the height of the cell comment box
+ explicitly in pixels::
+
+ worksheet.write_comment('C3', 'Hello', {'height': 200})
+
+* ``color``: This option is used to set the background color of cell comment
+ box. You can use one of the named colors recognized by XlsxWriter or a Html
+ color. See :ref:`colors`::
+
+ worksheet.write_comment('C3', 'Hello', {'color': 'green' })
+ worksheet.write_comment('C4', 'Hello', {'color': '#CCFFCC'})
+
+* ``start_cell``: This option is used to set the cell in which the comment
+ will appear. By default Excel displays comments one cell to the right and
+ one cell above the cell to which the comment relates. However, you can
+ change this behavior if you wish. In the following example the comment
+ which would appear by default in cell ``D2`` is moved to ``E2``::
+
+ worksheet.write_comment('C3', 'Hello', {'start_cell': 'E2'})
+
+* ``start_row``: This option is used to set the row in which the comment will
+ appear. See the ``start_cell`` option above. The row is zero indexed::
+
+ worksheet.write_comment('C3', 'Hello', {'start_row': 0})
+
+* ``start_col``: This option is used to set the column in which the comment
+ will appear. See the ``start_cell`` option above. The column is zero
+ indexed::
+
+ worksheet.write_comment('C3', 'Hello', {'start_col': 4})
+
+* ``x_offset``: This option is used to change the x offset, in pixels, of a
+ comment within a cell::
+
+ worksheet.write_comment('C3', comment, {'x_offset': 30})
+
+* ``y_offset``: This option is used to change the y offset, in pixels, of a
+ comment within a cell::
+
+ worksheet.write_comment('C3', comment, {'y_offset': 30})
+
+
+You can apply as many of these options as you require. For a working example of
+these options in use see :ref:`ex_comments2`.
+
+.. Note::
+ Excel only displays offset cell comments when they are displayed as
+ ``visible``. Excel does **not** display hidden cells as displaced
+ when you mouse over them. Please note this when using options that adjust
+ the position of the cell comment such as ``start_cell``, ``start_row``,
+ ``start_col``, ``x_offset`` and ``y_offset``.
+
+.. Note::
+ **Row height and comments**. If you specify the height of a row that
+ contains a comment then XlsxWriter will adjust the height of the comment
+ to maintain the default or user specified dimensions. However, the height
+ of a row can also be adjusted automatically by Excel if the text wrap
+ property is set or large fonts are used in the cell. This means that
+ the height of the row is unknown to the module at run time and thus
+ the comment box is stretched with the row. Use the ``set_row()`` method
+ to specify the row height explicitly and avoid this problem. See example 8
+ of :ref:`ex_comments2`.
+
diff --git a/dev/docs/source/working_with_cell_notation.rst b/dev/docs/source/working_with_cell_notation.rst
new file mode 100644
index 0000000..08f1fdd
--- /dev/null
+++ b/dev/docs/source/working_with_cell_notation.rst
@@ -0,0 +1,216 @@
+.. _cell_notation:
+
+Working with Cell Notation
+==========================
+
+XlsxWriter supports two forms of notation to designate the position of cells:
+**Row-column** notation and **A1** notation.
+
+Row-column notation uses a zero based index for both row and column while A1
+notation uses the standard Excel alphanumeric sequence of column letter and
+1-based row. For example::
+
+
+ (0, 0) # Row-column notation.
+ ('A1') # The same cell in A1 notation.
+
+ (6, 2) # Row-column notation.
+ ('C7') # The same cell in A1 notation.
+
+Row-column notation is useful if you are referring to cells programmatically::
+
+ for row in range(0, 5):
+ worksheet.write(row, 0, 'Hello')
+
+A1 notation is useful for setting up a worksheet manually and for working with
+formulas::
+
+ worksheet.write('H1', 200)
+ worksheet.write('H2', '=H1+1')
+
+In general when using the XlsxWriter module you can use A1 notation anywhere
+you can use row-column notation.
+
+XlsxWriter supports Excels worksheet limits of 1,048,576 rows by 16,384
+columns.
+
+.. note::
+ Ranges in A1 notation must be in uppercase, like in Excel.
+
+.. note::
+ In Excel it is also possible to use R1C1 notation. This is not
+ supported by XlsxWriter.
+
+
+.. _abs_reference:
+
+Relative and Absolute cell references
+-------------------------------------
+
+When dealing with Excel cell references it is important to distinguish between
+relative and absolute cell references in Excel.
+
+**Relative** cell references change when they are copied while **Absolute**
+references maintain fixed row and/or column references. In Excel absolute
+references are prefixed by the dollar symbol as shown below::
+
+ A1 # Column and row are relative.
+ $A1 # Column is absolute and row is relative.
+ A$1 # Column is relative and row is absolute.
+ $A$1 # Column and row are absolute.
+
+See the Microsoft Office documentation for
+`more information on relative and absolute references <http://office.microsoft.com/en-001/excel-help/switch-between-relative-absolute-and-mixed-references-HP010342940.aspx>`_.
+
+Some functions such as :func:`conditional_format()` require absolute
+references.
+
+
+Defined Names and Named Ranges
+------------------------------
+
+It is also possible to define and use "Defined names/Named ranges" in
+workbooks and worksheets, see :func:`define_name`::
+
+ workbook.define_name('Exchange_rate', '=0.96')
+ worksheet.write('B3', '=B2*Exchange_rate')
+
+See also :ref:`ex_defined_name`.
+
+
+.. _cell_utility:
+
+Cell Utility Functions
+----------------------
+
+The ``XlsxWriter`` ``utility`` module contains several helper functions for
+dealing with A1 notation as shown below. These functions can be imported as
+follows::
+
+ from xlsxwriter.utility import xl_rowcol_to_cell
+
+ cell = xl_rowcol_to_cell(1, 2) # C2
+
+
+xl_rowcol_to_cell()
+~~~~~~~~~~~~~~~~~~~
+
+.. py:function:: xl_rowcol_to_cell(row, col[, row_abs, col_abs])
+
+ Convert a zero indexed row and column cell reference to a A1 style string.
+
+ :param int row: The cell row.
+ :param int col: The cell column.
+ :param bool row_abs: Optional flag to make the row absolute.
+ :param bool col_abs: Optional flag to make the column absolute.
+ :rtype: A1 style string.
+
+
+The ``xl_rowcol_to_cell()`` function converts a zero indexed row and column
+cell values to an ``A1`` style string::
+
+ cell = xl_rowcol_to_cell(0, 0) # A1
+ cell = xl_rowcol_to_cell(0, 1) # B1
+ cell = xl_rowcol_to_cell(1, 0) # A2
+
+The optional parameters ``row_abs`` and ``col_abs`` can be used to indicate
+that the row or column is absolute::
+
+ str = xl_rowcol_to_cell(0, 0, col_abs=True) # $A1
+ str = xl_rowcol_to_cell(0, 0, row_abs=True) # A$1
+ str = xl_rowcol_to_cell(0, 0, row_abs=True, col_abs=True) # $A$1
+
+
+xl_cell_to_rowcol()
+~~~~~~~~~~~~~~~~~~~
+
+.. py:function:: xl_cell_to_rowcol(cell_str)
+
+ Convert a cell reference in A1 notation to a zero indexed row and column.
+
+ :param string cell_str: A1 style string, absolute or relative.
+ :rtype: Tuple of ints for (row, col).
+
+
+The ``xl_cell_to_rowcol()`` function converts an Excel cell reference in ``A1``
+notation to a zero based row and column. The function will also handle Excel's
+absolute, ``$``, cell notation::
+
+ (row, col) = xl_cell_to_rowcol('A1') # (0, 0)
+ (row, col) = xl_cell_to_rowcol('B1') # (0, 1)
+ (row, col) = xl_cell_to_rowcol('C2') # (1, 2)
+ (row, col) = xl_cell_to_rowcol('$C2') # (1, 2)
+ (row, col) = xl_cell_to_rowcol('C$2') # (1, 2)
+ (row, col) = xl_cell_to_rowcol('$C$2') # (1, 2)
+
+
+xl_col_to_name()
+~~~~~~~~~~~~~~~~
+
+.. py:function:: xl_col_to_name(col[, col_abs])
+
+ Convert a zero indexed column cell reference to a string.
+
+ :param int col: The cell column.
+ :param bool col_abs: Optional flag to make the column absolute.
+ :rtype: Column style string.
+
+
+The ``xl_col_to_name()`` converts a zero based column reference to a string::
+
+ column = xl_col_to_name(0) # A
+ column = xl_col_to_name(1) # B
+ column = xl_col_to_name(702) # AAA
+
+The optional parameter ``col_abs`` can be used to indicate if the column is
+absolute::
+
+ column = xl_col_to_name(0, False) # A
+ column = xl_col_to_name(0, True) # $A
+ column = xl_col_to_name(1, True) # $B
+
+
+xl_range()
+~~~~~~~~~~
+
+.. py:function:: xl_range(first_row, first_col, last_row, last_col)
+
+ Converts zero indexed row and column cell references to a A1:B1 range
+ string.
+
+ :param int first_row: The first cell row.
+ :param int first_col: The first cell column.
+ :param int last_row: The last cell row.
+ :param int last_col: The last cell column.
+ :rtype: A1:B1 style range string.
+
+
+The ``xl_range()`` function converts zero based row and column cell references
+to an ``A1:B1`` style range string::
+
+ cell_range = xl_range(0, 0, 9, 0) # A1:A10
+ cell_range = xl_range(1, 2, 8, 2) # C2:C9
+ cell_range = xl_range(0, 0, 3, 4) # A1:E4
+
+
+xl_range_abs()
+~~~~~~~~~~~~~~
+
+.. py:function:: xl_range_abs(first_row, first_col, last_row, last_col)
+
+ Converts zero indexed row and column cell references to a $A$1:$B$1
+ absolute range string.
+
+ :param int first_row: The first cell row.
+ :param int first_col: The first cell column.
+ :param int last_row: The last cell row.
+ :param int last_col: The last cell column.
+ :rtype: $A$1:$B$1 style range string.
+
+
+The ``xl_range_abs()`` function converts zero based row and column cell
+references to an absolute ``$A$1:$B$1`` style range string::
+
+ cell_range = xl_range_abs(0, 0, 9, 0) # $A$1:$A$10
+ cell_range = xl_range_abs(1, 2, 8, 2) # $C$2:$C$9
+ cell_range = xl_range_abs(0, 0, 3, 4) # $A$1:$E$4
diff --git a/dev/docs/source/working_with_charts.rst b/dev/docs/source/working_with_charts.rst
new file mode 100644
index 0000000..93e0e6b
--- /dev/null
+++ b/dev/docs/source/working_with_charts.rst
@@ -0,0 +1,1481 @@
+.. _working_with_charts:
+
+Working with Charts
+===================
+
+This section explains how to work with some of the options and features of
+:ref:`chart_class`.
+
+The majority of the examples in this section are based on a variation of the
+following program::
+
+ import xlsxwriter
+
+ workbook = xlsxwriter.Workbook('chart_line.xlsx')
+ worksheet = workbook.add_worksheet()
+
+ # Add the worksheet data to be plotted.
+ data = [10, 40, 50, 20, 10, 50]
+ worksheet.write_column('A1', data)
+
+ # Create a new chart object.
+ chart = workbook.add_chart({'type': 'line'})
+
+ # Add a series to the chart.
+ chart.add_series({'values': '=Sheet1!$A$1:$A$6'})
+
+ # Insert the chart into the worksheet.
+ worksheet.insert_chart('C1', chart)
+
+ workbook.close()
+
+.. image:: _images/chart_working.png
+
+See also :ref:`chart_examples`.
+
+.. _chart_val_cat_axes:
+
+Chart Value and Category Axes
+-----------------------------
+
+When working with charts it is important to understand how Excel
+differentiates between a chart axis that is used for series categories and a
+chart axis that is used for series values.
+
+In the example above the X axis is the **category** axis and each of the values
+is evenly spaced and sequential. The Y axis is the **value** axis and points
+are displayed according to their value.
+
+Excel treats these two types of axis differently and exposes different
+properties for each.
+
+As such some of the ``XlsxWriter`` axis properties can be set for a value axis,
+some can be set for a category axis and some properties can be set for both.
+
+For example ``reverse`` can be set for either category or value axes while the
+``min`` and ``max`` properties can only be set for value axes (and Date Axes).
+
+:ref:`date_category_axes` are a special type of category axis that give them
+some of the properties of Values axes such as ``min`` and ``max`` when used
+with date or time values.
+
+.. _chart_series_options:
+
+Chart Series Options
+--------------------
+
+This following sections detail the more complex options of the
+:func:`add_series()` Chart method::
+
+ marker
+ trendline
+ y_error_bars
+ x_error_bars
+ data_labels
+ points
+ smooth
+
+
+.. _chart_series_option_marker:
+
+Chart series option: Marker
+---------------------------
+
+The marker format specifies the properties of the markers used to distinguish
+series on a chart. In general only Line and Scatter chart types and trendlines
+use markers.
+
+The following properties can be set for ``marker`` formats in a chart::
+
+ type
+ size
+ border
+ fill
+ pattern
+ gradient
+
+The ``type`` property sets the type of marker that is used with a series::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'marker': {'type': 'diamond'},
+ })
+
+.. image:: _images/chart_marker1.png
+ :scale: 75 %
+
+The following ``type`` properties can be set for ``marker`` formats in a chart.
+These are shown in the same order as in the Excel format dialog::
+
+ automatic
+ none
+ square
+ diamond
+ triangle
+ x
+ star
+ short_dash
+ long_dash
+ circle
+ plus
+
+The ``automatic`` type is a special case which turns on a marker using the
+default marker style for the particular series number::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'marker': {'type': 'automatic'},
+ })
+
+If ``automatic`` is on then other marker properties such as size, border or
+fill cannot be set.
+
+The ``size`` property sets the size of the marker and is generally used in
+conjunction with ``type``::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'marker': {'type': 'diamond', 'size': 7},
+ })
+
+Nested ``border`` and ``fill`` properties can also be set for a marker::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'marker': {
+ 'type': 'square',
+ 'size': 8,
+ 'border': {'color': 'black'},
+ 'fill': {'color': 'red'},
+ },
+ })
+
+.. image:: _images/chart_marker2.png
+ :scale: 75 %
+
+.. _chart_series_option_trendline:
+
+Chart series option: Trendline
+------------------------------
+
+A trendline can be added to a chart series to indicate trends in the data such
+as a moving average or a polynomial fit.
+
+The following properties can be set for trendlines in a chart series::
+
+ type
+ order (for polynomial trends)
+ period (for moving average)
+ forward (for all except moving average)
+ backward (for all except moving average)
+ name
+ line
+ intercept (for exponential, linear and polynomial only)
+ display_equation (for all except moving average)
+ display_r_squared (for all except moving average)
+
+The ``type`` property sets the type of trendline in the series::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'trendline': {'type': 'linear'},
+ })
+
+The available ``trendline`` types are::
+
+ exponential
+ linear
+ log
+ moving_average
+ polynomial
+ power
+
+A ``polynomial`` trendline can also specify the ``order`` of the polynomial.
+The default value is 2::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'trendline': {
+ 'type': 'polynomial',
+ 'order': 3,
+ },
+ })
+
+.. image:: _images/chart_trendline1.png
+ :scale: 75 %
+
+A ``moving_average`` trendline can also specify the ``period`` of the moving
+average. The default value is 2::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'trendline': {
+ 'type': 'moving_average',
+ 'period': 2,
+ },
+ })
+
+
+.. image:: _images/chart_trendline2.png
+ :scale: 75 %
+
+The ``forward`` and ``backward`` properties set the forecast period of the
+trendline::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'trendline': {
+ 'type': 'polynomial',
+ 'name': 'My trend name',
+ 'order': 2,
+ },
+ })
+
+The ``name`` property sets an optional name for the trendline that will appear
+in the chart legend. If it isn't specified the Excel default name will be
+displayed. This is usually a combination of the trendline type and the series
+name::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'trendline': {
+ 'type': 'polynomial',
+ 'order': 2,
+ 'forward': 0.5,
+ 'backward': 0.5,
+ },
+ })
+
+
+The ``intercept`` property sets the point where the trendline crosses the Y
+(value) axis::
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {'type': 'linear',
+ 'intercept': 0.8,
+ },
+ })
+
+
+The ``display_equation`` property displays the trendline equation on the
+chart::
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {'type': 'linear',
+ 'display_equation': True,
+ },
+ })
+
+The ``display_r_squared`` property displays the R squared value of the
+trendline on the chart::
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {'type': 'linear',
+ 'display_r_squared': True,
+ },
+ })
+
+Several of these properties can be set in one go::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'trendline': {
+ 'type': 'polynomial',
+ 'name': 'My trend name',
+ 'order': 2,
+ 'forward': 0.5,
+ 'backward': 0.5,
+ 'display_equation': True,
+ 'line': {
+ 'color': 'red',
+ 'width': 1,
+ 'dash_type': 'long_dash',
+ },
+ },
+ })
+
+.. image:: _images/chart_trendline3.png
+ :scale: 75 %
+
+Trendlines cannot be added to series in a stacked chart or pie chart, doughnut
+chart, radar chart or (when implemented) to 3D or surface charts.
+
+
+.. _chart_series_option_error_bars:
+
+Chart series option: Error Bars
+-------------------------------
+
+Error bars can be added to a chart series to indicate error bounds in the data.
+The error bars can be vertical ``y_error_bars`` (the most common type) or
+horizontal ``x_error_bars`` (for Bar and Scatter charts only).
+
+The following properties can be set for error bars in a chart series::
+
+ type
+ value (for all types except standard error and custom)
+ plus_values (for custom only)
+ minus_values (for custom only)
+ direction
+ end_style
+ line
+
+The ``type`` property sets the type of error bars in the series::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'y_error_bars': {'type': 'standard_error'},
+ })
+
+.. image:: _images/chart_error_bars1.png
+ :scale: 75 %
+
+The available error bars types are available::
+
+ fixed
+ percentage
+ standard_deviation
+ standard_error
+ custom
+
+All error bar types, except for ``standard_error`` and ``custom`` must also
+have a value associated with it for the error bounds::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'y_error_bars': {
+ 'type': 'percentage',
+ 'value': 5,
+ },
+ })
+
+The ``custom`` error bar type must specify ``plus_values`` and ``minus_values``
+which should either by a ``Sheet1!$A$1:$A$5`` type range formula or a list of
+values::
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'y_error_bars': {
+ 'type': 'custom',
+ 'plus_values': '=Sheet1!$C$1:$C$5',
+ 'minus_values': '=Sheet1!$D$1:$D$5',
+ },
+ })
+
+ # or
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'y_error_bars': {
+ 'type': 'custom',
+ 'plus_values': [1, 1, 1, 1, 1],
+ 'minus_values': [2, 2, 2, 2, 2],
+ },
+ })
+
+Note, as in Excel the items in the ``minus_values`` do not need to be negative.
+
+The ``direction`` property sets the direction of the error bars. It should be
+one of the following::
+
+ plus # Positive direction only.
+ minus # Negative direction only.
+ both # Plus and minus directions, The default.
+
+The ``end_style`` property sets the style of the error bar end cap. The options
+are 1 (the default) or 0 (for no end cap)::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'y_error_bars': {
+ 'type': 'fixed',
+ 'value': 2,
+ 'end_style': 0,
+ 'direction': 'minus'
+ },
+ })
+
+.. image:: _images/chart_error_bars2.png
+ :scale: 75 %
+
+
+.. _chart_series_option_data_labels:
+
+Chart series option: Data Labels
+--------------------------------
+
+Data labels can be added to a chart series to indicate the values of the
+plotted data points.
+
+The following properties can be set for ``data_labels`` formats in a chart::
+
+ value
+ category
+ series_name
+ position
+ leader_lines
+ percentage
+ separator
+ legend_key
+ num_format
+ font
+
+The ``value`` property turns on the *Value* data label for a series::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'data_labels': {'value': True},
+ })
+
+.. image:: _images/chart_data_labels1.png
+ :scale: 75 %
+
+The ``category`` property turns on the *Category Name* data label for a series::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'data_labels': {'category': True},
+ })
+
+The ``series_name`` property turns on the *Series Name* data label for a
+series::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'data_labels': {'series_name': True},
+ })
+
+The ``position`` property is used to position the data label for a series::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'data_labels': {'series_name': True, 'position': 'center'},
+ })
+
+In Excel the allowable data label positions vary for different chart types.
+The allowable positions are:
+
++-------------+----------+--------+----------+-------+
+| Position | Line, | Bar, | Pie, | Area, |
+| | Scatter, | Column | Doughnut | Radar |
+| | Stock | | | |
++=============+==========+========+==========+=======+
+| center | Yes | Yes | Yes | Yes* |
++-------------+----------+--------+----------+-------+
+| right | Yes* | | | |
++-------------+----------+--------+----------+-------+
+| left | Yes | | | |
++-------------+----------+--------+----------+-------+
+| above | Yes | | | |
++-------------+----------+--------+----------+-------+
+| below | Yes | | | |
++-------------+----------+--------+----------+-------+
+| inside_base | | Yes | | |
++-------------+----------+--------+----------+-------+
+| inside_end | | Yes | Yes | |
++-------------+----------+--------+----------+-------+
+| outside_end | | Yes* | Yes | |
++-------------+----------+--------+----------+-------+
+| best_fit | | | Yes* | |
++-------------+----------+--------+----------+-------+
+
+Note: The * indicates the default position for each chart type in Excel, if
+a position isn't specified.
+
+The ``percentage`` property is used to turn on the display of data labels as a
+*Percentage* for a series. It is mainly used for pie charts::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'data_labels': {'percentage': True},
+ })
+
+The ``leader_lines`` property is used to turn on *Leader Lines* for the data
+label of a series. It is mainly used for pie charts::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'data_labels': {'value': True, 'leader_lines': True},
+ })
+
+.. Note::
+ Even when leader lines are turned on they aren't automatically visible in
+ Excel or XlsxWriter. Due to an Excel limitation (or design) leader lines
+ only appear if the data label is moved manually or if the data labels are
+ very close and need to be adjusted automatically.
+
+The ``separator`` property is used to change the separator between multiple
+data label items::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'data_labels': {'value': True, 'category': True, 'separator': "\n"},
+ })
+
+The separator value must be one of the following strings::
+
+ ','
+ ';'
+ '.'
+ '\n'
+ ' '
+
+The ``legend_key`` property is used to turn on the *Legend Key* for the data
+label of a series::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'data_labels': {'value': True, 'legend_key': True},
+ })
+
+
+The ``num_format`` property is used to set the number format for the data
+labels of a series::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': True, 'num_format': '#,##0.00'},
+ })
+
+The number format is similar to the Worksheet Cell Format ``num_format``
+apart from the fact that a format index cannot be used. An explicit format
+string must be used as shown above. See :func:`set_num_format()` for more
+information.
+
+The ``font`` property is used to set the font of the data labels of a series::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {
+ 'value': True,
+ 'font': {'name': 'Consolas'}
+ },
+ })
+
+The ``font`` property is also used to rotate the data labels of a series::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {
+ 'value': True,
+ 'font': {'rotation': 45}
+ },
+ })
+
+See :ref:`chart_fonts`.
+
+.. _chart_series_option_points:
+
+Chart series option: Points
+---------------------------
+
+In general formatting is applied to an entire series in a chart. However, it
+is occasionally required to format individual points in a series. In
+particular this is required for Pie/Doughnut charts where each segment is
+represented by a point.
+
+In these cases it is possible to use the ``points`` property of
+:func:`add_series()`::
+
+ import xlsxwriter
+
+ workbook = xlsxwriter.Workbook('chart_pie.xlsx')
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [
+ ['Pass', 'Fail'],
+ [90, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$2',
+ 'values': '=Sheet1!$B$1:$B$2',
+ 'points': [
+ {'fill': {'color': 'green'}},
+ {'fill': {'color': 'red'}},
+ ],
+ })
+
+ worksheet.insert_chart('C3', chart)
+
+ workbook.close()
+
+.. image:: _images/chart_points1.png
+ :scale: 75 %
+
+The ``points`` property takes a list of format options (see the "Chart
+Formatting" section below). To assign default properties to points in a series
+pass ``None`` values in the array ref::
+
+ # Format point 3 of 3 only.
+ chart.add_series({
+ 'values': '=Sheet1!A1:A3',
+ 'points': [
+ None,
+ None,
+ {'fill': {'color': '#990000'}},
+ ],
+ })
+
+ # Format point 1 of 3 only.
+ chart.add_series({
+ 'values': '=Sheet1!A1:A3',
+ 'points': [
+ {'fill': {'color': '#990000'}},
+ ],
+ })
+
+
+Chart series option: Smooth
+---------------------------
+
+The ``smooth`` option is used to set the smooth property of a line series. It
+is only applicable to the ``line`` and ``scatter`` chart types::
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'smooth': True,
+ })
+
+
+.. _chart_formatting:
+
+Chart Formatting
+----------------
+
+The following chart formatting properties can be set for any chart object that
+they apply to (and that are supported by XlsxWriter) such as chart lines,
+column fill areas, plot area borders, markers, gridlines and other chart
+elements::
+
+ line
+ border
+ fill
+ pattern
+ gradient
+
+Chart formatting properties are generally set using dicts::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'line': {'color': 'red'},
+ })
+
+.. image:: _images/chart_formatting1.png
+ :scale: 75 %
+
+In some cases the format properties can be nested. For example a ``marker`` may
+contain ``border`` and ``fill`` sub-properties::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'line': {'color': 'blue'},
+ 'marker': {'type': 'square',
+ 'size,': 5,
+ 'border': {'color': 'red'},
+ 'fill': {'color': 'yellow'}
+ },
+ })
+
+.. image:: _images/chart_formatting2.png
+ :scale: 75 %
+
+
+.. _chart_formatting_line:
+
+Chart formatting: Line
+----------------------
+
+The line format is used to specify properties of line objects that appear in a
+chart such as a plotted line on a chart or a border.
+
+The following properties can be set for ``line`` formats in a chart::
+
+ none
+ color
+ width
+ dash_type
+
+The ``none`` property is uses to turn the ``line`` off (it is always on by
+default except in Scatter charts). This is useful if you wish to plot a series
+with markers but without a line::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'line': {'none': True},
+ 'marker': {'type': 'automatic'},
+ })
+
+.. image:: _images/chart_formatting3.png
+ :scale: 75 %
+
+
+The ``color`` property sets the color of the ``line``::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'line': {'color': 'red'},
+ })
+
+The available colors are shown in the main XlsxWriter documentation. It is
+also possible to set the color of a line with a Html style ``#RRGGBB`` string
+or a limited number of named colors, see :ref:`colors`::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'line': {'color': '#FF9900'},
+ })
+
+.. image:: _images/chart_formatting4.png
+ :scale: 75 %
+
+
+The ``width`` property sets the width of the ``line``. It should be specified
+in increments of 0.25 of a point as in Excel::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'line': {'width': 3.25},
+ })
+
+
+The ``dash_type`` property sets the dash style of the line::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'line': {'dash_type': 'dash_dot'},
+ })
+
+.. image:: _images/chart_formatting5.png
+ :scale: 75 %
+
+The following ``dash_type`` values are available. They are shown in the order
+that they appear in the Excel dialog::
+
+ solid
+ round_dot
+ square_dot
+ dash
+ dash_dot
+ long_dash
+ long_dash_dot
+ long_dash_dot_dot
+
+The default line style is ``solid``.
+
+More than one ``line`` property can be specified at a time::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'line': {
+ 'color': 'red',
+ 'width': 1.25,
+ 'dash_type': 'square_dot',
+ },
+ })
+
+
+.. _chart_formatting_border:
+
+Chart formatting: Border
+------------------------
+
+The ``border`` property is a synonym for ``line``.
+
+It can be used as a descriptive substitute for ``line`` in chart types such as
+Bar and Column that have a border and fill style rather than a line style. In
+general chart objects with a ``border`` property will also have a fill
+property.
+
+
+.. _chart_formatting_fill:
+
+Chart formatting: Solid Fill
+----------------------------
+
+The solid fill format is used to specify filled areas of chart objects such as
+the interior of a column or the background of the chart itself.
+
+The following properties can be set for ``fill`` formats in a chart::
+
+ none
+ color
+ transparency
+
+The ``none`` property is used to turn the ``fill`` property off (it is
+generally on by default)::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'fill': {'none': True},
+ 'border': {'color': 'black'}
+ })
+
+.. image:: _images/chart_fill1.png
+ :scale: 75 %
+
+The ``color`` property sets the color of the ``fill`` area::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'fill': {'color': 'red'}
+ })
+
+
+The available colors are shown in the main XlsxWriter documentation. It is
+also possible to set the color of a fill with a Html style ``#RRGGBB`` string
+or a limited number of named colors, see :ref:`colors`::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'fill': {'color': '#FF9900'}
+ })
+
+.. image:: _images/chart_fill2.png
+ :scale: 75 %
+
+
+The ``transparency`` property sets the transparency of the solid fill color in
+the integer range 1 - 100::
+
+ chart.set_chartarea({'fill': {'color': 'yellow', 'transparency': 75}})
+
+The ``fill`` format is generally used in conjunction with a ``border`` format
+which has the same properties as a ``line`` format::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'fill': {'color': 'red'},
+ 'border': {'color': 'black'}
+ })
+
+
+.. _chart_formatting_pattern:
+
+Chart formatting: Pattern Fill
+------------------------------
+
+The pattern fill format is used to specify pattern filled areas of chart
+objects such as the interior of a column or the background of the chart
+itself.
+
+.. image:: _images/chart_pattern.png
+ :scale: 75 %
+
+The following properties can be set for ``pattern`` fill formats in a chart::
+
+ pattern: the pattern to be applied (required)
+ fg_color: the foreground color of the pattern (required)
+ bg_color: the background color (optional, defaults to white)
+
+
+For example::
+
+ chart.set_plotarea({
+ 'pattern': {
+ 'pattern': 'percent_5',
+ 'fg_color': 'red',
+ 'bg_color': 'yellow',
+ }
+ })
+
+The following patterns can be applied:
+
+* ``percent_5``
+* ``percent_10``
+* ``percent_20``
+* ``percent_25``
+* ``percent_30``
+* ``percent_40``
+* ``percent_50``
+* ``percent_60``
+* ``percent_70``
+* ``percent_75``
+* ``percent_80``
+* ``percent_90``
+* ``light_downward_diagonal``
+* ``light_upward_diagonal``
+* ``dark_downward_diagonal``
+* ``dark_upward_diagonal``
+* ``wide_downward_diagonal``
+* ``wide_upward_diagonal``
+* ``light_vertical``
+* ``light_horizontal``
+* ``narrow_vertical``
+* ``narrow_horizontal``
+* ``dark_vertical``
+* ``dark_horizontal``
+* ``dashed_downward_diagonal``
+* ``dashed_upward_diagonal``
+* ``dashed_horizontal``
+* ``dashed_vertical``
+* ``small_confetti``
+* ``large_confetti``
+* ``zigzag``
+* ``wave``
+* ``diagonal_brick``
+* ``horizontal_brick``
+* ``weave``
+* ``plaid``
+* ``divot``
+* ``dotted_grid``
+* ``dotted_diamond``
+* ``shingle``
+* ``trellis``
+* ``sphere``
+* ``small_grid``
+* ``large_grid``
+* ``small_check``
+* ``large_check``
+* ``outlined_diamond``
+* ``solid_diamond``
+
+
+The foreground color, ``fg_color``, is a required parameter and can be a Html
+style ``#RRGGBB`` string or a limited number of named colors, see
+:ref:`colors`.
+
+The background color, ``bg_color``, is optional and defaults to black.
+
+If a pattern fill is used on a chart object it overrides the solid fill
+properties of the object.
+
+
+.. _chart_formatting_gradient:
+
+Chart formatting: Gradient Fill
+-------------------------------
+
+The gradient fill format is used to specify gradient filled areas of chart
+objects such as the interior of a column or the background of the chart
+itself.
+
+.. image:: _images/chart_gradient.png
+ :scale: 75 %
+
+The following properties can be set for ``gradient`` fill formats in a chart::
+
+ colors: a list of colors
+ positions: an optional list of positions for the colors
+ type: the optional type of gradient fill
+ angle: the optional angle of the linear fill
+
+The ``colors`` property sets a list of colors that define the ``gradient``::
+
+ chart.set_plotarea({
+ 'gradient': {'colors': ['#FFEFD1', '#F0EBD5', '#B69F66']}
+ })
+
+Excel allows between 2 and 10 colors in a gradient but it is unlikely that
+you will require more than 2 or 3.
+
+As with solid or pattern fill it is also possible to set the colors of a
+gradient with a Html style ``#RRGGBB`` string or a limited number of named
+colors, see :ref:`colors`::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'gradient': {'colors': ['red', 'green']}
+ })
+
+The ``positions`` defines an optional list of positions, between 0 and 100, of
+where the colors in the gradient are located. Default values are provided for
+``colors`` lists of between 2 and 4 but they can be specified if required::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {
+ 'colors': ['#DDEBCF', '#156B13'],
+ 'positions': [10, 90],
+ }
+ })
+
+
+The ``type`` property can have one of the following values::
+
+ linear (the default)
+ radial
+ rectangular
+ path
+
+For example::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {
+ 'colors': ['#DDEBCF', '#9CB86E', '#156B13'],
+ 'type': 'radial'
+ }
+ })
+
+If ``type`` isn't specified it defaults to ``linear``.
+
+For a ``linear`` fill the angle of the gradient can also be specified::
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {'colors': ['#DDEBCF', '#9CB86E', '#156B13'],
+ 'angle': 45}
+ })
+
+The default angle is 90 degrees.
+
+If gradient fill is used on a chart object it overrides the solid fill and
+pattern fill properties of the object.
+
+
+.. _chart_fonts:
+
+Chart Fonts
+-----------
+
+The following font properties can be set for any chart object that they apply
+to (and that are supported by XlsxWriter) such as chart titles, axis labels,
+axis numbering and data labels::
+
+ name
+ size
+ bold
+ italic
+ underline
+ rotation
+ color
+
+These properties correspond to the equivalent Worksheet cell Format object
+properties. See the :ref:`format` section for more details about Format
+properties and how to set them.
+
+The following explains the available font properties:
+
+
+* ``name``: Set the font name::
+
+ chart.set_x_axis({'num_font': {'name': 'Arial'}})
+
+* ``size``: Set the font size::
+
+ chart.set_x_axis({'num_font': {'name': 'Arial', 'size': 9}})
+
+* ``bold``: Set the font bold property::
+
+ chart.set_x_axis({'num_font': {'bold': True}})
+
+* ``italic``: Set the font italic property::
+
+ chart.set_x_axis({'num_font': {'italic': True}})
+
+* ``underline``: Set the font underline property::
+
+ chart.set_x_axis({'num_font': {'underline': True}})
+
+* ``rotation``: Set the font rotation, angle, property in the range -90 to 90 deg::
+
+ chart.set_x_axis({'num_font': {'rotation': 45}})
+
+ This is useful for displaying axis data such as dates in a more compact
+ format.
+
+* ``color``: Set the font color property. Can be a color index, a color name
+ or HTML style RGB color::
+
+ chart.set_x_axis({'num_font': {'color': 'red' }})
+ chart.set_y_axis({'num_font': {'color': '#92D050'}})
+
+
+Here is an example of Font formatting in a Chart program::
+
+
+ chart.set_title({
+ 'name': 'Test Results',
+ 'name_font': {
+ 'name': 'Calibri',
+ 'color': 'blue',
+ },
+ })
+
+ chart.set_x_axis({
+ 'name': 'Month',
+ 'name_font': {
+ 'name': 'Courier New',
+ 'color': '#92D050'
+ },
+ 'num_font': {
+ 'name': 'Arial',
+ 'color': '#00B0F0',
+ },
+ })
+
+ chart.set_y_axis({
+ 'name': 'Units',
+ 'name_font': {
+ 'name': 'Century',
+ 'color': 'red'
+ },
+ 'num_font': {
+ 'bold': True,
+ 'italic': True,
+ 'underline': True,
+ 'color': '#7030A0',
+ },
+ })
+
+ chart.set_legend({'font': {'bold': 1, 'italic': 1}})
+
+.. image:: _images/chart_font1.png
+ :scale: 75 %
+
+.. _chart_layout:
+
+Chart Layout
+------------
+
+The position of the chart in the worksheet is controlled by the
+:func:`set_size()` method.
+
+It is also possible to change the layout of the following chart sub-objects::
+
+ plotarea
+ legend
+ title
+ x_axis caption
+ y_axis caption
+
+Here are some examples::
+
+ chart.set_plotarea({
+ 'layout': {
+ 'x': 0.13,
+ 'y': 0.26,
+ 'width': 0.73,
+ 'height': 0.57,
+ }
+ })
+
+ chart.set_legend({
+ 'layout': {
+ 'x': 0.80,
+ 'y': 0.37,
+ 'width': 0.12,
+ 'height': 0.25,
+ }
+ })
+
+ chart.set_title({
+ 'name': 'Title',
+ 'overlay': True,
+ 'layout': {
+ 'x': 0.42,
+ 'y': 0.14,
+ }
+ })
+
+ chart.set_x_axis({
+ 'name': 'X axis',
+ 'name_layout': {
+ 'x': 0.34,
+ 'y': 0.85,
+ }
+ })
+
+See :func:`set_plotarea()`, :func:`set_legend()`, :func:`set_title()` and
+:func:`set_x_axis()`,
+
+.. note::
+
+ It is only possible to change the width and height for the ``plotarea``
+ and ``legend`` objects. For the other text based objects the width and
+ height are changed by the font dimensions.
+
+The layout units must be a float in the range ``0 < x <= 1`` and are expressed
+as a percentage of the chart dimensions as shown below:
+
+.. image:: _images/chart_layout.png
+ :scale: 75 %
+
+From this the layout units are calculated as follows::
+
+ layout:
+ x = a / W
+ y = b / H
+ width = w / W
+ height = h / H
+
+These units are cumbersome and can vary depending on other elements in the
+chart such as text lengths. However, these are the units that are required by
+Excel to allow relative positioning. Some trial and error is generally
+required.
+
+.. note::
+
+ The ``plotarea`` origin is the top left corner in the plotarea itself and
+ does not take into account the axes.
+
+
+.. _date_category_axes:
+
+Date Category Axes
+------------------
+
+Date Category Axes are category axes that display time or date information. In
+XlsxWriter Date Category Axes are set using the ``date_axis`` option in
+:func:`set_x_axis` or :func:`set_y_axis`::
+
+ chart.set_x_axis({'date_axis': True})
+
+In general you should also specify a number format for a date axis although
+Excel will usually default to the same format as the data being plotted::
+
+ chart.set_x_axis({
+ 'date_axis': True,
+ 'num_format': 'dd/mm/yyyy',
+ })
+
+Excel doesn't normally allow minimum and maximum values to be set for category
+axes. However, date axes are an exception. The ``min`` and ``max`` values
+should be set as Excel times or dates::
+
+ chart.set_x_axis({
+ 'date_axis': True,
+ 'min': date(2013, 1, 2),
+ 'max': date(2013, 1, 9),
+ 'num_format': 'dd/mm/yyyy',
+ })
+
+For date axes it is also possible to set the type of the major and minor units::
+
+ chart.set_x_axis({
+ 'date_axis': True,
+ 'minor_unit': 4,
+ 'minor_unit_type': 'months',
+ 'major_unit': 1,
+ 'major_unit_type': 'years',
+ 'num_format': 'dd/mm/yyyy',
+ })
+
+See :ref:`ex_chart_date_axis`.
+
+.. _chart_secondary_axes:
+
+Chart Secondary Axes
+--------------------
+
+It is possible to add a secondary axis of the same type to a chart by setting
+the ``y2_axis`` or ``x2_axis`` property of the series::
+
+ import xlsxwriter
+
+ workbook = xlsxwriter.Workbook('chart_secondary_axis.xlsx')
+ worksheet = workbook.add_worksheet()
+
+ data = [
+ [2, 3, 4, 5, 6, 7],
+ [10, 40, 50, 20, 10, 50],
+ ]
+
+ worksheet.write_column('A2', data[0])
+ worksheet.write_column('B2', data[1])
+
+ chart = workbook.add_chart({'type': 'line'})
+
+ # Configure a series with a secondary axis.
+ chart.add_series({
+ 'values': '=Sheet1!$A$2:$A$7',
+ 'y2_axis': True,
+ })
+
+ # Configure a primary (default) Axis.
+ chart.add_series({
+ 'values': '=Sheet1!$B$2:$B$7',
+ })
+
+ chart.set_legend({'position': 'none'})
+
+ chart.set_y_axis({'name': 'Primary Y axis'})
+ chart.set_y2_axis({'name': 'Secondary Y axis'})
+
+ worksheet.insert_chart('D2', chart)
+
+ workbook.close()
+
+.. image:: _images/chart_secondary_axis2.png
+ :scale: 75 %
+
+It is also possible to have a secondary, combined, chart either with a shared
+or secondary axis, see below.
+
+
+.. _chart_combined_charts:
+
+Combined Charts
+---------------
+
+It is also possible to combine two different chart types, for example a column
+and line chart to create a Pareto chart using the Chart :func:`combine()`
+method:
+
+.. image:: _images/chart_pareto.png
+ :scale: 75 %
+
+The combined charts can share the same Y axis like the following example::
+
+ # Usual setup to create workbook and add data...
+
+ # Create a new column chart. This will use this as the primary chart.
+ column_chart = workbook.add_chart({'type': 'column'})
+
+ # Configure the data series for the primary chart.
+ column_chart.add_series({
+ 'name': '=Sheet1!B1',
+ 'categories': '=Sheet1!A2:A7',
+ 'values': '=Sheet1!B2:B7',
+ })
+
+ # Create a new column chart. This will use this as the secondary chart.
+ line_chart = workbook.add_chart({'type': 'line'})
+
+ # Configure the data series for the secondary chart.
+ line_chart.add_series({
+ 'name': '=Sheet1!C1',
+ 'categories': '=Sheet1!A2:A7',
+ 'values': '=Sheet1!C2:C7',
+ })
+
+ # Combine the charts.
+ column_chart.combine(line_chart)
+
+ # Add a chart title and some axis labels. Note, this is done via the
+ # primary chart.
+ column_chart.set_title({ 'name': 'Combined chart - same Y axis'})
+ column_chart.set_x_axis({'name': 'Test number'})
+ column_chart.set_y_axis({'name': 'Sample length (mm)'})
+
+ # Insert the chart into the worksheet
+ worksheet.insert_chart('E2', column_chart)
+
+
+.. image:: _images/chart_combined1.png
+ :scale: 75 %
+
+
+The secondary chart can also be placed on a secondary axis using the methods
+shown in the previous section.
+
+In this case it is just necessary to add a ``y2_axis`` parameter to the series
+and, if required, add a title using :func:`set_y2_axis()`. The following are
+the additions to the previous example to place the secondary chart on the
+secondary axis::
+
+ # ...
+ line_chart.add_series({
+ 'name': '=Sheet1!C1',
+ 'categories': '=Sheet1!A2:A7',
+ 'values': '=Sheet1!C2:C7',
+ 'y2_axis': True,
+ })
+
+ # Add a chart title and some axis labels.
+ # ...
+ column_chart.set_y2_axis({'name': 'Target length (mm)'})
+
+
+.. image:: _images/chart_combined2.png
+ :scale: 75 %
+
+The examples above use the concept of a *primary* and *secondary* chart. The
+primary chart is the chart that defines the primary X and Y axis. It is also
+used for setting all chart properties apart from the secondary data
+series. For example the chart title and axes properties should be set via the
+primary chart.
+
+See also :ref:`ex_chart_combined` and :ref:`ex_chart_pareto` for more detailed
+examples.
+
+There are some limitations on combined charts:
+
+* Pie charts cannot currently be combined.
+* Scatter charts cannot currently be used as a primary chart but they can be
+ used as a secondary chart.
+* Bar charts can only combined secondary charts on a secondary axis. This is
+ an Excel limitation.
+
+
+Chartsheets
+-----------
+
+The examples shown above and in general the most common type of charts in Excel
+are embedded charts.
+
+However, it is also possible to create "Chartsheets" which are worksheets that
+are comprised of a single chart:
+
+.. image:: _images/chartsheet.png
+
+See :ref:`chartsheet` for details.
+
+
+.. _charts_from_tables:
+
+Charts from Worksheet Tables
+----------------------------
+
+Charts can by created from :ref:`Worksheet Tables <tables>`. However, Excel
+has a limitation where the data series ``name``, if specified, must refer to a
+cell within the table (usually one of the headers).
+
+To workaround this Excel limitation you can specify a user defined name in the
+table and refer to that from the chart::
+
+ import xlsxwriter
+
+ workbook = xlsxwriter.Workbook('chart_pie.xlsx')
+
+ worksheet = workbook.add_worksheet()
+
+ data = [
+ ['Apple', 60],
+ ['Cherry', 30],
+ ['Pecan', 10],
+ ]
+
+ worksheet.add_table('A1:B4', {'data': data,
+ 'columns': [{'header': 'Types'},
+ {'header': 'Number'}]}
+ )
+
+ chart = workbook.add_chart({'type': 'pie'})
+
+ chart.add_series({
+ 'name': '=Sheet1!$A$1',
+ 'categories': '=Sheet1!$A$2:$A$4',
+ 'values': '=Sheet1!$B$2:$B$4',
+ })
+
+ worksheet.insert_chart('D2', chart)
+
+ workbook.close()
+
+
+Chart Limitations
+-----------------
+
+The following chart features aren't supported in XlsxWriter:
+
+* 3D charts and controls.
+* Bubble, Surface or other chart types not listed in :ref:`chart_class`.
+
+
+Chart Examples
+--------------
+
+See :ref:`chart_examples`.
diff --git a/dev/docs/source/working_with_colors.rst b/dev/docs/source/working_with_colors.rst
new file mode 100644
index 0000000..48c252f
--- /dev/null
+++ b/dev/docs/source/working_with_colors.rst
@@ -0,0 +1,51 @@
+.. _colors:
+
+Working with Colors
+===================
+
+Throughout XlsxWriter colors are specified using a Html style ``#RRGGBB``
+value. For example with a :ref:`Format <format>` object::
+
+ cell_format.set_font_color('#FF0000')
+
+For backward compatibility a limited number of color names are supported::
+
+ cell_format.set_font_color('red')
+
+The color names and corresponding ``#RRGGBB`` value are shown below:
+
++------------+----------------+
+| Color name | RGB color code |
++============+================+
+| black | ``#000000`` |
++------------+----------------+
+| blue | ``#0000FF`` |
++------------+----------------+
+| brown | ``#800000`` |
++------------+----------------+
+| cyan | ``#00FFFF`` |
++------------+----------------+
+| gray | ``#808080`` |
++------------+----------------+
+| green | ``#008000`` |
++------------+----------------+
+| lime | ``#00FF00`` |
++------------+----------------+
+| magenta | ``#FF00FF`` |
++------------+----------------+
+| navy | ``#000080`` |
++------------+----------------+
+| orange | ``#FF6600`` |
++------------+----------------+
+| pink | ``#FF00FF`` |
++------------+----------------+
+| purple | ``#800080`` |
++------------+----------------+
+| red | ``#FF0000`` |
++------------+----------------+
+| silver | ``#C0C0C0`` |
++------------+----------------+
+| white | ``#FFFFFF`` |
++------------+----------------+
+| yellow | ``#FFFF00`` |
++------------+----------------+
diff --git a/dev/docs/source/working_with_conditional_formats.rst b/dev/docs/source/working_with_conditional_formats.rst
new file mode 100644
index 0000000..131c6ae
--- /dev/null
+++ b/dev/docs/source/working_with_conditional_formats.rst
@@ -0,0 +1,736 @@
+.. _working_with_conditional_formats:
+
+Working with Conditional Formatting
+===================================
+
+Conditional formatting is a feature of Excel which allows you to apply a format
+to a cell or a range of cells based on certain criteria.
+
+For example the following rules are used to highlight cells in the
+:ref:`conditional_format.py <ex_cond_format>` example::
+
+ worksheet.conditional_format('B3:K12', {'type': 'cell',
+ 'criteria': '>=',
+ 'value': 50,
+ 'format': format1})
+
+ worksheet.conditional_format('B3:K12', {'type': 'cell',
+ 'criteria': '<',
+ 'value': 50,
+ 'format': format2})
+
+Which gives criteria like this:
+
+.. image:: _images/conditional_format3.png
+
+And output which looks like this:
+
+.. image:: _images/conditional_format1.png
+
+It is also possible to create color scales and data bars:
+
+.. image:: _images/conditional_format2.png
+
+
+The conditional_format() method
+-------------------------------
+
+The :func:`conditional_format` worksheet method is used to apply formatting
+based on user defined criteria to an XlsxWriter file.
+
+The conditional format can be applied to a single cell or a range of cells. As
+usual you can use A1 or Row/Column notation (:ref:`cell_notation`).
+
+With Row/Column notation you must specify all four cells in the range:
+``(first_row, first_col, last_row, last_col)``. If you need to refer to a
+single cell set the ``last_*`` values equal to the ``first_*`` values. With A1
+notation you can refer to a single cell or a range of cells::
+
+ worksheet.conditional_format(0, 0, 4, 1, {...})
+ worksheet.conditional_format('B1', {...})
+ worksheet.conditional_format('C1:E5', {...})
+
+The options parameter in ``conditional_format()`` must be a dictionary
+containing the parameters that describe the type and style of the conditional
+format. The main parameters are:
+
+* ``type``
+* ``format``
+* ``criteria``
+* ``value``
+* ``minimum``
+* ``maximum``
+
+Other, less commonly used parameters are:
+
+* ``min_type``
+* ``mid_type``
+* ``max_type``
+* ``min_value``
+* ``mid_value``
+* ``max_value``
+* ``min_color``
+* ``mid_color``
+* ``max_color``
+* ``bar_color``
+* ``multi_range``
+
+
+Conditional Format Options
+--------------------------
+
+The conditional format options that can be used with :func:`conditional_format`
+are explained in the following sections.
+
+type
+****
+
+The ``type`` option is a required parameter and it has no default value.
+Allowable ``type`` values and their associated parameters are:
+
++---------------+------------+
+| Type | Parameters |
++===============+============+
+| cell | criteria |
++---------------+------------+
+| | value |
++---------------+------------+
+| | minimum |
++---------------+------------+
+| | maximum |
++---------------+------------+
+| date | criteria |
++---------------+------------+
+| | value |
++---------------+------------+
+| | minimum |
++---------------+------------+
+| | maximum |
++---------------+------------+
+| time_period | criteria |
++---------------+------------+
+| text | criteria |
++---------------+------------+
+| | value |
++---------------+------------+
+| average | criteria |
++---------------+------------+
+| duplicate | (none) |
++---------------+------------+
+| unique | (none) |
++---------------+------------+
+| top | criteria |
++---------------+------------+
+| | value |
++---------------+------------+
+| bottom | criteria |
++---------------+------------+
+| | value |
++---------------+------------+
+| blanks | (none) |
++---------------+------------+
+| no_blanks | (none) |
++---------------+------------+
+| errors | (none) |
++---------------+------------+
+| no_errors | (none) |
++---------------+------------+
+| 2_color_scale | min_type |
++---------------+------------+
+| | max_type |
++---------------+------------+
+| | min_value |
++---------------+------------+
+| | max_value |
++---------------+------------+
+| | min_color |
++---------------+------------+
+| | max_color |
++---------------+------------+
+| 3_color_scale | min_type |
++---------------+------------+
+| | mid_type |
++---------------+------------+
+| | max_type |
++---------------+------------+
+| | min_value |
++---------------+------------+
+| | mid_value |
++---------------+------------+
+| | max_value |
++---------------+------------+
+| | min_color |
++---------------+------------+
+| | mid_color |
++---------------+------------+
+| | max_color |
++---------------+------------+
+| data_bar | min_type |
++---------------+------------+
+| | max_type |
++---------------+------------+
+| | min_value |
++---------------+------------+
+| | max_value |
++---------------+------------+
+| | bar_color |
++---------------+------------+
+| formula | criteria |
++---------------+------------+
+
+
+All conditional formatting types have an associated :ref:`Format <format>`
+parameter, see below.
+
+
+type: cell
+**********
+
+This is the most common conditional formatting type. It is used when a format
+is applied to a cell based on a simple criterion.
+
+For example using a single cell and the ``greater than`` criteria::
+
+ worksheet.conditional_format('A1', {'type': 'cell',
+ 'criteria': 'greater than',
+ 'value': 5,
+ 'format': red_format})
+
+Or, using a range and the ``between`` criteria::
+
+ worksheet.conditional_format('C1:C4', {'type': 'cell',
+ 'criteria': 'between',
+ 'minimum': 20,
+ 'maximum': 30,
+ 'format': green_format})
+
+Other types are shown below, after the other main options.
+
+
+criteria:
+*********
+
+The ``criteria`` parameter is used to set the criteria by which the cell data
+will be evaluated. It has no default value. The most common criteria as
+applied to ``{'type': 'cell'}`` are:
+
++------------------------------+--------+
+| ``between`` | |
++------------------------------+--------+
+| ``not between`` | |
++------------------------------+--------+
+| ``equal to`` | ``==`` |
++------------------------------+--------+
+| ``not equal to`` | ``!=`` |
++------------------------------+--------+
+| ``greater than`` | ``>`` |
++------------------------------+--------+
+| ``less than`` | ``<`` |
++------------------------------+--------+
+| ``greater than or equal to`` | ``>=`` |
++------------------------------+--------+
+| ``less than or equal to`` | ``<=`` |
++------------------------------+--------+
+
+
+You can either use Excel's textual description strings, in the first column
+above, or the more common symbolic alternatives.
+
+Additional criteria which are specific to other conditional format types are
+shown in the relevant sections below.
+
+
+value:
+******
+
+The ``value`` is generally used along with the ``criteria`` parameter to set
+the rule by which the cell data will be evaluated::
+
+ worksheet.conditional_format('A1', {'type': 'cell',
+ 'criteria': 'greater than',
+ 'value': 5,
+ 'format': red_format})
+
+
+The ``value`` property can also be an cell reference::
+
+ worksheet.conditional_format('A1', {'type': 'cell',
+ 'criteria': 'greater than',
+ 'value': '$C$1',
+ 'format': red_format})
+
+.. note::
+
+ In general any ``value`` property that refers to a cell reference should
+ use an :ref:`absolute reference <abs_reference>`, especially if the
+ conditional formatting is applied to a range of values. Without an absolute
+ cell reference the conditional format will not be applied correctly by
+ Excel from the first cell in the formatted range.
+
+
+format:
+*******
+
+The ``format`` parameter is used to specify the format that will be applied to
+the cell when the conditional formatting criterion is met. The format is
+created using the :func:`add_format()` method in the same way as cell formats::
+
+ format1 = workbook.add_format({'bold': 1, 'italic': 1})
+
+ worksheet.conditional_format('A1', {'type': 'cell',
+ 'criteria': '>',
+ 'value': 5,
+ 'format': format1})
+
+.. Note::
+
+ In Excel, a conditional format is superimposed over the existing cell
+ format and not all cell format properties can be modified. Properties that
+ **cannot** be modified in a conditional format are font name, font size,
+ superscript and subscript, diagonal borders, all alignment properties and
+ all protection properties.
+
+Excel specifies some default formats to be used with conditional formatting.
+These can be replicated using the following XlsxWriter formats::
+
+ # Light red fill with dark red text.
+ format1 = workbook.add_format({'bg_color': '#FFC7CE',
+ 'font_color': '#9C0006'})
+
+ # Light yellow fill with dark yellow text.
+ format2 = workbook.add_format({'bg_color': '#FFEB9C',
+ 'font_color': '#9C6500'})
+
+ # Green fill with dark green text.
+ format3 = workbook.add_format({'bg_color': '#C6EFCE',
+ 'font_color': '#006100'})
+
+See also :ref:`format`.
+
+minimum:
+********
+
+The ``minimum`` parameter is used to set the lower limiting value when the
+``criteria`` is either ``'between'`` or ``'not between'``::
+
+ worksheet.conditional_format('A1', {'type': 'cell',
+ 'criteria': 'between',
+ 'minimum': 2,
+ 'maximum': 6,
+ 'format': format1,
+ })
+
+maximum:
+********
+
+The ``maximum`` parameter is used to set the upper limiting value when the
+``criteria`` is either ``'between'`` or ``'not between'``. See the previous
+example.
+
+
+type: date
+**********
+
+The ``date`` type is similar the ``cell`` type and uses the same criteria and
+values. However, the ``value``, ``minimum`` and ``maximum`` properties are
+specified as a datetime object as shown in :ref:`working_with_dates_and_time`::
+
+
+ date = datetime.datetime.strptime('2011-01-01', "%Y-%m-%d")
+
+ worksheet.conditional_format('A1:A4', {'type': 'date',
+ 'criteria': 'greater than',
+ 'value': date,
+ 'format': format1})
+
+
+type: time_period
+*****************
+
+The ``time_period`` type is used to specify Excel's "Dates Occurring" style
+conditional format::
+
+ worksheet.conditional_format('A1:A4', {'type': 'time_period',
+ 'criteria': 'yesterday',
+ 'format': format1})
+
+The period is set in the ``criteria`` and can have one of the following values::
+
+ 'criteria': 'yesterday',
+ 'criteria': 'today',
+ 'criteria': 'last 7 days',
+ 'criteria': 'last week',
+ 'criteria': 'this week',
+ 'criteria': 'continue week',
+ 'criteria': 'last month',
+ 'criteria': 'this month',
+ 'criteria': 'continue month'
+
+
+type: text
+**********
+
+The ``text`` type is used to specify Excel's "Specific Text" style conditional
+format. It is used to do simple string matching using the ``criteria`` and
+``value`` parameters::
+
+ worksheet.conditional_format('A1:A4', {'type': 'text',
+ 'criteria': 'containing',
+ 'value': 'foo',
+ 'format': format1})
+
+The ``criteria`` can have one of the following values::
+
+ 'criteria': 'containing',
+ 'criteria': 'not containing',
+ 'criteria': 'begins with',
+ 'criteria': 'ends with',
+
+The ``value`` parameter should be a string or single character.
+
+
+type: average
+*************
+
+The ``average`` type is used to specify Excel's "Average" style conditional
+format::
+
+ worksheet.conditional_format('A1:A4', {'type': 'average',
+ 'criteria': 'above',
+ 'format': format1})
+
+The type of average for the conditional format range is specified by the
+``criteria``::
+
+ 'criteria': 'above',
+ 'criteria': 'below',
+ 'criteria': 'equal or above',
+ 'criteria': 'equal or below',
+ 'criteria': '1 std dev above',
+ 'criteria': '1 std dev below',
+ 'criteria': '2 std dev above',
+ 'criteria': '2 std dev below',
+ 'criteria': '3 std dev above',
+ 'criteria': '3 std dev below',
+
+
+type: duplicate
+***************
+
+The ``duplicate`` type is used to highlight duplicate cells in a range::
+
+ worksheet.conditional_format('A1:A4', {'type': 'duplicate',
+ 'format': format1})
+
+
+type: unique
+************
+
+The ``unique`` type is used to highlight unique cells in a range::
+
+ worksheet.conditional_format('A1:A4', {'type': 'unique',
+ 'format': format1})
+
+
+type: top
+*********
+
+The ``top`` type is used to specify the top ``n`` values by number or
+percentage in a range::
+
+ worksheet.conditional_format('A1:A4', {'type': 'top',
+ 'value': 10,
+ 'format': format1})
+
+The ``criteria`` can be used to indicate that a percentage condition is
+required::
+
+ worksheet.conditional_format('A1:A4', {'type': 'top',
+ 'value': 10,
+ 'criteria': '%',
+ 'format': format1})
+
+
+type: bottom
+************
+
+The ``bottom`` type is used to specify the bottom ``n`` values by number or
+percentage in a range.
+
+It takes the same parameters as ``top``, see above.
+
+
+type: blanks
+************
+
+The ``blanks`` type is used to highlight blank cells in a range::
+
+ worksheet.conditional_format('A1:A4', {'type': 'blanks',
+ 'format': format1})
+
+
+type: no_blanks
+***************
+
+The ``no_blanks`` type is used to highlight non blank cells in a range::
+
+ worksheet.conditional_format('A1:A4', {'type': 'no_blanks',
+ 'format': format1})
+
+
+type: errors
+************
+
+The ``errors`` type is used to highlight error cells in a range::
+
+ worksheet.conditional_format('A1:A4', {'type': 'errors',
+ 'format': format1})
+
+
+type: no_errors
+***************
+
+The ``no_errors`` type is used to highlight non error cells in a range::
+
+ worksheet.conditional_format('A1:A4', {'type': 'no_errors',
+ 'format': format1})
+
+
+type: 2_color_scale
+*******************
+
+The ``2_color_scale`` type is used to specify Excel's "2 Color Scale" style
+conditional format::
+
+ worksheet.conditional_format('A1:A12', {'type': '2_color_scale'})
+
+.. image:: _images/conditional_format4.png
+
+This conditional type can be modified with ``min_type``, ``max_type``,
+``min_value``, ``max_value``, ``min_color`` and ``max_color``, see below.
+
+type: 3_color_scale
+*******************
+
+The ``3_color_scale`` type is used to specify Excel's "3 Color Scale" style
+conditional format::
+
+ worksheet.conditional_format('A1:A12', {'type': '3_color_scale'})
+
+This conditional type can be modified with ``min_type``, ``mid_type``,
+``max_type``, ``min_value``, ``mid_value``, ``max_value``, ``min_color``,
+``mid_color`` and ``max_color``, see below.
+
+
+type: data_bar
+**************
+
+The ``data_bar`` type is used to specify Excel's "Data Bar" style conditional
+format::
+
+ worksheet.conditional_format('A1:A12', {'type': 'data_bar'})
+
+This conditional type can be modified with ``min_type``, ``max_type``,
+``min_value``, ``max_value`` and ``bar_color``, see below.
+
+
+type: formula
+*************
+
+The ``formula`` type is used to specify a conditional format based on a user
+defined formula::
+
+ worksheet.conditional_format('A1:A4', {'type': 'formula',
+ 'criteria': '=$A$1>5',
+ 'format': format1})
+
+The formula is specified in the ``criteria``.
+
+Formulas must be written with the US style separator/range operator which is a
+comma (not semi-colon) and should follow the same rules as
+:func:`write_formula`. Also any cell or range references in the formula should
+be :ref:`absolute references <abs_reference>` if they are applied to the full
+range of the conditional format. See the note in the ``value`` section above.
+
+
+min_type:
+*********
+
+The ``min_type`` and ``max_type`` properties are available when the conditional
+formatting type is ``2_color_scale``, ``3_color_scale`` or ``data_bar``. The
+``mid_type`` is available for ``3_color_scale``. The properties are used as
+follows::
+
+ worksheet.conditional_format('A1:A12', {'type': '2_color_scale',
+ 'min_type': 'percent',
+ 'max_type': 'percent'})
+
+The available min/mid/max types are::
+
+ min (for min_type only)
+ num
+ percent
+ percentile
+ formula
+ max (for max_type only)
+
+
+mid_type:
+*********
+
+Used for ``3_color_scale``. Same as ``min_type``, see above.
+
+
+max_type:
+*********
+
+Same as ``min_type``, see above.
+
+
+min_value:
+**********
+
+The ``min_value`` and ``max_value`` properties are available when the
+conditional formatting type is ``2_color_scale``, ``3_color_scale`` or
+``data_bar``. The ``mid_value`` is available for ``3_color_scale``. The
+properties are used as follows::
+
+ worksheet.conditional_format('A1:A12', {'type': '2_color_scale',
+ 'min_value': 10,
+ 'max_value': 90})
+
+
+mid_value:
+**********
+
+Used for ``3_color_scale``. Same as ``min_value``, see above.
+
+
+max_value:
+**********
+
+Same as ``min_value``, see above.
+
+
+min_color:
+**********
+
+The ``min_color`` and ``max_color`` properties are available when the
+conditional formatting type is ``2_color_scale``, ``3_color_scale`` or
+``data_bar``. The ``mid_color`` is available for ``3_color_scale``. The
+properties are used as follows::
+
+ worksheet.conditional_format('A1:A12', {'type': '2_color_scale',
+ 'min_color': '#C5D9F1',
+ 'max_color': '#538ED5'})
+
+The color can be a Html style ``#RRGGBB`` string or a limited number named
+colors, see :ref:`colors`.
+
+mid_color:
+**********
+
+Used for ``3_color_scale``. Same as ``min_color``, see above.
+
+max_color:
+**********
+
+Same as ``min_color``, see above.
+
+bar_color:
+**********
+
+Used for ``data_bar``. Same as ``min_color``, see above.
+
+
+multi_range:
+************
+
+The ``multi_range`` option is used to extend a conditional format over
+non-contiguous ranges.
+
+It is possible to apply the conditional format to different cell ranges in a
+worksheet using multiple calls to ``conditional_format()``. However, as a
+minor optimization it is also possible in Excel to apply the same conditional
+format to different non-contiguous cell ranges.
+
+This is replicated in ``conditional_format()`` using the ``multi_range``
+option. The range must contain the primary range for the conditional format
+and any others separated by spaces.
+
+For example to apply one conditional format to two ranges, ``'B3:K6'`` and
+``'B9:K12'``::
+
+ worksheet.conditional_format('B3:K6', {'type': 'cell',
+ 'criteria': '>=',
+ 'value': 50,
+ 'format': format1,
+ 'multi_range': 'B3:K6 B9:K12'})
+
+
+Conditional Formatting Examples
+-------------------------------
+
+Highlight cells greater than an integer value::
+
+ worksheet.conditional_format('A1:F10', {'type': 'cell',
+ 'criteria': 'greater than',
+ 'value': 5,
+ 'format': format1})
+
+Highlight cells greater than a value in a reference cell::
+
+ worksheet.conditional_format('A1:F10', {'type': 'cell',
+ 'criteria': 'greater than',
+ 'value': 'H1',
+ 'format': format1})
+
+Highlight cells more recent (greater) than a certain date::
+
+ date = datetime.datetime.strptime('2011-01-01', "%Y-%m-%d")
+
+ worksheet.conditional_format('A1:F10', {'type': 'date',
+ 'criteria': 'greater than',
+ 'value': date,
+ 'format': format1})
+
+Highlight cells with a date in the last seven days::
+
+ worksheet.conditional_format('A1:F10', {'type': 'time_period',
+ 'criteria': 'last 7 days',
+ 'format': format1})
+
+Highlight cells with strings starting with the letter ``b``::
+
+ worksheet.conditional_format('A1:F10', {'type': 'text',
+ 'criteria': 'begins with',
+ 'value': 'b',
+ 'format': format1})
+
+Highlight cells that are 1 standard deviation above the average for the range::
+
+ worksheet.conditional_format('A1:F10', {'type': 'average',
+ 'format': format1})
+
+Highlight duplicate cells in a range::
+
+ worksheet.conditional_format('A1:F10', {'type': 'duplicate',
+ 'format': format1})
+
+Highlight unique cells in a range::
+
+ worksheet.conditional_format('A1:F10', {'type': 'unique',
+ 'format': format1})
+
+Highlight the top 10 cells::
+
+ worksheet.conditional_format('A1:F10', {'type': 'top',
+ 'value': 10,
+ 'format': format1})
+
+Highlight blank cells::
+
+ worksheet.conditional_format('A1:F10', {'type': 'blanks',
+ 'format': format1})
+
+See also :ref:`ex_cond_format`.
diff --git a/dev/docs/source/working_with_data_validation.rst b/dev/docs/source/working_with_data_validation.rst
new file mode 100644
index 0000000..48cb6f1
--- /dev/null
+++ b/dev/docs/source/working_with_data_validation.rst
@@ -0,0 +1,399 @@
+.. _working_with_data_validation:
+
+Working with Data Validation
+============================
+
+Data validation is a feature of Excel which allows you to restrict the data
+that a users enters in a cell and to display associated help and warning
+messages. It also allows you to restrict input to values in a drop down list.
+
+A typical use case might be to restrict data in a cell to integer values in a
+certain range, to provide a help message to indicate the required value and to
+issue a warning if the input data doesn't meet the stated criteria. In
+XlsxWriter we could do that as follows::
+
+ worksheet.data_validation('B25', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 100,
+ 'input_title': 'Enter an integer:',
+ 'input_message': 'between 1 and 100'})
+
+.. image:: _images/data_validate1.png
+
+If the user inputs a value that doesn't match the specified criteria an error
+message is displayed:
+
+.. image:: _images/data_validate2.png
+
+
+For more information on data validation see the Microsoft support article
+"Description and examples of data validation in Excel":
+http://support.microsoft.com/kb/211485.
+
+The following sections describe how to use the ``data_validation()`` method and
+its various options.
+
+
+data_validation()
+-----------------
+
+The :func:`data_validation()` method is used to construct an Excel data
+validation.
+
+The data validation can be applied to a single cell or a range of cells. As
+usual you can use A1 or Row/Column notation, see :ref:`cell_notation`.
+
+With Row/Column notation you must specify all four cells in the range:
+``(first_row, first_col, last_row, last_col)``. If you need to refer to a
+single cell set the `last_` values equal to the `first_` values. With A1
+notation you can refer to a single cell or a range of cells::
+
+ worksheet.data_validation(0, 0, 4, 1, {...})
+ worksheet.data_validation('B1', {...})
+ worksheet.data_validation('C1:E5', {...})
+
+The options parameter in ``data_validation()`` must be a dictionary containing
+the parameters that describe the type and style of the data validation. The
+main parameters are:
+
++-------------------+-------------+------------+
+| ``validate`` | | |
++-------------------+-------------+------------+
+| ``criteria`` | | |
++-------------------+-------------+------------+
+| ``value`` | ``minimum`` | ``source`` |
++-------------------+-------------+------------+
+| ``maximum`` | | |
++-------------------+-------------+------------+
+| ``ignore_blank`` | | |
++-------------------+-------------+------------+
+| ``dropdown`` | | |
++-------------------+-------------+------------+
+| ``input_title`` | | |
++-------------------+-------------+------------+
+| ``input_message`` | | |
++-------------------+-------------+------------+
+| ``show_input`` | | |
++-------------------+-------------+------------+
+| ``error_title`` | | |
++-------------------+-------------+------------+
+| ``error_message`` | | |
++-------------------+-------------+------------+
+| ``error_type`` | | |
++-------------------+-------------+------------+
+| ``show_error`` | | |
++-------------------+-------------+------------+
+
+These parameters are explained in the following sections. Most of the
+parameters are optional, however, you will generally require the three main
+options ``validate``, ``criteria`` and ``value``::
+
+ worksheet.data_validation('A1', {'validate': 'integer',
+ 'criteria': '>',
+ 'value': 100})
+
+
+validate
+********
+
+The ``validate`` parameter is used to set the type of data that you wish to
+validate::
+
+ worksheet.data_validation('A1', {'validate': 'integer',
+ 'criteria': '>',
+ 'value': 100})
+
+It is always required and it has no default value. Allowable values are::
+
+ integer
+ decimal
+ list
+ date
+ time
+ length
+ custom
+ any
+
+* **integer**: restricts the cell to integer values. Excel refers to this as
+ 'whole number'.
+* **decimal**: restricts the cell to decimal values.
+* **list**: restricts the cell to a set of user specified values. These can
+ be passed in a Python list or as an Excel cell range.
+* **date**: restricts the cell to date values specified as a datetime object
+ as shown in :ref:`working_with_dates_and_time`.
+* **time**: restricts the cell to time values specified as a datetime object
+ as shown in :ref:`working_with_dates_and_time`.
+* **length**: restricts the cell data based on an integer string length.
+ Excel refers to this as 'Text length'.
+* **custom**: restricts the cell based on an external Excel formula that
+ returns a ``TRUE/FALSE`` value.
+* **any**: is used to specify that the type of data is unrestricted. It is
+ mainly used for specifying cell input messages without a data validation.
+
+
+criteria
+********
+
+The ``criteria`` parameter is used to set the criteria by which the data in the
+cell is validated. It is almost always required except for the ``list`` and
+``custom`` validate options. It has no default value::
+
+ worksheet.data_validation('A1', {'validate': 'integer',
+ 'criteria': '>',
+ 'value': 100})
+
+
+Allowable values are:
+
++------------------------------+--------+
+| ``between`` | |
++------------------------------+--------+
+| ``not between`` | |
++------------------------------+--------+
+| ``equal to`` | ``==`` |
++------------------------------+--------+
+| ``not equal to`` | ``!=`` |
++------------------------------+--------+
+| ``greater than`` | ``>`` |
++------------------------------+--------+
+| ``less than`` | ``<`` |
++------------------------------+--------+
+| ``greater than or equal to`` | ``>=`` |
++------------------------------+--------+
+| ``less than or equal to`` | ``<=`` |
++------------------------------+--------+
+
+You can either use Excel's textual description strings, in the first column
+above, or the more common symbolic alternatives. The following are equivalent::
+
+ worksheet.data_validation('A1', {'validate': 'integer',
+ 'criteria': '>',
+ 'value': 100})
+
+ worksheet.data_validation('A1', {'validate': 'integer',
+ 'criteria': 'greater than',
+ 'value': 100})
+
+The ``list`` and ``custom`` validate options don't require a ``criteria``. If
+you specify one it will be ignored::
+
+ worksheet.data_validation('B13', {'validate': 'list',
+ 'source': ['open', 'high', 'close']})
+
+ worksheet.data_validation('B23', {'validate': 'custom',
+ 'value': '=AND(F5=50,G5=60)'})
+
+
+value, minimum, source
+**********************
+
+The ``value`` parameter is used to set the limiting value to which the
+``criteria`` is applied. It is always required and it has no default value.
+You can also use the synonyms ``minimum`` or ``source`` to make the validation
+a little clearer and closer to Excel's description of the parameter::
+
+ # Use 'value'
+ worksheet.data_validation('A1', {'validate': 'integer',
+ 'criteria': 'greater than',
+ 'value': 100})
+
+ # Use 'minimum'
+ worksheet.data_validation('B11', {'validate': 'decimal',
+ 'criteria': 'between',
+ 'minimum': 0.1,
+ 'maximum': 0.5})
+
+ # Use 'source'
+ worksheet.data_validation('B10', {'validate': 'list',
+ 'source': '=$E$4:$G$4'})
+
+
+maximum
+*******
+
+The ``maximum`` parameter is used to set the upper limiting value when the
+``criteria`` is either ``'between'`` or ``'not between'``::
+
+ worksheet.data_validation('B11', {'validate': 'decimal',
+ 'criteria': 'between',
+ 'minimum': 0.1,
+ 'maximum': 0.5})
+
+
+ignore_blank
+************
+
+The ``ignore_blank`` parameter is used to toggle on and off the 'Ignore blank'
+option in the Excel data validation dialog. When the option is on the data
+validation is not applied to blank data in the cell. It is on by default::
+
+ worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ 'ignore_blank': False,
+ })
+
+
+dropdown
+********
+
+The ``dropdown`` parameter is used to toggle on and off the 'In-cell dropdown'
+option in the Excel data validation dialog. When the option is on a dropdown
+list will be shown for ``list`` validations. It is on by default.
+
+
+input_title
+***********
+
+The ``input_title`` parameter is used to set the title of the input message
+that is displayed when a cell is entered. It has no default value and is only
+displayed if the input message is displayed. See the ``input_message``
+parameter below.
+
+The maximum title length is 32 characters.
+
+
+input_message
+*************
+
+The ``input_message`` parameter is used to set the input message that is
+displayed when a cell is entered. It has no default value::
+
+ worksheet.data_validation('B25', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 100,
+ 'input_title': 'Enter an integer:',
+ 'input_message': 'between 1 and 100'})
+
+The input message generated from the above example is:
+
+.. image:: _images/data_validate3.png
+
+The message can be split over several lines using newlines. The maximum message
+length is 255 characters.
+
+
+show_input
+**********
+
+The ``show_input`` parameter is used to toggle on and off the 'Show input
+message when cell is selected' option in the Excel data validation dialog.
+When the option is off an input message is not displayed even if it has been
+set using ``input_message``. It is on by default.
+
+
+error_title
+***********
+
+The ``error_title`` parameter is used to set the title of the error message
+that is displayed when the data validation criteria is not met. The default
+error title is 'Microsoft Excel'. The maximum title length is 32 characters.
+
+
+error_message
+*************
+
+The ``error_message`` parameter is used to set the error message that is
+displayed when a cell is entered. The default error message is "The value you
+entered is not valid. A user has restricted values that can be entered into
+the cell.". A non-default error message can be displayed as follows::
+
+ worksheet.data_validation('B27', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 100,
+ 'input_title': 'Enter an integer:',
+ 'input_message': 'between 1 and 100',
+ 'error_title': 'Input value not valid!',
+ 'error_message': 'Sorry.'})
+
+The message can be split over several lines using newlines. The maximum message
+length is 255 characters.
+
+
+error_type
+**********
+
+The ``error_type`` parameter is used to specify the type of error dialog that
+is displayed. There are 3 options::
+
+ 'stop'
+ 'warning'
+ 'information'
+
+The default is ``'stop'``.
+
+
+show_error
+**********
+
+The ``show_error`` parameter is used to toggle on and off the 'Show error alert
+after invalid data is entered' option in the Excel data validation dialog.
+When the option is off an error message is not displayed even if it has been
+set using ``error_message``. It is on by default.
+
+
+
+Data Validation Examples
+------------------------
+
+Example 1. Limiting input to an integer greater than a fixed value::
+
+ worksheet.data_validation('A1', {'validate': 'integer',
+ 'criteria': '>',
+ 'value': 0,
+ })
+
+Example 2. Limiting input to an integer greater than a fixed value where the
+value is referenced from a cell::
+
+ worksheet.data_validation('A2', {'validate': 'integer',
+ 'criteria': '>',
+ 'value': '=E3',
+ })
+
+Example 3. Limiting input to a decimal in a fixed range::
+
+ worksheet.data_validation('A3', {'validate': 'decimal',
+ 'criteria': 'between',
+ 'minimum': 0.1,
+ 'maximum': 0.5,
+ })
+
+Example 4. Limiting input to a value in a dropdown list::
+
+ worksheet.data_validation('A4', {'validate': 'list',
+ 'source': ['open', 'high', 'close'],
+ })
+
+Example 5. Limiting input to a value in a dropdown list where the list is
+specified as a cell range::
+
+ worksheet.data_validation('A5', {'validate': 'list',
+ 'source': '=$E$4:$G$4',
+ })
+
+Example 6. Limiting input to a date in a fixed range::
+
+ from datetime import date
+
+ worksheet.data_validation('A6', {'validate': 'date',
+ 'criteria': 'between',
+ 'minimum': date(2013, 1, 1),
+ 'maximum': date(2013, 12, 12),
+ })
+
+Example 7. Displaying a message when the cell is selected::
+
+ worksheet.data_validation('A7', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 100,
+ 'input_title': 'Enter an integer:',
+ 'input_message': 'between 1 and 100',
+ })
+
+See also :ref:`ex_data_valid`.
diff --git a/dev/docs/source/working_with_dates_and_time.rst b/dev/docs/source/working_with_dates_and_time.rst
new file mode 100644
index 0000000..6db2ead
--- /dev/null
+++ b/dev/docs/source/working_with_dates_and_time.rst
@@ -0,0 +1,187 @@
+.. _working_with_dates_and_time:
+
+Working with Dates and Time
+===========================
+
+Dates and times in Excel are represented by real numbers, for example "Jan 1
+2013 12:00 PM" is represented by the number 41275.5.
+
+The integer part of the number stores the number of days since the epoch and
+the fractional part stores the percentage of the day.
+
+A date or time in Excel is just like any other number. To display the number as
+a date you must apply an Excel number format to it. Here are some examples:
+
+.. code-block:: python
+
+ import xlsxwriter
+
+ workbook = xlsxwriter.Workbook('date_examples.xlsx')
+ worksheet = workbook.add_worksheet()
+
+ # Widen column A for extra visibility.
+ worksheet.set_column('A:A', 30)
+
+ # A number to convert to a date.
+ number = 41333.5
+
+ # Write it as a number without formatting.
+ worksheet.write('A1', number) # 41333.5
+
+ format2 = workbook.add_format({'num_format': 'dd/mm/yy'})
+ worksheet.write('A2', number, format2) # 28/02/13
+
+ format3 = workbook.add_format({'num_format': 'mm/dd/yy'})
+ worksheet.write('A3', number, format3) # 02/28/13
+
+ format4 = workbook.add_format({'num_format': 'd-m-yyyy'})
+ worksheet.write('A4', number, format4) # 28-2-2013
+
+ format5 = workbook.add_format({'num_format': 'dd/mm/yy hh:mm'})
+ worksheet.write('A5', number, format5) # 28/02/13 12:00
+
+ format6 = workbook.add_format({'num_format': 'd mmm yyyy'})
+ worksheet.write('A6', number, format6) # 28 Feb 2013
+
+ format7 = workbook.add_format({'num_format': 'mmm d yyyy hh:mm AM/PM'})
+ worksheet.write('A7', number, format7) # Feb 28 2013 12:00 PM
+
+ workbook.close()
+
+.. image:: _images/working_with_dates_and_times01.png
+
+To make working with dates and times a little easier the XlsxWriter module
+provides a :func:`write_datetime` method to write dates in standard library
+:mod:`datetime` format.
+
+Specifically it supports datetime objects of type :class:`datetime.datetime`,
+:class:`datetime.date`, :class:`datetime.time` and :class:`datetime.timedelta`.
+
+There are many way to create datetime objects, for example the
+:meth:`datetime.datetime.strptime` method::
+
+ date_time = datetime.datetime.strptime('2013-01-23', '%Y-%m-%d')
+
+See the :mod:`datetime` documentation for other date/time creation methods.
+
+As explained above you also need to create and apply a number format to format
+the date/time::
+
+ date_format = workbook.add_format({'num_format': 'd mmmm yyyy'})
+ worksheet.write_datetime('A1', date_time, date_format)
+
+ # Displays "23 January 2013"
+
+Here is a longer example that displays the same date in a several different
+formats:
+
+.. code-block:: python
+
+ from datetime import datetime
+ import xlsxwriter
+
+ # Create a workbook and add a worksheet.
+ workbook = xlsxwriter.Workbook('datetimes.xlsx')
+ worksheet = workbook.add_worksheet()
+ bold = workbook.add_format({'bold': True})
+
+ # Expand the first columns so that the dates are visible.
+ worksheet.set_column('A:B', 30)
+
+ # Write the column headers.
+ worksheet.write('A1', 'Formatted date', bold)
+ worksheet.write('B1', 'Format', bold)
+
+ # Create a datetime object to use in the examples.
+
+ date_time = datetime.strptime('2013-01-23 12:30:05.123',
+ '%Y-%m-%d %H:%M:%S.%f')
+
+ # Examples date and time formats.
+ date_formats = (
+ 'dd/mm/yy',
+ 'mm/dd/yy',
+ 'dd m yy',
+ 'd mm yy',
+ 'd mmm yy',
+ 'd mmmm yy',
+ 'd mmmm yyy',
+ 'd mmmm yyyy',
+ 'dd/mm/yy hh:mm',
+ 'dd/mm/yy hh:mm:ss',
+ 'dd/mm/yy hh:mm:ss.000',
+ 'hh:mm',
+ 'hh:mm:ss',
+ 'hh:mm:ss.000',
+ )
+
+ # Start from first row after headers.
+ row = 1
+
+ # Write the same date and time using each of the above formats.
+ for date_format_str in date_formats:
+
+ # Create a format for the date or time.
+ date_format = workbook.add_format({'num_format': date_format_str,
+ 'align': 'left'})
+
+ # Write the same date using different formats.
+ worksheet.write_datetime(row, 0, date_time, date_format)
+
+ # Also write the format string for comparison.
+ worksheet.write_string(row, 1, date_format_str)
+
+ row += 1
+
+ workbook.close()
+
+.. image:: _images/working_with_dates_and_times02.png
+
+
+Default Date Formatting
+-----------------------
+
+In certain circumstances you may wish to apply a default date format when
+writing datetime objects, for example, when handling a row of data with
+:func:`write_row`.
+
+In these cases it is possible to specify a default date format string using the
+:func:`Workbook` constructor ``default_date_format`` option::
+
+ workbook = xlsxwriter.Workbook('datetimes.xlsx', {'default_date_format':
+ 'dd/mm/yy'})
+ worksheet = workbook.add_worksheet()
+ date_time = datetime.now()
+ worksheet.write_datetime(0, 0, date_time) # Formatted as 'dd/mm/yy'
+
+ workbook.close()
+
+
+.. _timezone_handling:
+
+Timezone Handling
+-----------------
+
+Excel doesn't support timezones in datetimes/times so there isn't any fail-safe
+way that XlsxWriter can map a Python timezone aware datetime into an Excel
+datetime. As such the user should handle the timezones in some way that makes
+sense according to their requirements. Usually this will require some
+conversion to a timezone adjusted time and the removal of the ``tzinfo`` from
+the datetime object so that it can be passed to :func:`write_datetime`::
+
+ utc_datetime = datetime(2016, 9, 23, 14, 13, 21, tzinfo=utc)
+ naive_datetime = utc_datetime.replace(tzinfo=None)
+
+ worksheet.write_datetime(row, 0, naive_datetime, date_format)
+
+Alternatively the :func:`Workbook` constructor option ``remove_timezone`` can
+be used to strip the timezone from datetime values passed to
+:func:`write_datetime`. The default is ``False``. To enable this option use::
+
+ workbook = xlsxwriter.Workbook(filename, {'remove_timezone': True})
+
+When :ref:`ewx_pandas` you can pass the argument as follows::
+
+ writer = pd.ExcelWriter('pandas_example.xlsx',
+ engine='xlsxwriter',
+ options={'remove_timezone': True})
diff --git a/dev/docs/source/working_with_formulas.rst b/dev/docs/source/working_with_formulas.rst
new file mode 100644
index 0000000..530cb70
--- /dev/null
+++ b/dev/docs/source/working_with_formulas.rst
@@ -0,0 +1,291 @@
+.. _working_with_formulas:
+
+Working with Formulas
+=====================
+
+In general a formula in Excel can be used directly in the
+:func:`write_formula` method::
+
+ worksheet.write_formula('A1', '=10*B1 + C1')
+
+.. image:: _images/working_with_formulas2.png
+
+However, there are a few potential issues and differences that the user should
+be aware of. These are explained in the following sections.
+
+
+.. _formula_syntax:
+
+Non US Excel functions and syntax
+---------------------------------
+
+Excel stores formulas in the format of the US English version, regardless
+of the language or locale of the end-user's version of Excel. Therefore all
+formula function names written using XlsxWriter must be in English::
+
+ worksheet.write_formula('A1', '=SUM(1, 2, 3)') # OK
+ worksheet.write_formula('A2', '=SOMME(1, 2, 3)') # French. Error on load.
+
+Also, formulas must be written with the US style separator/range operator
+which is a comma (not semi-colon). Therefore a formula with multiple values
+should be written as follows::
+
+ worksheet.write_formula('A1', '=SUM(1, 2, 3)') # OK
+ worksheet.write_formula('A2', '=SUM(1; 2; 3)') # Semi-colon. Error on load.
+
+If you have a non-English version of Excel you can use the following
+multi-lingual `formula translator <http://en.excel-translator.de/language/>`_
+to help you convert the formula. It can also replace semi-colons with commas.
+
+
+.. _formula_future:
+
+Formulas added in Excel 2010 and later
+--------------------------------------
+
+Excel 2010 and later added functions which weren't defined in the original
+file specification. These functions are referred to by Microsoft as *future*
+functions. Examples of these functions are ``ACOT``, ``CHISQ.DIST.RT`` ,
+``CONFIDENCE.NORM``, ``STDEV.P``, ``STDEV.S`` and ``WORKDAY.INTL``.
+
+When written using ``write_formula()`` these functions need to be fully
+qualified with a ``_xlfn.`` (or other) prefix as they are shown the list
+below. For example::
+
+ worksheet.write_formula('A1', '=_xlfn.STDEV.S(B1:B10)')
+
+They will appear without the prefix in Excel:
+
+.. image:: _images/working_with_formulas2.png
+
+The following list is taken from
+`MS XLSX extensions documentation on future functions <http://msdn.microsoft.com/en-us/library/dd907480%28v=office.12%29.aspx>`_.
+
+* ``_xlfn.ACOT``
+* ``_xlfn.ACOTH``
+* ``_xlfn.AGGREGATE``
+* ``_xlfn.ARABIC``
+* ``_xlfn.BASE``
+* ``_xlfn.BETA.DIST``
+* ``_xlfn.BETA.INV``
+* ``_xlfn.BINOM.DIST``
+* ``_xlfn.BINOM.DIST.RANGE``
+* ``_xlfn.BINOM.INV``
+* ``_xlfn.BITAND``
+* ``_xlfn.BITLSHIFT``
+* ``_xlfn.BITOR``
+* ``_xlfn.BITRSHIFT``
+* ``_xlfn.BITXOR``
+* ``_xlfn.CEILING.MATH``
+* ``_xlfn.CEILING.PRECISE``
+* ``_xlfn.CHISQ.DIST``
+* ``_xlfn.CHISQ.DIST.RT``
+* ``_xlfn.CHISQ.INV``
+* ``_xlfn.CHISQ.INV.RT``
+* ``_xlfn.CHISQ.TEST``
+* ``_xlfn.COMBINA``
+* ``_xlfn.CONFIDENCE.NORM``
+* ``_xlfn.CONFIDENCE.T``
+* ``_xlfn.COT``
+* ``_xlfn.COTH``
+* ``_xlfn.COVARIANCE.P``
+* ``_xlfn.COVARIANCE.S``
+* ``_xlfn.CSC``
+* ``_xlfn.CSCH``
+* ``_xlfn.DAYS``
+* ``_xlfn.DECIMAL``
+* ``ECMA.CEILING``
+* ``_xlfn.ERF.PRECISE``
+* ``_xlfn.ERFC.PRECISE``
+* ``_xlfn.EXPON.DIST``
+* ``_xlfn.F.DIST``
+* ``_xlfn.F.DIST.RT``
+* ``_xlfn.F.INV``
+* ``_xlfn.F.INV.RT``
+* ``_xlfn.F.TEST``
+* ``_xlfn.FILTERXML``
+* ``_xlfn.FLOOR.MATH``
+* ``_xlfn.FLOOR.PRECISE``
+* ``_xlfn.FORECAST.ETS``
+* ``_xlfn.FORECAST.ETS.CONFINT``
+* ``_xlfn.FORECAST.ETS.SEASONALITY``
+* ``_xlfn.FORECAST.ETS.STAT``
+* ``_xlfn.FORECAST.LINEAR``
+* ``_xlfn.FORMULATEXT``
+* ``_xlfn.GAMMA``
+* ``_xlfn.GAMMA.DIST``
+* ``_xlfn.GAMMA.INV``
+* ``_xlfn.GAMMALN.PRECISE``
+* ``_xlfn.GAUSS``
+* ``_xlfn.HYPGEOM.DIST``
+* ``_xlfn.IFNA``
+* ``_xlfn.IMCOSH``
+* ``_xlfn.IMCOT``
+* ``_xlfn.IMCSC``
+* ``_xlfn.IMCSCH``
+* ``_xlfn.IMSEC``
+* ``_xlfn.IMSECH``
+* ``_xlfn.IMSINH``
+* ``_xlfn.IMTAN``
+* ``_xlfn.ISFORMULA``
+* ``ISO.CEILING``
+* ``_xlfn.ISOWEEKNUM``
+* ``_xlfn.LOGNORM.DIST``
+* ``_xlfn.LOGNORM.INV``
+* ``_xlfn.MODE.MULT``
+* ``_xlfn.MODE.SNGL``
+* ``_xlfn.MUNIT``
+* ``_xlfn.NEGBINOM.DIST``
+* ``NETWORKDAYS.INTL``
+* ``_xlfn.NORM.DIST``
+* ``_xlfn.NORM.INV``
+* ``_xlfn.NORM.S.DIST``
+* ``_xlfn.NORM.S.INV``
+* ``_xlfn.NUMBERVALUE``
+* ``_xlfn.PDURATION``
+* ``_xlfn.PERCENTILE.EXC``
+* ``_xlfn.PERCENTILE.INC``
+* ``_xlfn.PERCENTRANK.EXC``
+* ``_xlfn.PERCENTRANK.INC``
+* ``_xlfn.PERMUTATIONA``
+* ``_xlfn.PHI``
+* ``_xlfn.POISSON.DIST``
+* ``_xlfn.QUARTILE.EXC``
+* ``_xlfn.QUARTILE.INC``
+* ``_xlfn.QUERYSTRING``
+* ``_xlfn.RANK.AVG``
+* ``_xlfn.RANK.EQ``
+* ``_xlfn.RRI``
+* ``_xlfn.SEC``
+* ``_xlfn.SECH``
+* ``_xlfn.SHEET``
+* ``_xlfn.SHEETS``
+* ``_xlfn.SKEW.P``
+* ``_xlfn.STDEV.P``
+* ``_xlfn.STDEV.S``
+* ``_xlfn.T.DIST``
+* ``_xlfn.T.DIST.2T``
+* ``_xlfn.T.DIST.RT``
+* ``_xlfn.T.INV``
+* ``_xlfn.T.INV.2T``
+* ``_xlfn.T.TEST``
+* ``_xlfn.UNICHAR``
+* ``_xlfn.UNICODE``
+* ``_xlfn.VAR.P``
+* ``_xlfn.VAR.S``
+* ``_xlfn.WEBSERVICE``
+* ``_xlfn.WEIBULL.DIST``
+* ``WORKDAY.INTL``
+* ``_xlfn.XOR``
+* ``_xlfn.Z.TEST``
+
+.. _formula_tables:
+
+Using Tables in Formulas
+------------------------
+
+Worksheet tables can be added with XlsxWriter using the :func:`add_table()`
+method::
+
+ worksheet.add_table('B3:F7', {options})
+
+By default tables are named ``Table1``, ``Table2``, etc., in the order that
+they are added. However it can also be set by the user using the ``name`` parameter::
+
+ worksheet.add_table('B3:F7', {'name': 'SalesData'})
+
+If you need to know the name of the table, for example to use it in a formula,
+you can get it as follows::
+
+ table = worksheet.add_table('B3:F7')
+ table_name = table.name
+
+When used in a formula a table name such as ``TableX`` should be referred to
+as ``TableX[]`` (like a Python list)::
+
+ worksheet.write_formula('A5', '=VLOOKUP("Sales", Table1[], 2, FALSE')
+
+.. _formula_errors:
+
+Dealing with formula errors
+---------------------------
+
+If there is an error in the syntax of a formula it is usually displayed in
+Excel as ``#NAME?``. Alternatively you may get a warning from Excel when the
+file is loaded. If you encounter an error like this you can debug it as
+follows:
+
+#. Ensure the formula is valid in Excel by copying and pasting it into a
+ cell. Note, this should be done in Excel and not other applications such as
+ OpenOffice or LibreOffice since they may have slightly different syntax.
+
+#. Ensure the formula is using comma separators instead of semi-colons, see
+ :ref:`formula_syntax` above.
+
+#. Ensure the formula is in English, see :ref:`formula_syntax` above.
+
+#. Ensure that the formula doesn't contain an Excel 2010+ future function as
+ listed above (:ref:`formula_future`). If it does then ensure that the
+ correct prefix is used.
+
+Finally if you have completed all the previous steps and still get a
+``#NAME?`` error you can examine a valid Excel file to see what the correct
+syntax should be. To do this you should create a valid formula in Excel and
+save the file. You can then examine the XML in the unzipped file.
+
+The following shows how to do that using Linux ``unzip`` and `libxml's xmllint
+<http://xmlsoft.org/xmllint.html>`_ to format the XML for clarity::
+
+ $ unzip myfile.xlsx -d myfile
+ $ xmllint --format myfile/xl/worksheets/sheet1.xml | grep '<f>'
+
+ <f>SUM(1, 2, 3)</f>
+
+
+.. _formula_result:
+
+Formula Results
+---------------
+
+XlsxWriter doesn't calculate the result of a formula and instead stores the
+value 0 as the formula result. It then sets a global flag in the XLSX file to
+say that all formulas and functions should be recalculated when the file is
+opened.
+
+This is the method recommended in the Excel documentation and in general it
+works fine with spreadsheet applications. However, applications that don't
+have a facility to calculate formulas will only display the 0
+results. Examples of such applications are Excel Viewer, PDF Converters, and
+some mobile device applications.
+
+If required, it is also possible to specify the calculated result of the
+formula using the optional ``value`` parameter for :func:`write_formula`::
+
+ worksheet.write_formula('A1', '=2+2', num_format, 4)
+
+The ``value`` parameter can be a number, a string, a bool or one of the
+following Excel error codes::
+
+ #DIV/0!
+ #N/A
+ #NAME?
+ #NULL!
+ #NUM!
+ #REF!
+ #VALUE!
+
+It is also possible to specify the calculated result of an array formula
+created with :func:`write_array_formula`::
+
+ # Specify the result for a single cell range.
+ worksheet.write_array_formula('A1:A1', '{=SUM(B1:C1*B2:C2)}', format, 2005)
+
+However, using this parameter only writes a single value to the upper left
+cell in the result array. For a multi-cell array formula where the results are
+required, the other result values can be specified by using ``write_number()``
+to write to the appropriate cell::
+
+ # Specify the results for a multi cell range.
+ worksheet.write_array_formula('A1:A3', '{=TREND(C1:C3,B1:B3)}', format, 15)
+ worksheet.write_number('A2', 12, format)
+ worksheet.write_number('A3', 14, format)
diff --git a/dev/docs/source/working_with_macros.rst b/dev/docs/source/working_with_macros.rst
new file mode 100644
index 0000000..23d9c37
--- /dev/null
+++ b/dev/docs/source/working_with_macros.rst
@@ -0,0 +1,167 @@
+.. _macros:
+
+Working with VBA Macros
+=======================
+
+This section explains how to add a VBA file containing functions or macros to an XlsxWriter file.
+
+.. image:: _images/macros.png
+
+**Note: This feature should be considered as experimental.**
+
+
+The Excel XLSM file format
+--------------------------
+
+An Excel ``xlsm`` file is exactly the same as a ``xlsx`` file except that is
+contains an additional ``vbaProject.bin`` file which contains functions and/or
+macros. Excel uses a different extension to differentiate between the two file
+formats since files containing macros are usually subject to additional
+security checks.
+
+
+How VBA macros are included in XlsxWriter
+-----------------------------------------
+
+The ``vbaProject.bin`` file is a binary OLE COM container. This was the format
+used in older ``xls`` versions of Excel prior to Excel 2007. Unlike all of the
+other components of an xlsx/xlsm file the data isn't stored in XML
+format. Instead the functions and macros as stored as pre-parsed binary
+format. As such it wouldn't be feasible to define macros and create a
+``vbaProject.bin`` file from scratch (at least not in the remaining lifespan
+and interest levels of the author).
+
+Instead a workaround is used to extract ``vbaProject.bin`` files from existing
+xlsm files and then add these to XlsxWriter files.
+
+
+The vba_extract utility
+-----------------------
+
+The ``vba_extract`` utility is used to extract the ``vbaProject.bin`` binary
+from an Excel 2007+ xlsm file. The utility is included in the XlsxWriter
+examples directory and is also installed as a standalone executable file::
+
+ $ vba_extract.py macro_file.xlsm
+ Extracted: vbaProject.bin
+
+
+Adding the VBA macros to a XlsxWriter file
+------------------------------------------
+
+Once the ``vbaProject.bin`` file has been extracted it can be added to the
+XlsxWriter workbook using the :func:`add_vba_project` method::
+
+ workbook.add_vba_project('./vbaProject.bin')
+
+If the VBA file contains functions you can then refer to them in calculations
+using :func:`write_formula`::
+
+ worksheet.write_formula('A1', '=MyMortgageCalc(200000, 25)')
+
+Excel files that contain functions and macros should use an ``xlsm`` extension
+or else Excel will complain and possibly not open the file::
+
+ workbook = xlsxwriter.Workbook('macros.xlsm')
+
+It is also possible to assign a macro to a button that is inserted into a
+worksheet using the :func:`insert_button` method::
+
+ import xlsxwriter
+
+ # Note the file extension should be .xlsm.
+ workbook = xlsxwriter.Workbook('macros.xlsm')
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('A:A', 30)
+
+ # Add the VBA project binary.
+ workbook.add_vba_project('./vbaProject.bin')
+
+ # Show text for the end user.
+ worksheet.write('A3', 'Press the button to say hello.')
+
+ # Add a button tied to a macro in the VBA project.
+ worksheet.insert_button('B3', {'macro': 'say_hello',
+ 'caption': 'Press Me',
+ 'width': 80,
+ 'height': 30})
+
+ workbook.close()
+
+It may be necessary to specify a more explicit macro name prefixed by the
+workbook VBA name as follows::
+
+ worksheet.insert_button('B3', {'macro': 'ThisWorkbook.say_hello'})
+
+See :ref:`ex_macros` from the examples directory for a working example.
+
+.. Note::
+ Button is the only VBA Control supported by Xlsxwriter. Due to the large
+ effort in implementation (1+ man months) it is unlikely that any other form
+ elements will be added in the future.
+
+
+Setting the VBA codenames
+-------------------------
+
+VBA macros generally refer to workbook and worksheet objects. If the VBA
+codenames aren't specified then XlsxWriter will use the Excel defaults of
+``ThisWorkbook`` and ``Sheet1``, ``Sheet2`` etc.
+
+If the macro uses other codenames you can set them using the workbook and
+worksheet ``set_vba_name()`` methods as follows::
+
+ # Note: set codename for workbook and any worksheets.
+ workbook.set_vba_name('MyWorkbook')
+ worksheet1.set_vba_name('MySheet1')
+ worksheet2.set_vba_name('MySheet2')
+
+You can find the names that are used in the VBA editor or by unzipping the
+``xlsm`` file and grepping the files. The following shows how to do that using
+`libxml's xmllint <http://xmlsoft.org/xmllint.html>`_ to format the XML for
+clarity::
+
+
+ $ unzip myfile.xlsm -d myfile
+ $ xmllint --format `find myfile -name "*.xml" | xargs` | grep "Pr.*codeName"
+
+ <workbookPr codeName="MyWorkbook" defaultThemeVersion="124226"/>
+ <sheetPr codeName="MySheet"/>
+
+
+.. Note::
+
+ This step is particularly important for macros created with non-English
+ versions of Excel.
+
+
+
+What to do if it doesn't work
+-----------------------------
+
+As stated at the start of this section this feature is experimental. The
+Xlsxwriter test suite contains several tests and there is a working example as
+shown above. However, there is no guarantee that it will work in all
+cases. Some effort may be required and some knowledge of VBA will certainly
+help. If things don't work out here are some things to try:
+
+#. Start with a simple macro file, ensure that it works and then add complexity.
+
+#. Try to extract the macros from an Excel 2007 file. The method should work
+ with macros from later versions (it was also tested with Excel 2010
+ macros). However there may be features in the macro files of more recent
+ version of Excel that aren't backward compatible.
+
+#. Check the code names that macros use to refer to the workbook and
+ worksheets (see the previous section above). In general VBA uses a code
+ name of ``ThisWorkbook`` to refer to the current workbook and the sheet
+ name (such as ``Sheet1``) to refer to the worksheets. These are the
+ defaults used by XlsxWriter. If the macro uses other names then you can
+ specify these using the workbook and worksheet :func:`set_vba_name`
+ methods::
+
+ # Note: set codename for workbook and any worksheets.
+ workbook.set_vba_name('MyWorkbook')
+ worksheet1.set_vba_name('MySheet1')
+ worksheet2.set_vba_name('MySheet2')
diff --git a/dev/docs/source/working_with_memory.rst b/dev/docs/source/working_with_memory.rst
new file mode 100644
index 0000000..8936705
--- /dev/null
+++ b/dev/docs/source/working_with_memory.rst
@@ -0,0 +1,140 @@
+.. _memory_perf:
+
+Working with Memory and Performance
+===================================
+
+By default XlsxWriter holds all cell data in memory. This is to allow future
+features when formatting is applied separately from the data.
+
+The effect of this is that XlsxWriter can consume a lot of memory and it is
+possible to run out of memory when creating large files.
+
+Fortunately, this memory usage can be reduced almost completely by using the
+:func:`Workbook` ``'constant_memory'`` property::
+
+ workbook = xlsxwriter.Workbook(filename, {'constant_memory': True})
+
+The optimization works by flushing each row after a subsequent row is written.
+In this way the largest amount of data held in memory for a worksheet is the
+amount of data required to hold a single row of data.
+
+Since each new row flushes the previous row, data must be written in sequential
+row order when ``'constant_memory'`` mode is on::
+
+ # With 'constant_memory' you must write data in row by column order.
+ for row in range(0, row_max):
+ for col in range(0, col_max):
+ worksheet.write(row, col, some_data)
+
+ # With 'constant_memory' this would only write the first column of data.
+ for col in range(0, col_max):
+ for row in range(0, row_max):
+ worksheet.write(row, col, some_data)
+
+Another optimization that is used to reduce memory usage is that cell strings
+aren't stored in an Excel structure call "shared strings" and instead are
+written "in-line". This is a documented Excel feature that is supported by
+most spreadsheet applications. One known exception is Apple Numbers for Mac
+where the string data isn't displayed.
+
+The trade-off when using ``'constant_memory'`` mode is that you won't be able
+to take advantage of any new features that manipulate cell data after it is
+written. Currently the :func:`add_table()` method doesn't work in this mode
+and :func:`merge_range()` and :func:`set_row()` only work for the current row.
+
+
+For larger files ``'constant_memory'`` mode also gives an increase in execution
+speed, see below.
+
+
+Performance Figures
+-------------------
+
+The performance figures below show execution time and memory usage for
+worksheets of size ``N`` rows x 50 columns with a 50/50 mixture of strings and
+numbers. The figures are taken from an arbitrary, mid-range, machine. Specific
+figures will vary from machine to machine but the trends should be the same.
+
+XlsxWriter in normal operation mode: the execution time and memory usage
+increase more of less linearly with the number of rows:
+
++-------+---------+----------+----------------+
+| Rows | Columns | Time (s) | Memory (bytes) |
++=======+=========+==========+================+
+| 200 | 50 | 0.43 | 2346728 |
++-------+---------+----------+----------------+
+| 400 | 50 | 0.84 | 4670904 |
++-------+---------+----------+----------------+
+| 800 | 50 | 1.68 | 8325928 |
++-------+---------+----------+----------------+
+| 1600 | 50 | 3.39 | 17855192 |
++-------+---------+----------+----------------+
+| 3200 | 50 | 6.82 | 32279672 |
++-------+---------+----------+----------------+
+| 6400 | 50 | 13.66 | 64862232 |
++-------+---------+----------+----------------+
+| 12800 | 50 | 27.60 | 128851880 |
++-------+---------+----------+----------------+
+
+XlsxWriter in ``constant_memory`` mode: the execution time still increases
+linearly with the number of rows but the memory usage remains small and
+constant:
+
++-------+---------+----------+----------------+
+| Rows | Columns | Time (s) | Memory (bytes) |
++=======+=========+==========+================+
+| 200 | 50 | 0.37 | 62208 |
++-------+---------+----------+----------------+
+| 400 | 50 | 0.74 | 62208 |
++-------+---------+----------+----------------+
+| 800 | 50 | 1.46 | 62208 |
++-------+---------+----------+----------------+
+| 1600 | 50 | 2.93 | 62208 |
++-------+---------+----------+----------------+
+| 3200 | 50 | 5.90 | 62208 |
++-------+---------+----------+----------------+
+| 6400 | 50 | 11.84 | 62208 |
++-------+---------+----------+----------------+
+| 12800 | 50 | 23.63 | 62208 |
++-------+---------+----------+----------------+
+
+In the ``constant_memory`` mode the performance is also increased slightly.
+
+These figures were generated using programs in the ``dev/performance``
+directory of the XlsxWriter repo.
+
+
+Benchmark of Python Excel Writers
+---------------------------------
+
+If you wish to compare the performance of different Python Excel writing
+modules there is a program called `bench_excel_writers.py
+<https://raw.githubusercontent.com/jmcnamara/XlsxWriter/master/dev/performance/bench_excel_writers.py>`_
+in the ``dev/performance`` directory of the XlsxWriter repo.
+
+And here is the output for 10,000 rows x 50 columns using the latest version
+of the modules at the time of writing::
+
+ Versions:
+ python : 2.7.2
+ openpyxl : 2.2.1
+ pyexcelerate: 0.6.6
+ xlsxwriter : 0.7.2
+ xlwt : 1.0.0
+
+ Dimensions:
+ Rows = 10000
+ Cols = 50
+
+ Times:
+ pyexcelerate : 10.63
+ xlwt : 16.93
+ xlsxwriter (optimized): 20.37
+ xlsxwriter : 24.24
+ openpyxl (optimized): 26.63
+ openpyxl : 35.75
+
+
+As with any benchmark the results will depend on Python/module versions, CPU,
+RAM and Disk I/O and on the benchmark itself. So make sure to verify these
+results for your own setup.
diff --git a/dev/docs/source/working_with_outlines.rst b/dev/docs/source/working_with_outlines.rst
new file mode 100644
index 0000000..5c18de5
--- /dev/null
+++ b/dev/docs/source/working_with_outlines.rst
@@ -0,0 +1,94 @@
+.. _outlines:
+
+Working with Outlines and Grouping
+==================================
+
+Excel allows you to group rows or columns so that they can be hidden or
+displayed with a single mouse click. This feature is referred to as outlines
+and grouping.
+
+Outlines can reduce complex data down to a few salient sub-totals or summaries.
+
+For example the following is a worksheet with three outlines. Rows 2-11 are
+grouped at level 1 and rows 2-5 and 7-10 are grouped at level 2. The lines at
+the left hand side are called outline level bars and the level is shown by the
+small numeral above the outline.
+
+.. image:: _images/outline1.png
+
+Clicking the minus sign on each of the level 2 outlines will collapse and hide
+the data as shown below. The minus sign changes to a plus sign to indicate
+that the data in the outline is hidden.
+
+.. image:: _images/outline5.png
+
+This shows the usefulness of outlines: with 2 mouse clicks we have reduced the
+amount of visual data down to 2 sub-totals and a master total.
+
+Clicking on the minus sign on the level 1 outline will collapse the remaining
+rows as follows:
+
+.. image:: _images/outline6.png
+
+
+Outlines and Grouping in XlsxWriter
+-----------------------------------
+
+Grouping in ``XlsxWriter`` is achieved by setting the outline level via the
+:func:`set_row()` and :func:`set_column()` worksheet methods::
+
+ worksheet.set_row(row, height, format, options)
+ worksheet.set_column(first_col, last_col, width, format, options)
+
+Adjacent row or columns with the same outline level are grouped together into a
+single outline.
+
+The ``'options'`` parameter is a dictionary with the following possible keys:
+
+* ``'hidden'``
+* ``'level'``
+* ``'collapsed'``
+
+Options can be set as follows::
+
+ worksheet.set_row(0, 20, cell_format, {'hidden': True})
+
+ # Or use defaults for other properties and set the options only.
+ worksheet.set_row(0, None, None, {'hidden': True})
+
+The following example sets an outline level of 1 for rows 1 and 2
+(zero-indexed) and columns B to G. The parameters ``height`` and
+``cell_format`` are assigned default values::
+
+ worksheet.set_row(1, None, None, {'level': 1})
+ worksheet.set_row(2, None, None, {'level': 1})
+ worksheet.set_column('B:G', None, None, {'level': 1})
+
+.. image:: _images/outline3.png
+
+Excel allows up to 7 outline levels. Therefore the ``level`` parameter should
+be in the range ``0 <= level <= 7``.
+
+.. image:: _images/outline4.png
+
+Rows and columns can be collapsed by setting the ``hidden`` flag for the hidden
+rows/columns and setting the ``collapsed`` flag for the row/column that has
+the collapsed ``'+'`` symbol::
+
+ worksheet.set_row(1, None, None, {'level': 1, 'hidden': True})
+ worksheet.set_row(2, None, None, {'level': 1, 'hidden': True})
+ worksheet.set_row(3, None, None, {'collapsed': True})
+
+ worksheet.set_column('B:G', None, None, {'level': 1, 'hidden': True})
+ worksheet.set_column('H:H', None, None, {'collapsed': True})
+
+.. Note::
+
+ Setting the ``collapsed`` flag is particularly important for
+ compatibility with non-Excel spreadsheets.
+
+For a more complete examples see :ref:`ex_outline1` and :ref:`ex_outline2`.
+
+Some additional outline properties can be set via the :func:`outline_settings`
+worksheet method.
+
diff --git a/dev/docs/source/working_with_pandas.rst b/dev/docs/source/working_with_pandas.rst
new file mode 100644
index 0000000..15a2445
--- /dev/null
+++ b/dev/docs/source/working_with_pandas.rst
@@ -0,0 +1,258 @@
+.. _ewx_pandas:
+
+Working with Python Pandas and XlsxWriter
+=========================================
+
+Python `Pandas <http://pandas.pydata.org/>`_ is a Python data analysis
+library. It can read, filter and re-arrange small and large data sets and
+output them in a range of formats including Excel.
+
+Pandas writes Excel files using the `Xlwt
+<https://pypi.python.org/pypi/xlwt>`_ module for xls files and the `Openpyxl
+<https://pypi.python.org/pypi/openpyxl>`_ or XlsxWriter modules for xlsx
+files.
+
+
+Using XlsxWriter with Pandas
+----------------------------
+
+To use XlsxWriter with Pandas you specify it as the Excel writer *engine*::
+
+ import pandas as pd
+
+ # Create a Pandas dataframe from the data.
+ df = pd.DataFrame({'Data': [10, 20, 30, 20, 15, 30, 45]})
+
+ # Create a Pandas Excel writer using XlsxWriter as the engine.
+ writer = pd.ExcelWriter('pandas_simple.xlsx', engine='xlsxwriter')
+
+ # Convert the dataframe to an XlsxWriter Excel object.
+ df.to_excel(writer, sheet_name='Sheet1')
+
+ # Close the Pandas Excel writer and output the Excel file.
+ writer.save()
+
+The output from this would look like the following:
+
+.. image:: _images/pandas_simple.png
+
+See the full example at :ref:`ex_pandas_simple`.
+
+
+Accessing XlsxWriter from Pandas
+--------------------------------
+
+In order to apply XlsxWriter features such as Charts, Conditional Formatting
+and Column Formatting to the Pandas output we need to access the underlying
+:ref:`workbook <Workbook>` and :ref:`worksheet <Worksheet>` objects. After
+that we can treat them as normal XlsxWriter objects.
+
+Continuing on from the above example we do that as follows::
+
+ import pandas as pd
+
+ # Create a Pandas dataframe from the data.
+ df = pd.DataFrame({'Data': [10, 20, 30, 20, 15, 30, 45]})
+
+ # Create a Pandas Excel writer using XlsxWriter as the engine.
+ writer = pd.ExcelWriter('pandas_simple.xlsx', engine='xlsxwriter')
+
+ # Convert the dataframe to an XlsxWriter Excel object.
+ df.to_excel(writer, sheet_name='Sheet1')
+
+ # Get the xlsxwriter objects from the dataframe writer object.
+ workbook = writer.book
+ worksheet = writer.sheets['Sheet1']
+
+This is equivalent to the following code when using XlsxWriter on its own::
+
+ workbook = xlsxwriter.Workbook('filename.xlsx')
+ worksheet = workbook.add_worksheet()
+
+The Workbook and Worksheet objects can then be used to access other XlsxWriter
+features, see below.
+
+
+Adding Charts to Dataframe output
+---------------------------------
+
+Once we have the Workbook and Worksheet objects, as shown in the previous
+section, we we can use them to apply other features such as adding a chart::
+
+ # Get the xlsxwriter objects from the dataframe writer object.
+ workbook = writer.book
+ worksheet = writer.sheets['Sheet1']
+
+ # Create a chart object.
+ chart = workbook.add_chart({'type': 'column'})
+
+ # Configure the series of the chart from the dataframe data.
+ chart.add_series({'values': '=Sheet1!$B$2:$B$8'})
+
+ # Insert the chart into the worksheet.
+ worksheet.insert_chart('D2', chart)
+
+The output would look like this:
+
+.. image:: _images/pandas_chart.png
+
+See the full example at :ref:`ex_pandas_chart`.
+
+.. Note::
+
+ The above example uses a fixed string ``=Sheet1!$B$2:$B$8`` for the data
+ range. It is also possible to use a ``(row, col)`` range which can be
+ varied based on the length of the dataframe. See for example
+ :ref:`ex_pandas_chart_line` and :ref:`cell_notation`.
+
+
+Adding Conditional Formatting to Dataframe output
+-------------------------------------------------
+
+Another option is to apply a conditional format like this::
+
+ # Apply a conditional format to the cell range.
+ worksheet.conditional_format('B2:B8', {'type': '3_color_scale'})
+
+Which would give:
+
+.. image:: _images/pandas_conditional.png
+
+See the full example at :ref:`ex_pandas_conditional`.
+
+
+Formatting of the Dataframe output
+----------------------------------
+
+XlsxWriter and Pandas provide very little support for formatting the output
+data from a dataframe apart from default formatting such as the header and
+index cells and any cells that contain dates of datetimes. In addition it
+isn't possible to format any cells that already have a default format applied.
+
+If you require very controlled formatting of the dataframe output then you
+would probably be better off using Xlsxwriter directly with raw data taken
+from Pandas. However, some formatting options are available.
+
+For example it is possible to set the default date and datetime formats via
+the Pandas interface::
+
+ writer = pd.ExcelWriter("pandas_datetime.xlsx",
+ engine='xlsxwriter',
+ datetime_format='mmm d yyyy hh:mm:ss',
+ date_format='mmmm dd yyyy')
+
+Which would give:
+
+.. image:: _images/pandas_datetime.png
+
+See the full example at :ref:`ex_pandas_datetime`.
+
+It is possible to format any other, non date/datetime column data using
+:func:`set_column()`::
+
+ # Add some cell formats.
+ format1 = workbook.add_format({'num_format': '#,##0.00'})
+ format2 = workbook.add_format({'num_format': '0%'})
+
+ # Set the column width and format.
+ worksheet.set_column('B:B', 18, format1)
+
+ # Set the format but not the column width.
+ worksheet.set_column('C:C', None, format2)
+
+.. image:: _images/pandas_column_formats.png
+
+Note: This feature requires Pandas >= 0.16.
+
+See the full example at :ref:`ex_pandas_column_formats`.
+
+
+Handling multiple Pandas Dataframes
+-----------------------------------
+
+It is possible to write more than one dataframe to a worksheet or to several
+worksheets. For example to write multiple dataframes to multiple worksheets::
+
+ # Write each dataframe to a different worksheet.
+ df1.to_excel(writer, sheet_name='Sheet1')
+ df2.to_excel(writer, sheet_name='Sheet2')
+ df3.to_excel(writer, sheet_name='Sheet3')
+
+See the full example at :ref:`ex_pandas_multiple`.
+
+It is also possible to position multiple dataframes within the same
+worksheet::
+
+
+ # Position the dataframes in the worksheet.
+ df1.to_excel(writer, sheet_name='Sheet1') # Default position, cell A1.
+ df2.to_excel(writer, sheet_name='Sheet1', startcol=3)
+ df3.to_excel(writer, sheet_name='Sheet1', startrow=6)
+
+ # Write the dataframe without the header and index.
+ df4.to_excel(writer, sheet_name='Sheet1',
+ startrow=7, startcol=4, header=False, index=False)
+
+.. image:: _images/pandas_positioning.png
+
+See the full example at :ref:`ex_pandas_positioning`.
+
+
+Passing XlsxWriter constructor options to Pandas
+------------------------------------------------
+
+XlsxWriter supports several :func:`Workbook` constructor options such as
+``strings_to_urls()``. These can also be applied to the ``Workbook`` object
+created by Pandas as follows::
+
+ writer = pd.ExcelWriter('pandas_example.xlsx',
+ engine='xlsxwriter',
+ options={'strings_to_urls': False})
+
+
+Saving the Dataframe output to a string
+---------------------------------------
+
+It is also possible to write the Pandas XlsxWriter DataFrame output to a
+string or byte array::
+
+ import pandas as pd
+ import StringIO
+
+ # Create a Pandas dataframe from the data.
+ df = pd.DataFrame({'Data': [10, 20, 30, 20, 15, 30, 45]})
+
+ # Note, Python 2 example. For Python 3 use: output = io.BytesIO().
+ output = StringIO.StringIO()
+
+ # Use the StringIO object as the filehandle.
+ writer = pd.ExcelWriter(output, engine='xlsxwriter')
+
+ # Write the data frame to the StringIO object.
+ pd.DataFrame().to_excel(writer, sheet_name='Sheet1')
+
+ writer.save()
+ xlsx_data = output.getvalue()
+
+ # Do something with the data...
+
+
+Note: This feature requires Pandas >= 0.17.
+
+
+Additional Pandas and Excel Information
+---------------------------------------
+
+Here are some additional resources in relation to Pandas, Excel and XlsxWriter.
+
+* The XlsxWriter Pandas examples later in the document: :ref:`pandas_examples`.
+
+* The Pandas documentation on the `pandas.DataFrame.to_excel() method
+ <http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.to_excel.html>`_.
+
+* A more detailed tutorial on `Using Pandas and XlsxWriter to create Excel
+ charts
+ <https://pandas-xlsxwriter-charts.readthedocs.io/>`_.
+
+* The series of articles on the "Practical Business Python" website about
+ `Using Pandas and Excel <http://pbpython.com/tag/excel.html>`_.
diff --git a/dev/docs/source/working_with_sparklines.rst b/dev/docs/source/working_with_sparklines.rst
new file mode 100644
index 0000000..9f95a62
--- /dev/null
+++ b/dev/docs/source/working_with_sparklines.rst
@@ -0,0 +1,321 @@
+.. _sparklines:
+
+Working with Sparklines
+=======================
+
+Sparklines are a feature of Excel 2010+ which allows you to add small charts to
+worksheet cells. These are useful for showing visual trends in data in a
+compact format.
+
+.. image:: _images/sparklines1.png
+
+Sparklines were invented by Edward Tufte:
+http://en.wikipedia.org/wiki/Sparklines
+
+The add_sparkline() method
+--------------------------
+
+The :func:`add_sparkline` worksheet method is used to add sparklines to a cell
+or a range of cells::
+
+ worksheet.add_sparkline(0, 5, {'range': 'Sheet1!A1:E1'})
+
+Both row-column and A1 style notation are supported. See :ref:`cell_notation`
+for more details.
+
+The parameters to ``add_sparkline()`` must be passed in a dictionary. The main
+sparkline parameters are:
+
+
++------------------+
+| range (required) |
++------------------+
+| type |
++------------------+
+| style |
++------------------+
+| markers |
++------------------+
+| negative_points |
++------------------+
+| axis |
++------------------+
+| reverse |
++------------------+
+
+Other, less commonly used parameters are:
+
++----------------+
+| location |
++----------------+
+| high_point |
++----------------+
+| low_point |
++----------------+
+| first_point |
++----------------+
+| last_point |
++----------------+
+| max |
++----------------+
+| min |
++----------------+
+| empty_cells |
++----------------+
+| show_hidden |
++----------------+
+| date_axis |
++----------------+
+| weight |
++----------------+
+| series_color |
++----------------+
+| negative_color |
++----------------+
+| markers_color |
++----------------+
+| first_color |
++----------------+
+| last_color |
++----------------+
+| high_color |
++----------------+
+| low_color |
++----------------+
+
+.. image:: _images/sparklines2.png
+
+
+These parameters are explained in the sections below.
+
+.. Note::
+ Sparklines are a feature of Excel 2010+ only. You can write them to an
+ XLSX file that can be read by Excel 2007 but they won't be displayed.
+
+
+range
+-----
+
+The ``range`` specifier is the only non-optional parameter.
+
+It specifies the cell data range that the sparkline will plot::
+
+ worksheet.add_sparkline('F1', {'range': 'A1:E1'})
+
+The ``range`` should be a 2D array. (For 3D arrays of cells see "Grouped
+Sparklines" below).
+
+If ``range`` is not on the same worksheet you can specify its location using
+the usual Excel notation::
+
+ worksheet.add_sparkline('F1', {'range': 'Sheet2!A1:E1'})
+
+If the worksheet contains spaces or special characters you should quote the
+worksheet name in the same way that Excel does::
+
+ worksheet.add_sparkline('F1', {'range': "'Monthly Data'!A1:E1"})
+
+
+type
+----
+
+Specifies the type of sparkline. There are 3 available sparkline types::
+
+ line (default)
+ column
+ win_loss
+
+For example::
+
+ worksheet.add_sparkline('F2', {'range': 'A2:E2',
+ 'type': 'column'})
+
+
+style
+-----
+
+Excel provides 36 built-in Sparkline styles in 6 groups of 6. The ``style``
+parameter can be used to replicate these and should be a corresponding number
+from 1 .. 36::
+
+ worksheet.add_sparkline('F2', {'range': 'A2:E2',
+ 'type': 'column',
+ 'style': 12})
+
+The style number starts in the top left of the style grid and runs left to
+right. The default style is 1. It is possible to override color elements of
+the sparklines using the ``_color`` parameters below.
+
+
+markers
+-------
+
+Turn on the markers for ``line`` style sparklines::
+
+ worksheet.add_sparkline('A6', {'range': 'Sheet2!A1:J1',
+ 'markers': True})
+
+Markers aren't shown in Excel for ``column`` and ``win_loss`` sparklines.
+
+
+negative_points
+---------------
+
+Highlight negative values in a sparkline range. This is usually required with
+``win_loss`` sparklines::
+
+ worksheet.add_sparkline('A9', {'range': 'Sheet2!A1:J1',
+ 'negative_points': True})
+
+
+axis
+----
+
+Display a horizontal axis in the sparkline::
+
+ worksheet.add_sparkline('A10', {'range': 'Sheet2!A1:J1',
+ 'axis': True})
+
+
+reverse
+-------
+
+Plot the data from right-to-left instead of the default left-to-right::
+
+ worksheet.add_sparkline('A24', {'range': 'Sheet2!A4:J4',
+ 'type': 'column',
+ 'style': 20,
+ 'reverse': True})
+
+
+weight
+------
+
+Adjust the default line weight (thickness) for ``line`` style sparklines::
+
+ worksheet.add_sparkline('F2', {'range': 'A2:E2',
+ 'weight': 0.25})
+
+The weight value should be one of the following values allowed by Excel::
+
+ 0.25, 0.5, 0.75, 1, 1.25, 2.25, 3, 4.25, 6
+
+high_point, low_point, first_point, last_point
+----------------------------------------------
+
+Highlight points in a sparkline range::
+
+ worksheet.add_sparkline('A7', {'range': 'Sheet2!A1:J1',
+ 'high_point': True,
+ 'low_point': True,
+ 'first_point': True})
+
+
+max, min
+--------
+
+Specify the maximum and minimum vertical axis values::
+
+ worksheet.add_sparkline('F1', {'range': 'A1:E1',
+ 'max': 0.5,
+ 'min': -0.5})
+
+As a special case you can set the maximum and minimum to be for a group of
+sparklines rather than one::
+
+ 'max': 'group'
+
+See "Grouped Sparklines" below.
+
+
+empty_cells
+-----------
+
+Define how empty cells are handled in a sparkline::
+
+ worksheet.add_sparkline('F1', {'range': 'A1:E1',
+ 'empty_cells': 'zero'})
+
+The available options are:
+
+* ``gaps``: show empty cells as gaps (the default).
+* ``zero``: plot empty cells as 0.
+* ``connect``: Connect points with a line ("line" type sparklines only).
+
+
+show_hidden
+-----------
+
+Plot data in hidden rows and columns::
+
+ worksheet.add_sparkline('F3', {'range': 'A3:E3',
+ 'show_hidden': True})
+
+Note, this option is off by default.
+
+
+date_axis
+---------
+
+Specify an alternative date axis for the sparkline. This is useful if the data
+being plotted isn't at fixed width intervals::
+
+ worksheet.add_sparkline('F3', {'range': 'A3:E3',
+ 'date_axis': 'A4:E4'})
+
+The number of cells in the date range should correspond to the number of cells
+in the data range.
+
+
+series_color
+------------
+
+It is possible to override the color of a sparkline style using the following
+parameters::
+
+ series_color
+ negative_color
+ markers_color
+ first_color
+ last_color
+ high_color
+ low_color
+
+The color should be specified as a HTML style ``#rrggbb`` hex value::
+
+ worksheet.add_sparkline('A18', {'range': 'Sheet2!A2:J2',
+ 'type': 'column',
+ 'series_color': '#E965E0'})
+
+location
+--------
+
+By default the sparkline location is specified by ``row`` and ``col`` in
+:func:`add_sparkline`. However, for grouped sparklines it is necessary to
+specify more than one cell location. The ``location`` parameter is used to
+specify a list of cells. See "Grouped Sparklines" below.
+
+
+Grouped Sparklines
+------------------
+
+The ``add_sparkline()`` worksheet method can be used multiple times to write as
+many sparklines as are required in a worksheet.
+
+However, it is sometimes necessary to group contiguous sparklines so that
+changes that are applied to one are applied to all. In Excel this is achieved
+by selecting a 3D range of cells for the data ``range`` and a 2D range of
+cells for the ``location``.
+
+In XlsxWriter, you can simulate this by passing an array refs of values to
+``location`` and ``range``::
+
+ worksheet.add_sparkline('A27', {'location': ['A27', 'A28', 'A29'],
+ 'range': ['A5:J5', 'A6:J6', 'A7:J7']})
+
+
+Sparkline examples
+------------------
+
+See :ref:`ex_sparklines1` and :ref:`ex_sparklines2`.
+
diff --git a/dev/docs/source/working_with_tables.rst b/dev/docs/source/working_with_tables.rst
new file mode 100644
index 0000000..c8bcfdc
--- /dev/null
+++ b/dev/docs/source/working_with_tables.rst
@@ -0,0 +1,398 @@
+.. _tables:
+
+Working with Worksheet Tables
+=============================
+
+Tables in Excel are a way of grouping a range of cells into a single entity
+that has common formatting or that can be referenced from formulas. Tables can
+have column headers, autofilters, total rows, column formulas and default
+formatting.
+
+.. image:: _images/tables12.png
+
+For more information see
+`An Overview of Excel Tables <http://office.microsoft.com/en-us/excel-help/overview-of-excel-tables-HA010048546.aspx>`_
+in the Microsoft Office documentation.
+
+.. Note::
+
+ Tables aren't available in XlsxWriter when :func:`Workbook`
+ ``'constant_memory'`` mode is enabled.
+
+
+add_table()
+-----------
+
+Tables are added to a worksheet using the :func:`add_table()` method::
+
+ worksheet.add_table('B3:F7', {options})
+
+The data range can be specified in 'A1' or 'Row/Column' notation (see
+:ref:`cell_notation`)::
+
+ worksheet.add_table('B3:F7')
+ # Same as:
+ worksheet.add_table(2, 1, 6, 5)
+
+.. image:: _images/tables1.png
+
+The options parameter should be a dict containing the parameters that describe
+the table options and data. The available options are:
+
++----------------+
+| data |
++----------------+
+| autofilter |
++----------------+
+| header_row |
++----------------+
+| banded_columns |
++----------------+
+| banded_rows |
++----------------+
+| first_column |
++----------------+
+| last_column |
++----------------+
+| style |
++----------------+
+| total_row |
++----------------+
+| columns |
++----------------+
+| name |
++----------------+
+
+
+These options are explained below. There are no required parameters and the
+options parameter is itself optional if no options are specified (as shown
+above).
+
+
+data
+----
+
+The ``data`` parameter can be used to specify the data in the cells of the
+table::
+
+ data = [
+ ['Apples', 10000, 5000, 8000, 6000],
+ ['Pears', 2000, 3000, 4000, 5000],
+ ['Bananas', 6000, 6000, 6500, 6000],
+ ['Oranges', 500, 300, 200, 700],
+
+ ]
+
+ worksheet.add_table('B3:F7', {'data': data})
+
+.. image:: _images/tables2.png
+
+Table data can also be written separately, as an array or individual cells::
+
+ # These statements are the same as the single statement above.
+ worksheet.add_table('B3:F7')
+ worksheet.write_row('B4', data[0])
+ worksheet.write_row('B5', data[1])
+ worksheet.write_row('B6', data[2])
+ worksheet.write_row('B7', data[3])
+
+Writing the cell data separately is occasionally required when you need to
+control the ``write_()`` methods used to populate the cells or if you wish to
+modify individual cell formatting.
+
+The ``data`` structure should be an list of lists holding row data as shown
+above.
+
+
+header_row
+----------
+
+The ``header_row`` parameter can be used to turn on or off the header row in
+the table. It is on by default::
+
+ # Turn off the header row.
+ worksheet.add_table('B4:F7', {'header_row': False})
+
+.. image:: _images/tables4.png
+
+
+The header row will contain default captions such as ``Column 1``,
+``Column 2``, etc. These captions can be overridden using the ``columns``
+parameter below.
+
+
+autofilter
+----------
+
+The ``autofilter`` parameter can be used to turn on or off the autofilter in
+the header row. It is on by default::
+
+ # Turn off the default autofilter.
+ worksheet.add_table('B3:F7', {'autofilter': False})
+
+.. image:: _images/tables3.png
+
+The ``autofilter`` is only shown if the ``header_row`` is on. Filter conditions
+within the table are not supported.
+
+
+banded_rows
+-----------
+
+The ``banded_rows`` parameter can be used to create rows of alternating color
+in the table. It is on by default::
+
+ # Turn off banded rows.
+ worksheet.add_table('B3:F7', {'banded_rows': False})
+
+.. image:: _images/tables6.png
+
+banded_columns
+--------------
+
+The ``banded_columns`` parameter can be used to used to create columns of
+alternating color in the table. It is off by default::
+
+ # Turn on banded columns.
+ worksheet.add_table('B3:F7', {'banded_columns': True})
+
+See the above image.
+
+first_column
+------------
+
+The ``first_column`` parameter can be used to highlight the first column of the
+table. The type of highlighting will depend on the ``style`` of the table. It
+may be bold text or a different color. It is off by default::
+
+ # Turn on highlighting for the first column in the table.
+ worksheet.add_table('B3:F7', {'first_column': True})
+
+.. image:: _images/tables5.png
+
+last_column
+-----------
+
+The ``last_column`` parameter can be used to highlight the last column of the
+table. The type of highlighting will depend on the ``style`` of the table. It
+may be bold text or a different color. It is off by default::
+
+ # Turn on highlighting for the last column in the table.
+ worksheet.add_table('B3:F7', {'last_column': True})
+
+See the above image.
+
+style
+-----
+
+The ``style`` parameter can be used to set the style of the table. Standard
+Excel table format names should be used (with matching capitalization)::
+
+ worksheet.add_table('B3:F7', {'data': data,
+ 'style': 'Table Style Light 11'})
+
+.. image:: _images/tables11.png
+
+The default table style is 'Table Style Medium 9'.
+
+
+name
+----
+
+By default tables are named ``Table1``, ``Table2``, etc. The ``name``
+parameter can be used to set the name of the table::
+
+ worksheet.add_table('B3:F7', {'name': 'SalesData'})
+
+If you override the table name you must ensure that it doesn't clash with an
+existing table name and that it follows Excel's requirements for table names,
+see the `Microsoft Office documentation
+<http://office.microsoft.com/en-001/excel-help/define-and-use-names-in-formulas-HA010147120.aspx>`_.
+
+If you need to know the name of the table, for example to use it in a formula,
+you can get it as follows::
+
+ table = worksheet.add_table('B3:F7')
+ table_name = table.name
+
+
+total_row
+---------
+
+The ``total_row`` parameter can be used to turn on the total row in the last
+row of a table. It is distinguished from the other rows by a different
+formatting and also with dropdown ``SUBTOTAL`` functions::
+
+ worksheet.add_table('B3:F7', {'total_row': True})
+
+.. image:: _images/tables9.png
+
+The default total row doesn't have any captions or functions. These must by
+specified via the ``columns`` parameter below.
+
+
+columns
+-------
+
+The ``columns`` parameter can be used to set properties for columns within the
+table.
+
+.. image:: _images/tables7.png
+
+The sub-properties that can be set are:
+
++----------------+
+| header |
++----------------+
+| header_format |
++----------------+
+| formula |
++----------------+
+| total_string |
++----------------+
+| total_function |
++----------------+
+| total_value |
++----------------+
+| format |
++----------------+
+
+The column data must be specified as a list of dicts. For example to override
+the default 'Column n' style table headers::
+
+ worksheet.add_table('B3:F7', {'data': data,
+ 'columns': [{'header': 'Product'},
+ {'header': 'Quarter 1'},
+ {'header': 'Quarter 2'},
+ {'header': 'Quarter 3'},
+ {'header': 'Quarter 4'},
+ ]})
+
+See the resulting image above.
+
+If you don't wish to specify properties for a specific column you pass an empty
+hash ref and the defaults will be applied::
+
+ ...
+ columns, [
+ {header, 'Product'},
+ {header, 'Quarter 1'},
+ {}, # Defaults to 'Column 3'.
+ {header, 'Quarter 3'},
+ {header, 'Quarter 4'},
+ ]
+ ...
+
+Column formulas can by applied using the column ``formula`` property::
+
+ formula = '=SUM(Table8[@[Quarter 1]:[Quarter 4]])'
+
+ worksheet.add_table('B3:G7', {'data': data,
+ 'columns': [{'header': 'Product'},
+ {'header': 'Quarter 1'},
+ {'header': 'Quarter 2'},
+ {'header': 'Quarter 3'},
+ {'header': 'Quarter 4'},
+ {'header': 'Year',
+ 'formula': formula},
+ ]})
+
+.. image:: _images/tables8.png
+
+The Excel 2007 style ``[#This Row]`` and Excel 2010 style ``@`` structural
+references are supported within the formula. However, other Excel 2010
+additions to structural references aren't supported and formulas should
+conform to Excel 2007 style formulas. See the Microsoft documentation on
+`Using structured references with Excel tables <http://office.microsoft.com/en-us/excel-help/using-structured-references-with-excel-tables-HA010155686.aspx>`_
+for details.
+
+As stated above the ``total_row`` table parameter turns on the "Total" row in
+the table but it doesn't populate it with any defaults. Total captions and
+functions must be specified via the ``columns`` property and the
+``total_string`` and ``total_function`` sub properties::
+
+ options = {'data': data,
+ 'total_row': 1,
+ 'columns': [{'header': 'Product', 'total_string': 'Totals'},
+ {'header': 'Quarter 1', 'total_function': 'sum'},
+ {'header': 'Quarter 2', 'total_function': 'sum'},
+ {'header': 'Quarter 3', 'total_function': 'sum'},
+ {'header': 'Quarter 4', 'total_function': 'sum'},
+ {'header': 'Year',
+ 'formula': '=SUM(Table10[@[Quarter 1]:[Quarter 4]])',
+ 'total_function': 'sum'
+ },
+ ]}
+
+ # Add a table to the worksheet.
+ worksheet.add_table('B3:G8', options)
+
+The supported totals row ``SUBTOTAL`` functions are:
+
++------------+
+| average |
++------------+
+| count_nums |
++------------+
+| count |
++------------+
+| max |
++------------+
+| min |
++------------+
+| std_dev |
++------------+
+| sum |
++------------+
+| var |
++------------+
+
+User defined functions or formulas aren't supported.
+
+It is also possible to set a calculated value for the ``total_function`` using
+the ``total_value`` sub property. This is only necessary when creating
+workbooks for applications that cannot calculate the value of formulas
+automatically. This is similar to setting the ``value`` optional property in
+:func:`write_formula`::
+
+
+ options = {'data': data,
+ 'total_row': 1,
+ 'columns': [{'total_string': 'Totals'},
+ {'total_function': 'sum', 'total_value': 150},
+ {'total_function': 'sum', 'total_value': 200},
+ {'total_function': 'sum', 'total_value': 333},
+ {'total_function': 'sum', 'total_value': 124},
+ {'formula': '=SUM(Table10[@[Quarter 1]:[Quarter 4]])',
+ 'total_function': 'sum',
+ 'total_value': 807}]}
+
+Formatting can also be applied to columns, to the column data using ``format`` and to the header using ``header_format``::
+
+
+ currency_format = workbook.add_format({'num_format': '$#,##0'})
+ wrap_format = workbook.add_format({'text_wrap': 1})
+
+ worksheet.add_table('B3:D8', {'data': data,
+ 'total_row': 1,
+ 'columns': [{'header': 'Product'},
+ {'header': 'Quarter 1',
+ 'total_function': 'sum',
+ 'format': currency_format},
+ {'header': 'Quarter 2',
+ 'header_format': wrap_format,
+ 'total_function': 'sum',
+ 'format': currency_format}]})
+
+.. image:: _images/tables12.png
+
+Standard XlsxWriter :ref:`Format object <format>` objects are used for this
+formatting. However, they should be limited to numerical formats for the
+columns and simple formatting like text wrap for the headers. Overriding other
+table formatting may produce inconsistent results.
+
+
+Example
+-------
+
+All of the images shown above are taken from :ref:`ex_tables`.
diff --git a/dev/docs/source/working_with_textboxes.rst b/dev/docs/source/working_with_textboxes.rst
new file mode 100644
index 0000000..623023b
--- /dev/null
+++ b/dev/docs/source/working_with_textboxes.rst
@@ -0,0 +1,454 @@
+.. _working_with_textboxes:
+
+Working with Textboxes
+======================
+
+This section explains how to work with some of the options and features of
+textboxes in XlsxWriter::
+
+ import xlsxwriter
+
+ workbook = xlsxwriter.Workbook('textbox.xlsx')
+ worksheet = workbook.add_worksheet()
+
+ text = 'Formatted textbox'
+
+ options = {
+ 'width': 256,
+ 'height': 100,
+ 'x_offset': 10,
+ 'y_offset': 10,
+
+ 'font': {'color': 'red',
+ 'size': 14},
+ 'align': {'vertical': 'middle',
+ 'horizontal': 'center'
+ },
+ 'gradient': {'colors': ['#DDEBCF',
+ '#9CB86E',
+ '#156B13']},
+ }
+
+ worksheet.insert_textbox('B2', text, options)
+
+ workbook.close()
+
+.. image:: _images/textbox02.png
+
+See also :ref:`ex_textbox`.
+
+Textbox options
+---------------
+
+This Worksheet :func:`insert_textbox()` method is used to insert a textbox
+into a worksheet::
+
+ worksheet.insert_textbox('B2', 'A simple textbox with some text')
+
+.. image:: _images/textbox31.png
+
+
+The text can contain newlines to wrap the text::
+
+ worksheet.insert_textbox('B2', 'Line 1\nLine 2\n\nMore text')
+
+.. image:: _images/textbox32.png
+
+This :func:`insert_textbox()` takes an optional ``dict`` parameter that can be
+used to control the size, positioning and format of the textbox::
+
+ worksheet.insert_textbox('B2', 'Some text', {'width': 256, 'height': 100})
+
+The available options are::
+
+ # Size and position
+ width
+ height
+ x_scale
+ y_scale
+ x_offset
+ y_offset
+
+ # Formatting
+ line
+ border
+ fill
+ gradient
+ font
+ align
+
+These options are explained in the sections below. They are similar or
+identical to position and formatting parameters used in charts.
+
+
+Textbox size and positioning
+----------------------------
+
+The :func:`insert_textbox()` options to control the size and positioning of a
+textbox are::
+
+ width
+ height
+ x_scale
+ y_scale
+ x_offset
+ y_offset
+
+The ``width`` and ``height`` are in pixels. The default textbox size is 192 x
+120 pixels (or equivalent to 3 default columns x 6 default rows).
+
+.. image:: _images/textbox35.png
+
+The size of the textbox can be modified by setting the ``width`` and
+``height`` or by setting the ``x_scale`` and ``y_scale``::
+
+ worksheet.insert_textbox('B2', 'Size adjusted textbox',
+ {'width': 288, 'height': 30})
+
+ # or ...
+ worksheet.insert_textbox('B2', 'Size adjusted textbox',
+ {'x_scale': 1.5, 'y_scale': 0.25})
+
+.. image:: _images/textbox33.png
+
+The ``x_offset`` and ``y_offset`` position the top left corner of the textbox in
+the cell that it is inserted into.
+
+.. image:: _images/textbox34.png
+
+
+Textbox Formatting
+------------------
+
+
+
+The following formatting properties can be set for textbox objects::
+
+ line
+ border
+ fill
+ gradient
+ font
+ align
+
+Textbox formatting properties are set using the options dict::
+
+ worksheet.insert_textbox('B2', 'A textbox with a color text',
+ {'font': {'color': 'green'}})
+
+.. image:: _images/textbox11.png
+
+In some cases the format properties can be nested::
+
+ worksheet.insert_textbox('B2', 'Some text in a textbox with formatting',
+ {'font': {'color': 'white'},
+ 'align': {'vertical': 'middle',
+ 'horizontal': 'center'
+ },
+ 'gradient': {'colors': ['green', 'white']}})
+
+.. image:: _images/textbox12.png
+
+
+.. _textbox_formatting_line:
+
+Textbox formatting: Line
+------------------------
+
+The line format is used to specify properties of the border in a textbox. The
+following properties can be set for ``line`` formats in a textbox::
+
+ none
+ color
+ width
+ dash_type
+
+The ``none`` property is uses to turn the ``line`` off (it is always on by
+default)::
+
+ worksheet.insert_textbox('B2', 'A textbox with no border line',
+ {'line': {'none': True}})
+
+
+The ``color`` property sets the color of the ``line``::
+
+ worksheet.insert_textbox('B2', 'A textbox with a color border',
+ {'line': {'color': 'red'}})
+
+.. image:: _images/textbox13.png
+
+
+The available colors are shown in the main XlsxWriter documentation. It is
+also possible to set the color of a line with a Html style ``#RRGGBB`` string
+or a limited number of named colors, see :ref:`colors`::
+
+ worksheet.insert_textbox('B2', 'A textbox with a color border',
+ {'line': {'color': '#FF9900'}})
+
+.. image:: _images/textbox14.png
+
+
+The ``width`` property sets the width of the ``line``. It should be specified
+in increments of 0.25 of a point as in Excel::
+
+ worksheet.insert_textbox('B2', 'A textbox with larger border',
+ {'line': {'width': 3.25}})
+
+.. image:: _images/textbox15.png
+
+
+The ``dash_type`` property sets the dash style of the line::
+
+ worksheet.insert_textbox('B2', 'A textbox a dash border',
+ {'line': {'dash_type': 'dash_dot'}})
+
+.. image:: _images/textbox16.png
+
+The following ``dash_type`` values are available. They are shown in the order
+that they appear in the Excel dialog::
+
+ solid
+ round_dot
+ square_dot
+ dash
+ dash_dot
+ long_dash
+ long_dash_dot
+ long_dash_dot_dot
+
+The default line style is ``solid``.
+
+More than one ``line`` property can be specified at a time::
+
+ worksheet.insert_textbox('B2', 'A textbox with border formatting',
+ {'line': {'color': 'red',
+ 'width': 1.25,
+ 'dash_type': 'square_dot'}})
+
+.. image:: _images/textbox17.png
+
+.. _textbox_formatting_border:
+
+Textbox formatting: Border
+--------------------------
+
+The ``border`` property is a synonym for ``line``.
+
+Excel uses a common dialog for setting object formatting but depending on
+context it may refer to a *line* or a *border*. For formatting these can be
+used interchangeably.
+
+.. _textbox_formatting_fill:
+
+Textbox formatting: Solid Fill
+------------------------------
+
+The solid fill format is used to specify a fill for a textbox object.
+
+The following properties can be set for ``fill`` formats in a textbox::
+
+ none
+ color
+
+The ``none`` property is used to turn the ``fill`` property off (to make the
+textbox transparent)::
+
+ worksheet.insert_textbox('B2', 'A textbox with no fill',
+ {'fill': {'none': True}})
+
+.. image:: _images/textbox21.png
+
+The ``color`` property sets the color of the ``fill`` area::
+
+ worksheet.insert_textbox('B2', 'A textbox with color fill',
+ {'fill': {'color': '#FF9900'}})
+
+.. image:: _images/textbox22.png
+
+The available colors are shown in the main XlsxWriter documentation. It is
+also possible to set the color of a fill with a Html style ``#RRGGBB`` string
+or a limited number of named colors, see :ref:`colors`::
+
+ worksheet.insert_textbox('B2', 'A textbox with color fill',
+ {'fill': {'color': 'red'}})
+
+
+.. _textbox_formatting_gradient:
+
+Textbox formatting: Gradient Fill
+---------------------------------
+
+The gradient fill format is used to specify a gradient fill for a textbox. The
+following properties can be set for ``gradient`` fill formats in a textbox::
+
+ colors: a list of colors
+ positions: an optional list of positions for the colors
+ type: the optional type of gradient fill
+ angle: the optional angle of the linear fill
+
+If gradient fill is used on a textbox object it overrides the solid fill
+properties of the object.
+
+The ``colors`` property sets a list of colors that define the ``gradient``::
+
+ worksheet.insert_textbox('B2', 'A textbox with gradient fill',
+ {'gradient': {'colors': ['gray', 'white']}})
+
+.. image:: _images/textbox23.png
+
+Excel allows between 2 and 10 colors in a gradient but it is unlikely that
+you will require more than 2 or 3.
+
+As with solid fill it is also possible to set the colors of a gradient with a
+Html style ``#RRGGBB`` string or a limited number of named colors, see
+:ref:`colors`::
+
+ worksheet.insert_textbox('B2', 'A textbox with gradient fill',
+ {'gradient': {'colors': ['#DDEBCF',
+ '#9CB86E',
+ '#156B13']}})
+
+.. image:: _images/textbox24.png
+
+The ``positions`` defines an optional list of positions, between 0 and 100, of
+where the colors in the gradient are located. Default values are provided for
+``colors`` lists of between 2 and 4 but they can be specified if required::
+
+ worksheet.insert_textbox('B2', 'A textbox with gradient fill',
+ {'gradient': {'colors': ['#DDEBCF', '#156B13'],
+ 'positions': [10, 90]}})
+
+The ``type`` property can have one of the following values::
+
+ linear (the default)
+ radial
+ rectangular
+ path
+
+For example::
+
+ worksheet.insert_textbox('B2', 'A textbox with gradient fill',
+ {'gradient': {'colors': ['#DDEBCF', '#9CB86E', '#156B13'],
+ 'type': 'radial'}})
+
+.. image:: _images/textbox25.png
+
+If ``type`` isn't specified it defaults to ``linear``.
+
+For a ``linear`` fill the angle of the gradient can also be specified (the
+default angle is 90 degrees)::
+
+ worksheet.insert_textbox('B2', 'A textbox with angle gradient',
+ {'gradient': {'colors': ['#DDEBCF', '#9CB86E', '#156B13'],
+ 'angle': 45}})
+
+
+.. _textbox_fonts:
+
+Textbox Fonts
+-------------
+
+The following font properties can be set for the entire textbox::
+
+ name
+ size
+ bold
+ italic
+ underline
+ color
+
+These properties correspond to the equivalent Worksheet cell Format object
+properties. See the :ref:`format` section for more details about Format
+properties and how to set them.
+
+The font properties are:
+
+
+* ``name``: Set the font name::
+
+ {'font': {'name': 'Arial'}}
+
+* ``size``: Set the font size::
+
+ {'font': {'name': 'Arial', 'size': 9}}
+
+* ``bold``: Set the font bold property::
+
+ {'font': {'bold': True}}
+
+* ``italic``: Set the font italic property::
+
+ {'font': {'italic': True}}
+
+* ``underline``: Set the font underline property::
+
+ {'font': {'underline': True}}
+
+* ``color``: Set the font color property. Can be a color index, a color name
+ or HTML style RGB color::
+
+ {'font': {'color': 'red' }}
+ {'font': {'color': '#92D050'}}
+
+
+Here is an example of Font formatting in a textbox::
+
+ worksheet.insert_textbox('B2', 'Some font formatting',
+ {'font': {'bold': True,
+ 'italic': True,
+ 'underline': True,
+ 'name': 'Arial',
+ 'color': 'red',
+ 'size': 14}})
+
+.. image:: _images/textbox26.png
+
+
+.. _textbox_align:
+
+Textbox Align
+-------------
+
+The ``align`` property is used to set the text alignment for the entire textbox::
+
+ worksheet.insert_textbox('B2', 'Alignment: middle - center',
+ {'align': {'vertical': 'middle',
+ 'horizontal': 'center'}})
+
+.. image:: _images/textbox41.png
+
+The alignment properties that can be set in Excel for a textbox are::
+
+ {'align': {'vertical': 'top'}} # Default
+ {'align': {'vertical': 'middle'}}
+ {'align': {'vertical': 'bottom'}}
+
+ {'align': {'horizontal': 'left'}} # Default
+ {'align': {'horizontal': 'center'}}
+
+Note, Excel doesn't support right text alignment for the entire textbox. It
+does support it for text within the textbox but that currently isn't supported
+by XlsxWriter, see the next section.
+
+The default textbox alignment is::
+
+ worksheet.insert_textbox('B2', 'Default alignment',
+ {'align': {'vertical': 'top',
+ 'horizontal': 'left'}})
+
+ # Same as this:
+ worksheet.insert_textbox('B2', 'Default alignment')
+
+.. image:: _images/textbox42.png
+
+
+Other Textbox Features
+----------------------
+
+Excel textboxes have a large range of possible options. Where possible, these
+will be added, if a feature request is opened on `GitHub
+<https://github.com/jmcnamara/XlsxWriter>`_, and there is interest from more
+than one person.
+
+Inline text formatting like :func:`write_rich_string()` will probably be added
+in an upcoming release.
diff --git a/dev/docs/source/worksheet.rst b/dev/docs/source/worksheet.rst
new file mode 100644
index 0000000..1e78de1
--- /dev/null
+++ b/dev/docs/source/worksheet.rst
@@ -0,0 +1,2120 @@
+.. _worksheet:
+
+The Worksheet Class
+===================
+
+The worksheet class represents an Excel worksheet. It handles operations such
+as writing data to cells or formatting worksheet layout.
+
+A worksheet object isn't instantiated directly. Instead a new worksheet is
+created by calling the :func:`add_worksheet()` method from a :func:`Workbook`
+object::
+
+ workbook = xlsxwriter.Workbook('filename.xlsx')
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+
+ worksheet1.write('A1', 123)
+
+ workbook.close()
+
+
+.. image:: _images/worksheet00.png
+
+XlsxWriter supports Excels worksheet limits of 1,048,576 rows by 16,384
+columns.
+
+
+worksheet.write()
+-----------------
+
+.. py:function:: write(row, col, *args)
+
+ Write generic data to a worksheet cell.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param \*args: The additional args that are passed to the sub methods
+ such as number, string and cell_format.
+
+Excel makes a distinction between data types such as strings, numbers, blanks,
+formulas and hyperlinks. To simplify the process of writing data to an
+XlsxWriter file the ``write()`` method acts as a general alias for several
+more specific methods:
+
+* :func:`write_string()`
+* :func:`write_number()`
+* :func:`write_blank()`
+* :func:`write_formula()`
+* :func:`write_datetime()`
+* :func:`write_boolean()`
+* :func:`write_url()`
+
+The rules for handling data in ``write()`` are as follows:
+
+* Data types ``float``, ``int``, ``long``, :class:`decimal.Decimal` and
+ :class:`fractions.Fraction` are written using :func:`write_number()`.
+
+* Data types :class:`datetime.datetime`, :class:`datetime.date`
+ :class:`datetime.time` or :class:`datetime.timedelta` are written using
+ :func:`write_datetime()` .
+
+* ``None`` and empty strings ``""`` are written using :func:`write_blank()`.
+
+* Data type ``bool`` is written using :func:`write_boolean()`.
+
+Strings are then handled as follows:
+
+* Strings that start with ``"="`` are take to match a formula and are written
+ using :func:`write_formula()`. This can be overridden, see below.
+
+* Strings that match supported URL types are written using
+ :func:`write_url()`. This can be overridden, see below.
+
+* When the :func:`Workbook` constructor ``strings_to_numbers`` option is
+ ``True`` strings that convert to numbers using :func:`float()` are written
+ using :func:`write_number()` in order to avoid Excel warnings about "Numbers
+ Stored as Text". See the note below.
+
+* Strings that don't match any of the above criteria are written using
+ :func:`write_string()`.
+
+If none of the above types are matched the value is evaluated with ``float()``
+to see if it corresponds to a user defined float type. If it does then it is
+written using :func:`write_number()`.
+
+Finally, if none of these rules are matched then a ``TypeError`` exception is
+raised.
+
+Here are some examples::
+
+ worksheet.write(0, 0, 'Hello') # write_string()
+ worksheet.write(1, 0, 'World') # write_string()
+ worksheet.write(2, 0, 2) # write_number()
+ worksheet.write(3, 0, 3.00001) # write_number()
+ worksheet.write(4, 0, '=SIN(PI()/4)') # write_formula()
+ worksheet.write(5, 0, '') # write_blank()
+ worksheet.write(6, 0, None) # write_blank()
+
+This creates a worksheet like the following:
+
+.. image:: _images/worksheet01.png
+
+.. note::
+
+ The :func:`Workbook` constructor option takes three optional arguments
+ that can be used to override string handling in the ``write()`` function.
+ These options are shown below with their default values::
+
+ xlsxwriter.Workbook(filename, {'strings_to_numbers': False,
+ 'strings_to_formulas': True,
+ 'strings_to_urls': True})
+
+The ``write()`` method supports two forms of notation to designate the position
+of cells: **Row-column** notation and **A1** notation::
+
+ # These are equivalent.
+ worksheet.write(0, 0, 'Hello')
+ worksheet.write('A1', 'Hello')
+
+See :ref:`cell_notation` for more details.
+
+The ``cell_format`` parameter in the sub ``write`` methods is used to apply
+formatting to the cell. This parameter is optional but when present it should
+be a valid :ref:`Format <format>` object::
+
+ cell_format = workbook.add_format({'bold': True, 'italic': True})
+
+ worksheet.write(0, 0, 'Hello', cell_format) # Cell is bold and italic.
+
+
+worksheet.write_string()
+------------------------
+
+.. py:function:: write_string(row, col, string[, cell_format])
+
+ Write a string to a worksheet cell.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param string: String to write to cell.
+ :param cell_format: Optional Format object.
+ :type row: int
+ :type col: int
+ :type string: string
+ :type cell_format: :ref:`Format <format>`
+
+The ``write_string()`` method writes a string to the cell specified by ``row``
+and ``column``::
+
+ worksheet.write_string(0, 0, 'Your text here')
+ worksheet.write_string('A2', 'or here')
+
+Both row-column and A1 style notation are supported. See :ref:`cell_notation`
+for more details.
+
+The ``cell_format`` parameter is used to apply formatting to the cell. This
+parameter is optional but when present is should be a valid
+:ref:`Format <format>` object.
+
+Unicode strings are supported in UTF-8 encoding. This generally requires that
+your source file in also UTF-8 encoded::
+
+ # _*_ coding: utf-8
+
+ worksheet.write('A1', u'Some UTF-8 text')
+
+.. image:: _images/worksheet02.png
+
+Alternatively, you can read data from an encoded file, convert it to UTF-8
+during reading and then write the data to an Excel file. There are several
+sample``unicode_\*.py`` programs like this in the ``examples`` directory of the XlsxWriter source tree.
+
+The maximum string size supported by Excel is 32,767 characters. Strings longer
+than this will be truncated by ``write_string()``.
+
+.. note::
+
+ Even though Excel allows strings of 32,767 characters it can only
+ **display** 1000 in a cell. However, all 32,767 characters are displayed in the
+ formula bar.
+
+
+worksheet.write_number()
+------------------------
+
+.. py:function:: write_number(row, col, number[, cell_format])
+
+ Write a number to a worksheet cell.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param number: Number to write to cell.
+ :param cell_format: Optional Format object.
+ :type row: int
+ :type col: int
+ :type number: int or float
+ :type cell_format: :ref:`Format <format>`
+
+The ``write_number()`` method writes numeric types to the cell specified by
+``row`` and ``column``::
+
+ worksheet.write_number(0, 0, 123456)
+ worksheet.write_number('A2', 2.3451)
+
+The numeric types supported are ``float``, ``int``, ``long``,
+:class:`decimal.Decimal` and :class:`fractions.Fraction` or anything that can
+be converted via ``float()``.
+
+When written to an Excel file numbers are converted to IEEE-754 64-bit
+double-precision floating point. This means that, in most cases, the maximum
+number of digits that can be stored in Excel without losing precision is 15.
+
+.. note::
+ NAN and INF are not supported and will raise a TypeError exception.
+
+Both row-column and A1 style notation are supported. See :ref:`cell_notation`
+for more details.
+
+The ``cell_format`` parameter is used to apply formatting to the cell. This
+parameter is optional but when present is should be a valid
+:ref:`Format <format>` object.
+
+
+worksheet.write_formula()
+-------------------------
+
+.. py:function:: write_formula(row, col, formula[, cell_format[, value]])
+
+ Write a formula to a worksheet cell.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param formula: Formula to write to cell.
+ :param cell_format: Optional Format object.
+ :param value: Optional result. The value if the formula was calculated.
+ :type row: int
+ :type col: int
+ :type formula: string
+ :type cell_format: :ref:`Format <format>`
+
+The ``write_formula()`` method writes a formula or function to the cell
+specified by ``row`` and ``column``::
+
+ worksheet.write_formula(0, 0, '=B3 + B4')
+ worksheet.write_formula(1, 0, '=SIN(PI()/4)')
+ worksheet.write_formula(2, 0, '=SUM(B1:B5)')
+ worksheet.write_formula('A4', '=IF(A3>1,"Yes", "No")')
+ worksheet.write_formula('A5', '=AVERAGE(1, 2, 3, 4)')
+ worksheet.write_formula('A6', '=DATEVALUE("1-Jan-2013")')
+
+Array formulas are also supported::
+
+ worksheet.write_formula('A7', '{=SUM(A1:B1*A2:B2)}')
+
+See also the ``write_array_formula()`` method below.
+
+Both row-column and A1 style notation are supported. See :ref:`cell_notation`
+for more details.
+
+The ``cell_format`` parameter is used to apply formatting to the cell. This
+parameter is optional but when present is should be a valid
+:ref:`Format <format>` object.
+
+If required, it is also possible to specify the calculated result of the
+formula using the optional ``value`` parameter. This is occasionally
+necessary when working with non-Excel applications that don't calculate the
+result of the formula::
+
+ worksheet.write('A1', '=2+2', num_format, 4)
+
+See :ref:`formula_result` for more details.
+
+Excel stores formulas in US style formatting regardless of the Locale or
+Language of the Excel version::
+
+ worksheet.write_formula('A1', '=SUM(1, 2, 3)') # OK
+ worksheet.write_formula('A2', '=SOMME(1, 2, 3)') # French. Error on load.
+
+See :ref:`formula_syntax` for a full explanation.
+
+Excel 2010 and 2013 added functions which weren't defined in the original file
+specification. These functions are referred to as *future* functions. Examples
+of these functions are ``ACOT``, ``CHISQ.DIST.RT`` , ``CONFIDENCE.NORM``,
+``STDEV.P``, ``STDEV.S`` and ``WORKDAY.INTL``. In XlsxWriter these require a
+prefix::
+
+ worksheet.write_formula('A1', '=_xlfn.STDEV.S(B1:B10)')
+
+See :ref:`formula_future` for a detailed explanation and full list of
+functions that are affected.
+
+
+worksheet.write_array_formula()
+-------------------------------
+
+.. py:function:: write_array_formula(first_row, first_col, last_row, \
+ last_col, formula[, cell_format[, value]])
+
+ Write an array formula to a worksheet cell.
+
+ :param first_row: The first row of the range. (All zero indexed.)
+ :param first_col: The first column of the range.
+ :param last_row: The last row of the range.
+ :param last_col: The last col of the range.
+ :param formula: Array formula to write to cell.
+ :param cell_format: Optional Format object.
+ :param value: Optional result. The value if the formula was calculated.
+ :type first_row: int
+ :type first_col: int
+ :type last_row: int
+ :type last_col: int
+ :type formula: string
+ :type cell_format: :ref:`Format <format>`
+
+The ``write_array_formula()`` method writes an array formula to a cell range. In
+Excel an array formula is a formula that performs a calculation on a set of
+values. It can return a single value or a range of values.
+
+An array formula is indicated by a pair of braces around the formula:
+``{=SUM(A1:B1*A2:B2)}``.
+
+For array formulas that return a range of values you must specify the range
+that the return values will be written to::
+
+ worksheet.write_array_formula('A1:A3', '{=TREND(C1:C3,B1:B3)}')
+ worksheet.write_array_formula(0, 0, 2, 0, '{=TREND(C1:C3,B1:B3)}')
+
+If the array formula returns a single value then the ``first_`` and ``last_``
+parameters should be the same::
+
+ worksheet.write_array_formula('A1:A1', '{=SUM(B1:C1*B2:C2)}')
+
+It this case however it is easier to just use the ``write_formula()`` or
+``write()`` methods::
+
+ # Same as above but more concise.
+ worksheet.write('A1', '{=SUM(B1:C1*B2:C2)}')
+ worksheet.write_formula('A1', '{=SUM(B1:C1*B2:C2)}')
+
+As shown above, both row-column and A1 style notation are supported. See
+:ref:`cell_notation` for more details.
+
+The ``cell_format`` parameter is used to apply formatting to the cell. This
+parameter is optional but when present is should be a valid
+:ref:`Format <format>` object.
+
+If required, it is also possible to specify the calculated result of the
+formula (see discussion of formulas and the ``value`` parameter for the
+``write_formula()`` method above). However, using this parameter only writes a
+single value to the upper left cell in the result array. See
+:ref:`formula_result` for more details.
+
+See also :ref:`ex_array_formula`.
+
+
+worksheet.write_blank()
+-----------------------
+
+.. py:function:: write_blank(row, col, blank[, cell_format])
+
+ Write a blank worksheet cell.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param blank: None or empty string. The value is ignored.
+ :param cell_format: Optional Format object.
+ :type row: int
+ :type col: int
+ :type cell_format: :ref:`Format <format>`
+
+Write a blank cell specified by ``row`` and ``column``::
+
+ worksheet.write_blank(0, 0, None, format)
+
+This method is used to add formatting to a cell which doesn't contain a string
+or number value.
+
+Excel differentiates between an "Empty" cell and a "Blank" cell. An "Empty"
+cell is a cell which doesn't contain data or formatting whilst a "Blank" cell
+doesn't contain data but does contain formatting. Excel stores "Blank" cells
+but ignores "Empty" cells.
+
+As such, if you write an empty cell without formatting it is ignored::
+
+ worksheet.write('A1', None, format) # write_blank()
+ worksheet.write('A2', None) # Ignored
+
+This seemingly uninteresting fact means that you can write arrays of data
+without special treatment for ``None`` or empty string values.
+
+As shown above, both row-column and A1 style notation are supported. See
+:ref:`cell_notation` for more details.
+
+worksheet.write_boolean()
+-------------------------
+
+.. py:function:: write_boolean(row, col, boolean[, cell_format])
+
+ Write a boolean value to a worksheet cell.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param boolean: Boolean value to write to cell.
+ :param cell_format: Optional Format object.
+ :type row: int
+ :type col: int
+ :type boolean: bool
+ :type cell_format: :ref:`Format <format>`
+
+The ``write_boolean()`` method writes a boolean value to the cell specified by
+``row`` and ``column``::
+
+ worksheet.write_boolean(0, 0, True)
+ worksheet.write_boolean('A2', False)
+
+Both row-column and A1 style notation are supported. See :ref:`cell_notation`
+for more details.
+
+The ``cell_format`` parameter is used to apply formatting to the cell. This
+parameter is optional but when present is should be a valid
+:ref:`Format <format>` object.
+
+
+worksheet.write_datetime()
+--------------------------
+
+.. py:function:: write_datetime(row, col, datetime [, cell_format])
+
+ Write a date or time to a worksheet cell.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param datetime: A datetime.datetime, .date, .time or .delta object.
+ :param cell_format: Optional Format object.
+ :type row: int
+ :type col: int
+ :type formula: string
+ :type datetime: :mod:`datetime`
+ :type cell_format: :ref:`Format <format>`
+
+The ``write_datetime()`` method can be used to write a date or time to the cell
+specified by ``row`` and ``column``::
+
+ worksheet.write_datetime(0, 0, datetime, date_format)
+
+The datetime should be a :class:`datetime.datetime`, :class:`datetime.date`
+:class:`datetime.time` or :class:`datetime.timedelta` object. The
+:mod:`datetime` class is part of the standard Python libraries.
+
+There are many ways to create datetime objects, for example the
+:meth:`datetime.datetime.strptime` method::
+
+ date_time = datetime.datetime.strptime('2013-01-23', '%Y-%m-%d')
+
+See the :mod:`datetime` documentation for other date/time creation methods.
+
+A date/time should have a ``cell_format`` of type :ref:`Format <format>`,
+otherwise it will appear as a number::
+
+ date_format = workbook.add_format({'num_format': 'd mmmm yyyy'})
+
+ worksheet.write_datetime('A1', date_time, date_format)
+
+If required, a default date format string can be set using the :func:`Workbook`
+constructor ``default_date_format`` option.
+
+See :ref:`working_with_dates_and_time` for more details and also
+:ref:`Timezone Handling in XlsxWriter <timezone_handling>`.
+
+
+worksheet.write_url()
+---------------------
+
+.. py:function:: write_url(row, col, url[, cell_format[, string[, tip]]])
+
+ Write a hyperlink to a worksheet cell.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param url: Hyperlink url.
+ :param cell_format: Optional Format object. Defaults to blue underline.
+ :param string: An optional display string for the hyperlink.
+ :param tip: An optional tooltip.
+ :type row: int
+ :type col: int
+ :type url: string
+ :type string: string
+ :type tip: string
+ :type cell_format: :ref:`Format <format>`
+
+The ``write_url()`` method is used to write a hyperlink in a worksheet cell.
+The url is comprised of two elements: the displayed string and the
+non-displayed link. The displayed string is the same as the link unless an
+alternative string is specified.
+
+Both row-column and A1 style notation are supported. See :ref:`cell_notation`
+for more details.
+
+The ``cell_format`` parameter is used to apply formatting to the cell. This
+parameter is optional. Since a hyperlink without a format doesn't look like a
+link the following default :ref:`Format <format>` is used::
+
+ workbook.add_format({'color': 'blue', 'underline': 1})
+
+Therefore the following are equivalent::
+
+ link_format = workbook.add_format({'color': 'blue', 'underline': 1})
+ worksheet.write_url('A1', 'ftp://www.python.org/', link_format)
+
+ # Same as:
+ worksheet.write_url('A1', 'ftp://www.python.org/') # Default format.
+
+Four web style URI's are supported: ``http://``, ``https://``, ``ftp://`` and
+``mailto:``::
+
+ worksheet.write_url('A1', 'ftp://www.python.org/')
+ worksheet.write_url('A2', 'http://www.python.org/')
+ worksheet.write_url('A3', 'https://www.python.org/')
+ worksheet.write_url('A4', 'mailto:jmcnamara at cpan.org')
+
+All of the these URI types are recognized by the :func:`write()` method, so the
+following are equivalent::
+
+ worksheet.write_url('A2', 'http://www.python.org/')
+ worksheet.write ('A2', 'http://www.python.org/') # Same.
+
+You can display an alternative string using the ``string`` parameter::
+
+ worksheet.write_url('A1', 'http://www.python.org', link_format, 'Python')
+
+.. Note::
+
+ If you wish to have some other cell data such as a number or a formula you
+ can overwrite the cell using another call to ``write_*()``::
+
+ worksheet.write_url('A1', 'http://www.python.org/', link_format)
+
+ # Overwrite the URL string with a formula. The cell is still a link.
+ worksheet.write_formula('A1', '=1+1', link_format)
+
+There are two local URIs supported: ``internal:`` and ``external:``. These are
+used for hyperlinks to internal worksheet references or external workbook and
+worksheet references::
+
+ worksheet.write_url('A1', 'internal:Sheet2!A1')
+ worksheet.write_url('A2', 'internal:Sheet2!A1')
+ worksheet.write_url('A3', 'internal:Sheet2!A1:B2')
+ worksheet.write_url('A4', "internal:'Sales Data'!A1")
+ worksheet.write_url('A5', r'external:c:\temp\foo.xlsx')
+ worksheet.write_url('A6', r'external:c:\foo.xlsx#Sheet2!A1')
+ worksheet.write_url('A7', r'external:..\foo.xlsx')
+ worksheet.write_url('A8', r'external:..\foo.xlsx#Sheet2!A1')
+ worksheet.write_url('A9', r'external:\\NET\share\foo.xlsx')
+
+Worksheet references are typically of the form ``Sheet1!A1``. You can also link
+to a worksheet range using the standard Excel notation: ``Sheet1!A1:B2``.
+
+In external links the workbook and worksheet name must be separated by the
+``#`` character: ``external:Workbook.xlsx#Sheet1!A1'``.
+
+You can also link to a named range in the target worksheet. For example say you
+have a named range called ``my_name`` in the workbook ``c:\temp\foo.xlsx`` you
+could link to it as follows::
+
+ worksheet.write_url('A14', r'external:c:\temp\foo.xlsx#my_name')
+
+Excel requires that worksheet names containing spaces or non alphanumeric
+characters are single quoted as follows ``'Sales Data'!A1``.
+
+Links to network files are also supported. Network files normally begin with
+two back slashes as follows ``\\NETWORK\etc``. In order to generate this in a
+single or double quoted string you will have to escape the backslashes,
+``'\\\\NETWORK\\etc'`` or use a raw string ``r'\\NETWORK\etc'``.
+
+Alternatively, you can avoid most of these quoting problems by using forward
+slashes. These are translated internally to backslashes::
+
+ worksheet.write_url('A14', "external:c:/temp/foo.xlsx")
+ worksheet.write_url('A15', 'external://NETWORK/share/foo.xlsx')
+
+See also :ref:`ex_hyperlink`.
+
+.. note::
+ XlsxWriter will escape the following characters in URLs as required
+ by Excel: ``\s " < > \ [ ] ` ^ { }`` unless the URL already contains ``%xx``
+ style escapes. In which case it is assumed that the URL was escaped
+ correctly by the user and will by passed directly to Excel.
+
+.. note::
+ Excel limits hyperlink links and anchor/locations to 255 characters each.
+
+
+worksheet.write_rich_string()
+-----------------------------
+
+.. py:function:: write_rich_string(row, col, *string_parts[, cell_format])
+
+ Write a "rich" string with multiple formats to a worksheet cell.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param string_parts: String and format pairs.
+ :param cell_format: Optional Format object.
+ :type row: int
+ :type col: int
+ :type string_parts: list
+ :type cell_format: :ref:`Format <format>`
+
+
+The ``write_rich_string()`` method is used to write strings with multiple
+formats. For example to write the string "This is **bold** and this is
+*italic*" you would use the following::
+
+ bold = workbook.add_format({'bold': True})
+ italic = workbook.add_format({'italic': True})
+
+ worksheet.write_rich_string('A1',
+ 'This is ',
+ bold, 'bold',
+ ' and this is ',
+ italic, 'italic')
+
+.. image:: _images/rich_strings_small.png
+
+The basic rule is to break the string into fragments and put a
+:func:`Format <format>` object before the fragment that you want to format.
+For example::
+
+ # Unformatted string.
+ 'This is an example string'
+
+ # Break it into fragments.
+ 'This is an ', 'example', ' string'
+
+ # Add formatting before the fragments you want formatted.
+ 'This is an ', format, 'example', ' string'
+
+ # In XlsxWriter.
+ worksheet.write_rich_string('A1',
+ 'This is an ', format, 'example', ' string')
+
+String fragments that don't have a format are given a default format. So for
+example when writing the string "Some **bold** text" you would use the first
+example below but it would be equivalent to the second::
+
+ # Some bold format and a default format.
+ bold = workbook.add_format({'bold': True})
+ default = workbook.add_format()
+
+ # With default formatting:
+ worksheet.write_rich_string('A1',
+ 'Some ',
+ bold, 'bold',
+ ' text')
+
+ # Or more explicitly:
+ worksheet.write_rich_string('A1',
+ default, 'Some ',
+ bold, 'bold',
+ default, ' text')
+
+In Excel only the font properties of the format such as font name, style, size,
+underline, color and effects are applied to the string fragments in a rich
+string. Other features such as border, background, text wrap and alignment
+must be applied to the cell.
+
+The ``write_rich_string()`` method allows you to do this by using the last
+argument as a cell format (if it is a format object). The following example
+centers a rich string in the cell::
+
+ bold = workbook.add_format({'bold': True})
+ center = workbook.add_format({'align': 'center'})
+
+ worksheet.write_rich_string('A5',
+ 'Some ',
+ bold, 'bold text',
+ ' centered',
+ center)
+
+
+See also :ref:`ex_rich_strings` and :ref:`ex_merge_rich`.
+
+worksheet.write_row()
+---------------------
+
+.. py:function:: write_row(row, col, data[, cell_format])
+
+ Write a row of data starting from (row, col).
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param data: Cell data to write. Variable types.
+ :param cell_format: Optional Format object.
+ :type row: int
+ :type col: int
+ :type cell_format: :ref:`Format <format>`
+
+The ``write_row()`` method can be used to write a list of data in one go. This
+is useful for converting the results of a database query into an Excel
+worksheet. The :func:`write()` method is called for each element of the data.
+For example::
+
+ # Some sample data.
+ data = ('Foo', 'Bar', 'Baz')
+
+ # Write the data to a sequence of cells.
+ worksheet.write_row('A1', data)
+
+ # The above example is equivalent to:
+ worksheet.write('A1', data[0])
+ worksheet.write('B1', data[1])
+ worksheet.write('C1', data[2])
+
+
+worksheet.write_column()
+------------------------
+
+.. py:function:: write_column(row, col, data[, cell_format])
+
+ Write a column of data starting from (row, col).
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param data: Cell data to write. Variable types.
+ :param cell_format: Optional Format object.
+ :type row: int
+ :type col: int
+ :type cell_format: :ref:`Format <format>`
+
+The ``write_column()`` method can be used to write a list of data in one go.
+This is useful for converting the results of a database query into an Excel
+worksheet. The :func:`write()` method is called for each element of the data.
+For example::
+
+ # Some sample data.
+ data = ('Foo', 'Bar', 'Baz')
+
+ # Write the data to a sequence of cells.
+ worksheet.write_column('A1', data)
+
+ # The above example is equivalent to:
+ worksheet.write('A1', data[0])
+ worksheet.write('A2', data[1])
+ worksheet.write('A3', data[2])
+
+
+worksheet.set_row()
+-------------------
+
+.. py:function:: set_row(row, height, cell_format, options)
+
+ Set properties for a row of cells.
+
+ :param int row: The worksheet row (zero indexed).
+ :param float height: The row height.
+ :param cell_format: Optional Format object.
+ :type cell_format: :ref:`Format <format>`
+ :param dict options: Optional row parameters: hidden, level, collapsed.
+
+The ``set_row()`` method is used to change the default properties of a row. The
+most common use for this method is to change the height of a row::
+
+ worksheet.set_row(0, 20) # Set the height of Row 1 to 20.
+
+The other common use for ``set_row()`` is to set the :ref:`Format <format>` for
+all cells in the row::
+
+ cell_format = workbook.add_format({'bold': True})
+
+ worksheet.set_row(0, 20, cell_format)
+
+If you wish to set the format of a row without changing the height you can pass
+``None`` as the height parameter or use the default row height of 15::
+
+ worksheet.set_row(1, None, cell_format)
+ worksheet.set_row(1, 15, cell_format) # Same as above.
+
+The ``cell_format`` parameter will be applied to any cells in the row that
+don't have a format. As with Excel it is overridden by an explicit cell
+format. For example::
+
+ worksheet.set_row(0, None, format1) # Row 1 has format1.
+
+ worksheet.write('A1', 'Hello') # Cell A1 defaults to format1.
+ worksheet.write('B1', 'Hello', format2) # Cell B1 keeps format2.
+
+The ``options`` parameter is a dictionary with the following possible keys:
+
+* ``'hidden'``
+* ``'level'``
+* ``'collapsed'``
+
+Options can be set as follows::
+
+ worksheet.set_row(0, 20, cell_format, {'hidden': True})
+
+ # Or use defaults for other properties and set the options only.
+ worksheet.set_row(0, None, None, {'hidden': True})
+
+The ``'hidden'`` option is used to hide a row. This can be used, for example,
+to hide intermediary steps in a complicated calculation::
+
+ worksheet.set_row(0, 20, cell_format, {'hidden': True})
+
+The ``'level'`` parameter is used to set the outline level of the row. Outlines
+are described in :ref:`outlines`. Adjacent rows with the same outline level
+are grouped together into a single outline.
+
+The following example sets an outline level of 1 for some rows::
+
+ worksheet.set_row(0, None, None, {'level': 1})
+ worksheet.set_row(1, None, None, {'level': 1})
+ worksheet.set_row(2, None, None, {'level': 1})
+
+Excel allows up to 7 outline levels. The ``'level'`` parameter should be in the
+range ``0 <= level <= 7``.
+
+The ``'hidden'`` parameter can also be used to hide collapsed outlined rows
+when used in conjunction with the ``'level'`` parameter::
+
+ worksheet.set_row(1, None, None, {'hidden': 1, 'level': 1})
+ worksheet.set_row(2, None, None, {'hidden': 1, 'level': 1})
+
+The ``'collapsed'`` parameter is used in collapsed outlines to indicate which
+row has the collapsed ``'+'`` symbol::
+
+ worksheet.set_row(3, None, None, {'collapsed': 1})
+
+
+worksheet.set_column()
+----------------------
+
+.. py:function:: set_column(first_col, last_col, width, cell_format, options)
+
+ Set properties for one or more columns of cells.
+
+ :param int first_col: First column (zero-indexed).
+ :param int last_col: Last column (zero-indexed). Can be same as firstcol.
+ :param float width: The width of the column(s).
+ :param cell_format: Optional Format object.
+ :type cell_format: :ref:`Format <format>`
+ :param dict options: Optional parameters: hidden, level, collapsed.
+
+The ``set_column()`` method can be used to change the default properties of a
+single column or a range of columns::
+
+ worksheet.set_column(1, 3, 30) # Width of columns B:D set to 30.
+
+If ``set_column()`` is applied to a single column the value of ``first_col``
+and ``last_col`` should be the same::
+
+ worksheet.set_column(1, 1, 30) # Width of column B set to 30.
+
+It is also possible, and generally clearer, to specify a column range using the
+form of A1 notation used for columns. See :ref:`cell_notation` for more
+details.
+
+Examples::
+
+ worksheet.set_column(0, 0, 20) # Column A width set to 20.
+ worksheet.set_column(1, 3, 30) # Columns B-D width set to 30.
+ worksheet.set_column('E:E', 20) # Column E width set to 20.
+ worksheet.set_column('F:H', 30) # Columns F-H width set to 30.
+
+The ``width`` parameter sets the column width in the same units used by Excel
+which is: the number of characters in the default font. The default width is
+8.43 in the default font of Calibri 11. The actual relationship between a
+string width and a column width in Excel is complex. See the `following
+explanation of column widths <https://support.microsoft.com/en-us/kb/214123>`_
+from the Microsoft support documentation for more details.
+
+There is no way to specify "AutoFit" for a column in the Excel file
+format. This feature is only available at runtime from within Excel. It is
+possible to simulate "AutoFit" in your application by tracking the maximum
+width of the data in the column as your write it and then adjusting the column
+width at the end.
+
+As usual the ``cell_format`` :ref:`Format <format>` parameter is optional. If
+you wish to set the format without changing the width you can pass ``None`` as
+the width parameter::
+
+ cell_format = workbook.add_format({'bold': True})
+
+ worksheet.set_column(0, 0, None, cell_format)
+
+The ``cell_format`` parameter will be applied to any cells in the column that
+don't have a format. For example::
+
+ worksheet.set_column('A:A', None, format1) # Col 1 has format1.
+
+ worksheet.write('A1', 'Hello') # Cell A1 defaults to format1.
+ worksheet.write('A2', 'Hello', format2) # Cell A2 keeps format2.
+
+A row format takes precedence over a default column format::
+
+ worksheet.set_row(0, None, format1) # Set format for row 1.
+ worksheet.set_column('A:A', None, format2) # Set format for col 1.
+
+ worksheet.write('A1', 'Hello') # Defaults to format1
+ worksheet.write('A2', 'Hello') # Defaults to format2
+
+The ``options`` parameter is a dictionary with the following possible keys:
+
+* ``'hidden'``
+* ``'level'``
+* ``'collapsed'``
+
+Options can be set as follows::
+
+ worksheet.set_column('D:D', 20, cell_format, {'hidden': 1})
+
+ # Or use defaults for other properties and set the options only.
+ worksheet.set_column('E:E', None, None, {'hidden': 1})
+
+The ``'hidden'`` option is used to hide a column. This can be used, for
+example, to hide intermediary steps in a complicated calculation::
+
+ worksheet.set_column('D:D', 20, cell_format, {'hidden': 1})
+
+The ``'level'`` parameter is used to set the outline level of the column.
+Outlines are described in :ref:`outlines`. Adjacent columns with the same
+outline level are grouped together into a single outline.
+
+The following example sets an outline level of 1 for columns B to G::
+
+ worksheet.set_column('B:G', None, None, {'level': 1})
+
+Excel allows up to 7 outline levels. The ``'level'`` parameter should be in the
+range ``0 <= level <= 7``.
+
+The ``'hidden'`` parameter can also be used to hide collapsed outlined columns
+when used in conjunction with the ``'level'`` parameter::
+
+ worksheet.set_column('B:G', None, None, {'hidden': 1, 'level': 1})
+
+The ``'collapsed'`` parameter is used in collapsed outlines to indicate which
+column has the collapsed ``'+'`` symbol::
+
+ worksheet.set_column('H:H', None, None, {'collapsed': 1})
+
+worksheet.insert_image()
+------------------------
+
+.. py:function:: insert_image(row, col, image[, options])
+
+ Insert an image in a worksheet cell.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param image: Image filename (with path if required).
+ :param options: Optional parameters for image position, scale and url.
+ :type row: int
+ :type col: int
+ :type image: string
+ :type options: dict
+
+This method can be used to insert a image into a worksheet. The image can be in
+PNG, JPEG or BMP format::
+
+ worksheet.insert_image('B2', 'python.png')
+
+.. image:: _images/insert_image.png
+
+A file path can be specified with the image name::
+
+ worksheet1.insert_image('B10', '../images/python.png')
+ worksheet2.insert_image('B20', r'c:\images\python.png')
+
+The ``insert_image()`` method takes optional parameters in a dictionary to
+position and scale the image. The available parameters with their default
+values are::
+
+ {
+ 'x_offset': 0,
+ 'y_offset': 0,
+ 'x_scale': 1,
+ 'y_scale': 1,
+ 'url': None,
+ 'tip': None,
+ 'image_data': None,
+ 'positioning': None,
+ }
+
+The offset values are in pixels::
+
+ worksheet1.insert_image('B2', 'python.png', {'x_offset': 15, 'y_offset': 10})
+
+The offsets can be greater than the width or height of the underlying cell.
+This can be occasionally useful if you wish to align two or more images
+relative to the same cell.
+
+The ``x_scale`` and ``y_scale`` parameters can be used to scale the image
+horizontally and vertically::
+
+ worksheet.insert_image('B3', 'python.png', {'x_scale': 0.5, 'y_scale': 0.5})
+
+The ``url`` parameter can used to add a hyperlink/url to the image. The ``tip``
+parameter gives an option mouseover tooltip for images with hyperlinks::
+
+ worksheet.insert_image('B4', 'python.png', {'url': 'http://python.org'})
+
+See also :func:`write_url` for details on supported URIs.
+
+The ``image_data`` parameter is used to add an in-memory byte stream in
+:class:`io.BytesIO` format::
+
+ worksheet.insert_image('B5', 'python.png', {'image_data': image_data})
+
+This is generally used for inserting images from URLs::
+
+ url = 'http://python.org/logo.png'
+ image_data = io.BytesIO(urllib2.urlopen(url).read())
+
+ worksheet.insert_image('B5', url, {'image_data': image_data})
+
+When using the ``image_data`` parameter a filename must still be passed to
+``insert_image()`` since it is required by Excel. In the previous example
+the filename is extracted from the URL string. See also
+:ref:`ex_images_bytesio`.
+
+The ``positioning`` parameter can be used to control the object positioning
+of the image::
+
+ worksheet.insert_image('B3', 'python.png', {'positioning': 1})
+
+Where ``positioning`` has the following allowable values:
+
+1. Move and size with cells.
+2. Move but don't size with cells (the default).
+3. Don't move or size with cells.
+
+
+.. Note::
+ The scaling of a image may be affected if is crosses a row that has its
+ default height changed due to a font that is larger than the default font
+ size or that has text wrapping turned on. To avoid this you should
+ explicitly set the height of the row using ``set_row()`` if it crosses an
+ inserted image.
+
+BMP images are only supported for backward compatibility. In general it is best
+to avoid BMP images since they aren't compressed. If used, BMP images must be
+24 bit, true color, bitmaps.
+
+See also :ref:`ex_insert_image`.
+
+worksheet.insert_chart()
+------------------------
+
+.. py:function:: insert_chart(row, col, chart[, options])
+
+ Write a string to a worksheet cell.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param chart: A chart object.
+ :param options: Optional parameters to position and scale the chart.
+ :type row: int
+ :type col: int
+ :type options: dict
+
+This method can be used to insert a chart into a worksheet. A chart object is
+created via the Workbook :func:`add_chart()` method where the chart type is
+specified::
+
+ chart = workbook.add_chart({type, 'column'})
+
+It is then inserted into a worksheet as an embedded chart::
+
+ worksheet.insert_chart('B5', chart)
+
+.. image:: _images/chart_simple.png
+ :scale: 75 %
+
+
+.. Note::
+
+ A chart can only be inserted into a worksheet once. If several similar
+ charts are required then each one must be created separately with
+ :func:`add_chart()`.
+
+See :ref:`chart_class`, :ref:`working_with_charts` and :ref:`chart_examples`.
+
+The ``insert_chart()`` method takes optional parameters in a dictionary to
+position and scale the chart. The available parameters with their default
+values are::
+
+ {
+ 'x_offset': 0,
+ 'y_offset': 0,
+ 'x_scale': 1,
+ 'y_scale': 1,
+ }
+
+The offset values are in pixels::
+
+ worksheet.insert_chart('B5', chart, {'x_offset': 25, 'y_offset': 10})
+
+The ``x_scale`` and ``y_scale`` parameters can be used to scale the chart
+horizontally and vertically::
+
+ worksheet.insert_chart('B5', chart, {'x_scale': 0.5, 'y_scale': 0.5})
+
+These properties can also be set via the Chart :func:`set_size` method.
+
+.. Note::
+ The scaling of a chart may be affected if is crosses a row that has its
+ default height changed due to a font that is larger than the default font
+ size or that has text wrapping turned on. To avoid this you should
+ explicitly set the height of the row using ``set_row()`` if it crosses an
+ inserted chart.
+
+
+worksheet.insert_textbox()
+--------------------------
+
+.. py:function:: insert_textbox(row, col, textbox[, options])
+
+ Write a string to a worksheet cell.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param text: The text in the textbox.
+ :param options: Optional parameters to position and scale the textbox.
+ :type row: int
+ :type col: int
+ :type text: string
+ :type options: dict
+
+This method can be used to insert a textbox into a worksheet::
+
+ worksheet.insert_textbox('B2', 'A simple textbox with some text')
+
+.. image:: _images/textbox03.png
+
+
+The size and formatting of the textbox can be controlled via the ``options`` dict::
+
+ # Size and position
+ width
+ height
+ x_scale
+ y_scale
+ x_offset
+ y_offset
+
+ # Formatting
+ line
+ border
+ fill
+ gradient
+ font
+ align
+
+These options are explained in more detail in the
+:ref:`working_with_textboxes` section.
+
+See also :ref:`ex_textbox`.
+
+
+.. Note::
+ The scaling of a textbox may be affected if is crosses a row that has its
+ default height changed due to a font that is larger than the default font
+ size or that has text wrapping turned on. To avoid this you should
+ explicitly set the height of the row using ``set_row()`` if it crosses an
+ inserted chart.
+
+
+worksheet.insert_button()
+-------------------------
+
+.. py:function:: insert_button(row, col[, options])
+
+ Insert a VBA button control on a worksheet.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param options: Optional parameters to position and scale the button.
+ :type row: int
+ :type col: int
+ :type options: dict
+
+The ``insert_button(``) method can be used to insert an Excel form button into a worksheet.
+
+This method is generally only useful when used in conjunction with the
+Workbook :func:`add_vba_project` method to tie the button to a macro from an
+embedded VBA project::
+
+ # Add the VBA project binary.
+ workbook.add_vba_project('./vbaProject.bin')
+
+ # Add a button tied to a macro in the VBA project.
+ worksheet.insert_button('B3',{'macro': 'say_hello',
+ 'caption': 'Press Me'})
+
+.. image:: _images/macros.png
+
+See :ref:`macros` and :ref:`ex_macros` for more details.
+
+The ``insert_button()`` method takes optional parameters in a dictionary to
+position and scale the chart. The available parameters with their default
+values are::
+
+ {
+ 'macro': None,
+ 'caption': 'Button 1',
+ 'width': 64,
+ 'height': 20.
+ 'x_offset': 0,
+ 'y_offset': 0,
+ 'x_scale': 1,
+ 'y_scale': 1,
+ }
+
+The ``macro`` option is used to set the macro that the button will invoke when
+the user clicks on it. The macro should be included using the Workbook
+``add_vba_project()`` method shown above.
+
+The ``caption`` is used to set the caption on the button. The default is
+``Button n`` where ``n`` is the button number.
+
+The default button ``width`` is 64 pixels which is the width of a default cell
+and the default button ``height`` is 20 pixels which is the height of a
+default cell.
+
+The offset and scale options are the same as for ``insert_chart()``, see above.
+
+
+worksheet.data_validation()
+---------------------------
+
+.. py:function:: data_validation(first_row, first_col, last_row, \
+ last_col, options)
+
+ Write a conditional format to range of cells.
+
+ :param first_row: The first row of the range. (All zero indexed.)
+ :param first_col: The first column of the range.
+ :param last_row: The last row of the range.
+ :param last_col: The last col of the range.
+ :param options: Data validation options.
+ :type first_row: int
+ :type first_col: int
+ :type last_row: int
+ :type last_col: int
+ :type options: dict
+
+
+The ``data_validation()`` method is used to construct an Excel data validation
+or to limit the user input to a dropdown list of values::
+
+ worksheet.data_validation('B3', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10})
+
+
+ worksheet.data_validation('B13', {'validate': 'list',
+ 'source': ['open', 'high', 'close']})
+
+.. image:: _images/data_validate1.png
+
+The data validation can be applied to a single cell or a range of cells. As
+usual you can use A1 or Row/Column notation, see :ref:`cell_notation`.
+
+With Row/Column notation you must specify all four cells in the range:
+``(first_row, first_col, last_row, last_col)``. If you need to refer to a
+single cell set the `last_` values equal to the `first_` values. With A1
+notation you can refer to a single cell or a range of cells::
+
+ worksheet.data_validation(0, 0, 4, 1, {...})
+ worksheet.data_validation('B1', {...})
+ worksheet.data_validation('C1:E5', {...})
+
+
+The options parameter in ``data_validation()`` must be a dictionary containing
+the parameters that describe the type and style of the data validation. There
+are a lot of available options which are described in detail in a separate
+section: :ref:`working_with_data_validation`. See also :ref:`ex_data_valid`.
+
+
+
+worksheet.conditional_format()
+------------------------------
+
+.. py:function:: conditional_format(first_row, first_col, last_row, \
+ last_col, options)
+
+ Write a conditional format to range of cells.
+
+ :param first_row: The first row of the range. (All zero indexed.)
+ :param first_col: The first column of the range.
+ :param last_row: The last row of the range.
+ :param last_col: The last col of the range.
+ :param options: Conditional formatting options.
+ :type first_row: int
+ :type first_col: int
+ :type last_row: int
+ :type last_col: int
+ :type options: dict
+
+The ``conditional_format()`` method is used to add formatting to a cell or
+range of cells based on user defined criteria::
+
+ worksheet.conditional_format('B3:K12', {'type': 'cell',
+ 'criteria': '>=',
+ 'value': 50,
+ 'format': format1})
+
+.. image:: _images/conditional_format1.png
+
+The conditional format can be applied to a single cell or a range of cells. As
+usual you can use A1 or Row/Column notation, see :ref:`cell_notation`.
+
+With Row/Column notation you must specify all four cells in the range:
+``(first_row, first_col, last_row, last_col)``. If you need to refer to a
+single cell set the `last_` values equal to the `first_` values. With A1
+notation you can refer to a single cell or a range of cells::
+
+ worksheet.conditional_format(0, 0, 4, 1, {...})
+ worksheet.conditional_format('B1', {...})
+ worksheet.conditional_format('C1:E5', {...})
+
+
+The options parameter in ``conditional_format()`` must be a dictionary
+containing the parameters that describe the type and style of the conditional
+format. There are a lot of available options which are described in detail in
+a separate section: :ref:`working_with_conditional_formats`. See also
+:ref:`ex_cond_format`.
+
+
+worksheet.add_table()
+---------------------
+
+.. py:function:: add_table(first_row, first_col, last_row, last_col, options)
+
+ Add an Excel table to a worksheet.
+
+ :param first_row: The first row of the range. (All zero indexed.)
+ :param first_col: The first column of the range.
+ :param last_row: The last row of the range.
+ :param last_col: The last col of the range.
+ :param options: Table formatting options. (Optional)
+ :type first_row: int
+ :type first_col: int
+ :type last_row: int
+ :type last_col: int
+ :type options: dict
+
+
+
+The ``add_table()`` method is used to group a range of cells into an Excel
+Table::
+
+ worksheet.add_table('B3:F7', { ... })
+
+This method contains a lot of parameters and is described in :ref:`tables`.
+
+See also :ref:`ex_tables`.
+
+.. Note::
+
+ Tables aren't available in XlsxWriter when :func:`Workbook`
+ ``'constant_memory'`` mode is enabled.
+
+
+worksheet.add_sparkline()
+-------------------------
+
+.. py:function:: add_sparkline(row, col, options)
+
+ Add sparklines to a worksheet.
+
+ :param int row: The cell row (zero indexed).
+ :param int col: The cell column (zero indexed).
+ :param dict options: Sparkline formatting options.
+
+
+Sparklines are small charts that fit in a single cell and are used to show
+trends in data.
+
+.. image:: _images/sparklines1.png
+
+The ``add_sparkline()`` worksheet method is used to add sparklines to a cell or
+a range of cells::
+
+ worksheet.add_sparkline('F1', {'range': 'A1:E1'})
+
+This method contains a lot of parameters and is described in detail in
+:ref:`sparklines`.
+
+
+
+See also :ref:`ex_sparklines1` and :ref:`ex_sparklines2`.
+
+.. Note::
+ Sparklines are a feature of Excel 2010+ only. You can write them to
+ an XLSX file that can be read by Excel 2007 but they won't be displayed.
+
+
+worksheet.write_comment()
+-------------------------
+
+.. py:function:: write_comment(row, col, comment[, options])
+
+ Write a comment to a worksheet cell.
+
+ :param row: The cell row (zero indexed).
+ :param col: The cell column (zero indexed).
+ :param comment: String to write to cell.
+ :param options: Comment formatting options.
+ :type row: int
+ :type col: int
+ :type comment: string
+ :type options: dict
+
+The ``write_comment()`` method is used to add a comment to a cell. A comment is
+indicated in Excel by a small red triangle in the upper right-hand corner of
+the cell. Moving the cursor over the red triangle will reveal the comment.
+
+The following example shows how to add a comment to a cell::
+
+ worksheet.write('A1', 'Hello')
+ worksheet.write_comment('A1', 'This is a comment')
+
+.. image:: _images/comments1.png
+
+As usual you can replace the ``row`` and ``col`` parameters with an ``A1`` cell
+reference. See :ref:`cell_notation` for more details.
+
+The properties of the cell comment can be modified by passing an optional
+dictionary of key/value pairs to control the format of the comment. For
+example::
+
+ worksheet.write_comment('C3', 'Hello', {'x_scale': 1.2, 'y_scale': 0.8})
+
+Most of these options are quite specific and in general the default comment
+behavior will be all that you need. However, should you need greater control
+over the format of the cell comment the following options are available::
+
+ author
+ visible
+ x_scale
+ width
+ y_scale
+ height
+ color
+ start_cell
+ start_row
+ start_col
+ x_offset
+ y_offset
+
+For more details see :ref:`cell_comments` and :ref:`ex_comments2` .
+
+worksheet.show_comments()
+-------------------------
+
+.. py:function:: show_comments()
+
+ Make any comments in the worksheet visible.
+
+This method is used to make all cell comments visible when a worksheet is
+opened::
+
+ worksheet.show_comments()
+
+Individual comments can be made visible using the ``visible`` parameter of the
+``write_comment`` method (see above)::
+
+ worksheet.write_comment('C3', 'Hello', {'visible': True})
+
+If all of the cell comments have been made visible you can hide individual
+comments as follows::
+
+ worksheet.show_comments()
+ worksheet.write_comment('C3', 'Hello', {'visible': False})
+
+For more details see :ref:`cell_comments` and :ref:`ex_comments2` .
+
+
+worksheet.set_comments_author()
+-------------------------------
+
+.. py:function:: set_comments_author(author)
+
+ Set the default author of the cell comments.
+
+ :param string author: Comment author.
+
+This method is used to set the default author of all cell comments::
+
+ worksheet.set_comments_author('John Smith')
+
+Individual comment authors can be set using the ``author`` parameter of the
+``write_comment`` method (see above).
+
+If no author is specified the default comment author name is an empty string.
+
+For more details see :ref:`cell_comments` and :ref:`ex_comments2` .
+
+
+worksheet.get_name()
+--------------------
+
+.. py:function:: get_name()
+
+ Retrieve the worksheet name.
+
+The ``get_name()`` method is used to retrieve the name of a worksheet. This is
+something useful for debugging or logging::
+
+ for worksheet in workbook.worksheets():
+ print worksheet.get_name()
+
+There is no ``set_name()`` method. The only safe way to set the worksheet name
+is via the ``add_worksheet()`` method.
+
+
+worksheet.activate()
+--------------------
+
+.. py:function:: activate()
+
+ Make a worksheet the active, i.e., visible worksheet.
+
+The ``activate()`` method is used to specify which worksheet is initially
+visible in a multi-sheet workbook::
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet()
+
+ worksheet3.activate()
+
+.. image:: _images/worksheet_activate.png
+
+More than one worksheet can be selected via the ``select()`` method, see below,
+however only one worksheet can be active.
+
+The default active worksheet is the first worksheet.
+
+
+worksheet.select()
+------------------
+
+.. py:function:: select()
+
+ Set a worksheet tab as selected.
+
+The ``select()`` method is used to indicate that a worksheet is selected in a
+multi-sheet workbook::
+
+ worksheet1.activate()
+ worksheet2.select()
+ worksheet3.select()
+
+A selected worksheet has its tab highlighted. Selecting worksheets is a way of
+grouping them together so that, for example, several worksheets could be
+printed in one go. A worksheet that has been activated via the ``activate()``
+method will also appear as selected.
+
+
+worksheet.hide()
+----------------
+
+.. py:function:: hide()
+
+ Hide the current worksheet.
+
+The ``hide()`` method is used to hide a worksheet::
+
+ worksheet2.hide()
+
+You may wish to hide a worksheet in order to avoid confusing a user with
+intermediate data or calculations.
+
+.. image:: _images/hide_sheet.png
+
+A hidden worksheet can not be activated or selected so this method is mutually
+exclusive with the :func:`activate()` and :func:`select()` methods. In
+addition, since the first worksheet will default to being the active
+worksheet, you cannot hide the first worksheet without activating another
+sheet::
+
+ worksheet2.activate()
+ worksheet1.hide()
+
+See :ref:`ex_hide_sheet` for more details.
+
+worksheet.set_first_sheet()
+---------------------------
+
+.. py:function:: set_first_sheet()
+
+ Set current worksheet as the first visible sheet tab.
+
+The :func:`activate()` method determines which worksheet is initially selected.
+However, if there are a large number of worksheets the selected worksheet may
+not appear on the screen. To avoid this you can select which is the leftmost
+visible worksheet tab using ``set_first_sheet()``::
+
+ for in range(1, 21):
+ workbook.add_worksheet
+
+ worksheet19.set_first_sheet() # First visible worksheet tab.
+ worksheet20.activate() # First visible worksheet.
+
+This method is not required very often. The default value is the first
+worksheet.
+
+
+worksheet.merge_range()
+-----------------------
+
+.. py:function:: merge_range(first_row, first_col, \
+ last_row, last_col, data[, cell_format])
+
+ Merge a range of cells.
+
+ :param first_row: The first row of the range. (All zero indexed.)
+ :param first_col: The first column of the range.
+ :param last_row: The last row of the range.
+ :param last_col: The last col of the range.
+ :param data: Cell data to write. Variable types.
+ :param cell_format: Optional Format object.
+ :type first_row: int
+ :type first_col: int
+ :type last_row: int
+ :type last_col: int
+ :type cell_format: :ref:`Format <format>`
+
+
+The ``merge_range()`` method allows cells to be merged together so that they
+act as a single area.
+
+Excel generally merges and centers cells at same time. To get similar behavior
+with XlsxWriter you need to apply a :ref:`Format <format>`::
+
+ merge_format = workbook.add_format({'align': 'center'})
+
+ worksheet.merge_range('B3:D4', 'Merged Cells', merge_format)
+
+It is possible to apply other formatting to the merged cells as well::
+
+ merge_format = workbook.add_format({
+ 'bold': True,
+ 'border': 6,
+ 'align': 'center',
+ 'valign': 'vcenter',
+ 'fg_color': '#D7E4BC',
+ })
+
+ worksheet.merge_range('B3:D4', 'Merged Cells', merge_format)
+
+.. image:: _images/merge_range.png
+
+See :ref:`ex_merge1` for more details.
+
+The ``merge_range()`` method writes its ``data`` argument using
+:func:`write()`. Therefore it will handle numbers, strings and formulas as
+usual. If this doesn't handle your data correctly then you can overwrite the
+first cell with a call to one of the other
+``write_*()`` methods using the same :ref:`Format
+<format>` as in the merged cells. See :ref:`ex_merge_rich`.
+
+.. image:: _images/merge_rich.png
+
+.. Note::
+
+ Merged ranges generally don't work in XlsxWriter when :func:`Workbook`
+ ``'constant_memory'`` mode is enabled.
+
+
+worksheet.autofilter()
+----------------------
+
+.. py:function:: autofilter(first_row, first_col, last_row, last_col)
+
+ Set the autofilter area in the worksheet.
+
+ :param first_row: The first row of the range. (All zero indexed.)
+ :param first_col: The first column of the range.
+ :param last_row: The last row of the range.
+ :param last_col: The last col of the range.
+ :type first_row: int
+ :type first_col: int
+ :type last_row: int
+ :type last_col: int
+
+The ``autofilter()`` method allows an autofilter to be added to a worksheet. An
+autofilter is a way of adding drop down lists to the headers of a 2D range of
+worksheet data. This allows users to filter the data based on simple criteria
+so that some data is shown and some is hidden.
+
+.. image:: _images/autofilter3.png
+
+To add an autofilter to a worksheet::
+
+ worksheet.autofilter('A1:D11')
+ worksheet.autofilter(0, 0, 10, 3) # Same as above.
+
+Filter conditions can be applied using the :func:`filter_column()` or
+:func:`filter_column_list()` methods.
+
+See :ref:`working_with_autofilters` for more details.
+
+
+worksheet.filter_column()
+-------------------------
+
+.. py:function:: filter_column(col, criteria)
+
+ Set the column filter criteria.
+
+ :param int col: Filter column (zero-indexed).
+ :param string criteria: Filter criteria.
+
+
+The ``filter_column`` method can be used to filter columns in a autofilter
+range based on simple conditions.
+
+
+The conditions for the filter are specified using simple expressions::
+
+ worksheet.filter_column('A', 'x > 2000')
+ worksheet.filter_column('B', 'x > 2000 and x < 5000')
+
+The ``col`` parameter can either be a zero indexed column number or a string
+column name.
+
+It isn't sufficient to just specify the filter condition. You must also hide
+any rows that don't match the filter condition. See
+:ref:`working_with_autofilters` for more details.
+
+
+worksheet.filter_column_list()
+------------------------------
+
+.. py:function:: filter_column_list(col, filters)
+
+ Set the column filter criteria in Excel 2007 list style.
+
+ :param int col: Filter column (zero-indexed).
+ :param list filters: List of filter criteria to match.
+
+The ``filter_column_list()`` method can be used to represent filters with
+multiple selected criteria::
+
+ worksheet.filter_column_list('A', ['March', 'April', 'May'])
+
+The ``col`` parameter can either be a zero indexed column number or a string
+column name.
+
+One or more criteria can be selected::
+
+ worksheet.filter_column_list('A', ['March'])
+ worksheet.filter_column_list('C', [100, 110, 120, 130])
+
+It isn't sufficient to just specify filters. You must also hide any rows that
+don't match the filter condition. See :ref:`working_with_autofilters` for more
+details.
+
+
+worksheet.set_selection()
+-------------------------
+.. py:function:: set_selection(first_row, first_col, last_row, last_col)
+
+ Set the selected cell or cells in a worksheet.
+
+ :param first_row: The first row of the range. (All zero indexed.)
+ :param first_col: The first column of the range.
+ :param last_row: The last row of the range.
+ :param last_col: The last col of the range.
+ :type first_row: int
+ :type first_col: int
+ :type last_row: int
+ :type last_col: int
+
+
+The ``set_selection()`` method can be used to specify which cell or range of
+cells is selected in a worksheet. The most common requirement is to select a
+single cell, in which case the ``first_`` and ``last_`` parameters should be
+the same.
+
+The active cell within a selected range is determined by the order in which
+``first_`` and ``last_`` are specified.
+
+Examples::
+
+ worksheet1.set_selection(3, 3, 3, 3) # 1. Cell D4.
+ worksheet2.set_selection(3, 3, 6, 6) # 2. Cells D4 to G7.
+ worksheet3.set_selection(6, 6, 3, 3) # 3. Cells G7 to D4.
+ worksheet4.set_selection('D4') # Same as 1.
+ worksheet5.set_selection('D4:G7') # Same as 2.
+ worksheet6.set_selection('G7:D4') # Same as 3.
+
+As shown above, both row-column and A1 style notation are supported. See
+:ref:`cell_notation` for more details. The default cell selection is
+``(0, 0)``, ``'A1'``.
+
+
+
+worksheet.freeze_panes()
+------------------------
+
+.. py:function:: freeze_panes(row, col [, top_row, left_col])
+
+ Create worksheet panes and mark them as frozen.
+
+ :param int row: The cell row (zero indexed).
+ :param int col: The cell column (zero indexed).
+ :param int top_row: Topmost visible row in scrolling region of pane.
+ :param int left_col: Leftmost visible row in scrolling region of pane.
+
+This ``freeze_panes`` method can be used to divide a worksheet into horizontal
+or vertical regions known as panes and to "freeze" these panes so that the
+splitter bars are not visible.
+
+The parameters ``row`` and ``col`` are used to specify the location of the
+split. It should be noted that the split is specified at the top or left of a
+cell and that the method uses zero based indexing. Therefore to freeze the
+first row of a worksheet it is necessary to specify the split at row 2 (which
+is 1 as the zero-based index).
+
+You can set one of the ``row`` and ``col`` parameters as zero if you do not
+want either a vertical or horizontal split.
+
+Examples::
+
+ worksheet.freeze_panes(1, 0) # Freeze the first row.
+ worksheet.freeze_panes('A2') # Same using A1 notation.
+ worksheet.freeze_panes(0, 1) # Freeze the first column.
+ worksheet.freeze_panes('B1') # Same using A1 notation.
+ worksheet.freeze_panes(1, 2) # Freeze first row and first 2 columns.
+ worksheet.freeze_panes('C2') # Same using A1 notation.
+
+The parameters ``top_row`` and ``left_col`` are optional. They are used to
+specify the top-most or left-most visible row or column in the scrolling
+region of the panes. For example to freeze the first row and to have the
+scrolling region begin at row twenty::
+
+ worksheet.freeze_panes(1, 0, 20, 0)
+
+You cannot use A1 notation for the ``top_row`` and ``left_col`` parameters.
+
+See :ref:`ex_panes` for more details.
+
+
+worksheet.split_panes()
+-----------------------
+
+.. py:function:: split_panes(x, y [, top_row, left_col])
+
+ Create worksheet panes and mark them as split.
+
+ :param float x: The position for the vertical split.
+ :param float y: The position for the horizontal split.
+ :param int top_row: Topmost visible row in scrolling region of pane.
+ :param int left_col: Leftmost visible row in scrolling region of pane.
+
+The ``split_panes`` method can be used to divide a worksheet into horizontal
+or vertical regions known as panes. This method is different from the
+``freeze_panes()`` method in that the splits between the panes will be visible
+to the user and each pane will have its own scroll bars.
+
+The parameters ``y`` and ``x`` are used to specify the vertical and horizontal
+position of the split. The units for ``y`` and ``x`` are the same as those
+used by Excel to specify row height and column width. However, the vertical
+and horizontal units are different from each other. Therefore you must specify
+the ``y`` and ``x`` parameters in terms of the row heights and column widths
+that you have set or the default values which are ``15`` for a row and
+``8.43`` for a column.
+
+You can set one of the ``y`` and ``x`` parameters as zero if you do not want
+either a vertical or horizontal split. The parameters ``top_row`` and
+``left_col`` are optional. They are used to specify the top-most or left-most
+visible row or column in the bottom-right pane.
+
+Example::
+
+ worksheet.split_panes(15, 0) # First row.
+ worksheet.split_panes(0, 8.43) # First column.
+ worksheet.split_panes(15, 8.43) # First row and column.
+
+You cannot use A1 notation with this method.
+
+See :ref:`ex_panes` for more details.
+
+
+worksheet.set_zoom()
+--------------------
+
+.. py:function:: set_zoom(zoom)
+
+ Set the worksheet zoom factor.
+
+ :param int zoom: Worksheet zoom factor.
+
+Set the worksheet zoom factor in the range ``10 <= zoom <= 400``::
+
+ worksheet1.set_zoom(50)
+ worksheet2.set_zoom(75)
+ worksheet3.set_zoom(300)
+ worksheet4.set_zoom(400)
+
+The default zoom factor is 100. It isn't possible to set the zoom to
+"Selection" because it is calculated by Excel at run-time.
+
+Note, ``set_zoom()`` does not affect the scale of the printed page. For that
+you should use :func:`set_print_scale()`.
+
+
+worksheet.right_to_left()
+-------------------------
+
+.. py:function:: right_to_left()
+
+ Display the worksheet cells from right to left for some versions of Excel.
+
+The ``right_to_left()`` method is used to change the default direction of the
+worksheet from left-to-right, with the A1 cell in the top left, to
+right-to-left, with the A1 cell in the top right::
+
+ worksheet.right_to_left()
+
+This is useful when creating Arabic, Hebrew or other near or far eastern
+worksheets that use right-to-left as the default direction.
+
+
+worksheet.hide_zero()
+---------------------
+
+.. py:function:: hide_zero()
+
+ Hide zero values in worksheet cells.
+
+The ``hide_zero()`` method is used to hide any zero values that appear in
+cells::
+
+ worksheet.hide_zero()
+
+
+worksheet.set_tab_color()
+-------------------------
+
+.. py:function:: set_tab_color()
+
+ Set the color of the worksheet tab.
+
+ :param string color: The tab color.
+
+The ``set_tab_color()`` method is used to change the color of the worksheet
+tab::
+
+ worksheet1.set_tab_color('red')
+ worksheet2.set_tab_color('#FF9900') # Orange
+
+The color can be a Html style ``#RRGGBB`` string or a limited number named
+colors, see :ref:`colors`.
+
+See :ref:`ex_tab_colors` for more details.
+
+
+worksheet.protect()
+-------------------
+
+.. py:function:: protect()
+
+ Protect elements of a worksheet from modification.
+
+ :param string password: A worksheet password.
+ :param dict options: A dictionary of worksheet options to protect.
+
+
+The ``protect()`` method is used to protect a worksheet from modification::
+
+ worksheet.protect()
+
+The ``protect()`` method also has the effect of enabling a cell's ``locked``
+and ``hidden`` properties if they have been set. A *locked* cell cannot be
+edited and this property is on by default for all cells. A *hidden* cell will
+display the results of a formula but not the formula itself. These properties
+can be set using the :func:`set_locked` and :func:`set_hidden` format methods.
+
+You can optionally add a password to the worksheet protection::
+
+ worksheet.protect('abc123')
+
+Passing the empty string ``''`` is the same as turning on protection without a
+password.
+
+You can specify which worksheet elements you wish to protect by passing a
+dictionary in the ``options`` argument with any or all of the following keys::
+
+ # Default values shown.
+ options = {
+ 'objects': False,
+ 'scenarios': False,
+ 'format_cells': False,
+ 'format_columns': False,
+ 'format_rows': False,
+ 'insert_columns': False,
+ 'insert_rows': False,
+ 'insert_hyperlinks': False,
+ 'delete_columns': False,
+ 'delete_rows': False,
+ 'select_locked_cells': True,
+ 'sort': False,
+ 'autofilter': False,
+ 'pivot_tables': False,
+ 'select_unlocked_cells': True,
+ }
+
+The default boolean values are shown above. Individual elements can be
+protected as follows::
+
+ worksheet.protect('abc123', { 'insert_rows': 1 })
+
+See also the :func:`set_locked` and :func:`set_hidden` format methods and
+:ref:`ex_protection`.
+
+.. Note::
+ Worksheet level passwords in Excel offer very weak protection. They do not
+ encrypt your data and are very easy to deactivate. Full workbook encryption
+ is not supported by ``XlsxWriter`` since it requires a completely different
+ file format and would take several man months to implement.
+
+
+worksheet.set_default_row()
+---------------------------
+
+.. py:function:: set_default_row(height, hide_unused_rows)
+
+ Set the default row properties.
+
+ :param float height: Default height. Optional, defaults to 15.
+ :param bool hide_unused_rows: Hide unused rows. Optional, defaults to False.
+
+
+The ``set_default_row()`` method is used to set the limited number of default
+row properties allowed by Excel which are the default height and the option to
+hide unused rows. These parameters are an optimization used by Excel to set
+row properties without generating a very large file with an entry for each row.
+
+To set the default row height::
+
+ worksheet.set_default_row(24)
+
+To hide unused rows::
+
+ worksheet.set_default_row(hide_unused_rows=True)
+
+See :ref:`ex_hide_row_col` for more details.
+
+worksheet.outline_settings()
+----------------------------
+
+.. py:function:: outline_settings(visible, symbols_below, symbols_right, \
+ auto_style)
+
+ Control outline settings.
+
+ :param bool visible: Outlines are visible. Optional, defaults to True.
+ :param bool symbols_below: Show row outline symbols below the outline bar.
+ Optional, defaults to True.
+ :param bool symbols_right: Show column outline symbols to the right of the
+ outline bar. Optional, defaults to True.
+ :param bool auto_style: Use Automatic style. Optional, defaults to False.
+
+
+The ``outline_settings()`` method is used to control the appearance of outlines
+in Excel. Outlines are described in :ref:`outlines`::
+
+ worksheet1.outline_settings(False, False, False, True)
+
+The ``'visible'`` parameter is used to control whether or not outlines are
+visible. Setting this parameter to ``False`` will cause all outlines on the
+worksheet to be hidden. They can be un-hidden in Excel by means of the "Show
+Outline Symbols" command button. The default setting is ``True`` for visible
+outlines.
+
+The ``'symbols_below'`` parameter is used to control whether the row outline
+symbol will appear above or below the outline level bar. The default setting
+is ``True`` for symbols to appear below the outline level bar.
+
+The ``'symbols_right'`` parameter is used to control whether the column outline
+symbol will appear to the left or the right of the outline level bar. The
+default setting is ``True`` for symbols to appear to the right of the outline
+level bar.
+
+The ``'auto_style'`` parameter is used to control whether the automatic outline
+generator in Excel uses automatic styles when creating an outline. This has no
+effect on a file generated by ``XlsxWriter`` but it does have an effect on how
+the worksheet behaves after it is created. The default setting is ``False``
+for "Automatic Styles" to be turned off.
+
+The default settings for all of these parameters correspond to Excel's default
+parameters.
+
+The worksheet parameters controlled by ``outline_settings()`` are rarely used.
+
+
+worksheet.set_vba_name()
+------------------------
+
+.. py:function:: set_vba_name(name)
+ :noindex:
+
+ Set the VBA name for the worksheet.
+
+ :param string name: The VBA name for the worksheet.
+
+The ``set_vba_name()`` method can be used to set the VBA codename for the
+worksheet (there is a similar method for the workbook VBA name). This is
+sometimes required when a vbaProject macro included via ``add_vba_project()``
+refers to the worksheet. The default Excel VBA name of ``Sheet1``, etc., is
+used if a user defined name isn't specified.
+
+See :ref:`macros` for more details.
diff --git a/dev/performance/Readme.txt b/dev/performance/Readme.txt
new file mode 100644
index 0000000..7c1e3a6
--- /dev/null
+++ b/dev/performance/Readme.txt
@@ -0,0 +1,6 @@
+
+This directory contains some simple tests programs for testing
+performance of the XlsxWriter module.
+
+See the perf_test.sh shell script for examples.
+
diff --git a/dev/performance/bench_excel_writers.py b/dev/performance/bench_excel_writers.py
new file mode 100644
index 0000000..c3fd3e8
--- /dev/null
+++ b/dev/performance/bench_excel_writers.py
@@ -0,0 +1,177 @@
+##############################################################################
+#
+# Simple Python program to benchmark several Python Excel writing modules.
+#
+# python bench_excel_writers.py [num_rows] [num_cols]
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import sys
+from time import clock
+
+import openpyxl
+import pyexcelerate
+import xlsxwriter
+import xlwt
+
+from openpyxl.cell import get_column_letter
+
+
+# Default to 1000 rows x 50 cols.
+if len(sys.argv) > 1:
+ row_max = int(sys.argv[1])
+ col_max = 50
+else:
+ row_max = 1000
+ col_max = 50
+
+if len(sys.argv) > 2:
+ col_max = int(sys.argv[2])
+
+
+def print_elapsed_time(module_name, elapsed):
+ """ Print module run times in a consistent format. """
+ print(" %-22s: %6.2f" % (module_name, elapsed))
+
+
+def time_xlsxwriter():
+ """ Run XlsxWriter in default mode. """
+ start_time = clock()
+
+ workbook = xlsxwriter.Workbook('xlsxwriter.xlsx')
+ worksheet = workbook.add_worksheet()
+
+ for row in range(row_max // 2):
+ for col in range(col_max):
+ worksheet.write_string(row * 2, col, "Row: %d Col: %d" % (row, col))
+ for col in range(col_max):
+ worksheet.write_number(row * 2 + 1, col, row + col)
+
+ workbook.close()
+
+ elapsed = clock() - start_time
+ print_elapsed_time('xlsxwriter', elapsed)
+
+
+def time_xlsxwriter_optimised():
+ """ Run XlsxWriter in optimised/constant memory mode. """
+ start_time = clock()
+
+ workbook = xlsxwriter.Workbook('xlsxwriter_opt.xlsx',
+ {'constant_memory': True})
+ worksheet = workbook.add_worksheet()
+
+ for row in range(row_max // 2):
+ for col in range(col_max):
+ worksheet.write_string(row * 2, col, "Row: %d Col: %d" % (row, col))
+ for col in range(col_max):
+ worksheet.write_number(row * 2 + 1, col, row + col)
+
+ workbook.close()
+
+ elapsed = clock() - start_time
+ print_elapsed_time('xlsxwriter (optimised)', elapsed)
+
+
+def time_openpyxl():
+ """ Run OpenPyXL in default mode. """
+ start_time = clock()
+
+ workbook = openpyxl.workbook.Workbook()
+ worksheet = workbook.active
+
+ for row in range(row_max // 2):
+ for col in range(col_max):
+ colletter = get_column_letter(col + 1)
+ worksheet.cell('%s%s' % (colletter, row * 2 + 1)).value = "Row: %d Col: %d" % (row, col)
+ for col in range(col_max):
+ colletter = get_column_letter(col + 1)
+ worksheet.cell('%s%s' % (colletter, row * 2 + 2)).value = row + col
+
+ workbook.save('openpyxl.xlsx')
+
+ elapsed = clock() - start_time
+ print_elapsed_time('openpyxl', elapsed)
+
+
+def time_openpyxl_optimised():
+ """ Run OpenPyXL in optimised mode. """
+ start_time = clock()
+
+ workbook = openpyxl.workbook.Workbook(optimized_write=True)
+ worksheet = workbook.create_sheet()
+
+ for row in range(row_max // 2):
+ string_data = ["Row: %d Col: %d" % (row, col) for col in range(col_max)]
+ worksheet.append(string_data)
+
+ num_data = [row + col for col in range(col_max)]
+ worksheet.append(num_data)
+
+ workbook.save('openpyxl_opt.xlsx')
+
+ elapsed = clock() - start_time
+ print_elapsed_time('openpyxl (optimised)', elapsed)
+
+
+def time_pyexcelerate():
+ """ Run pyexcelerate in "faster" mode. """
+ start_time = clock()
+
+ workbook = pyexcelerate.Workbook()
+ worksheet = workbook.new_sheet('Sheet1')
+
+ for row in range(row_max // 2):
+ for col in range(col_max):
+ worksheet.set_cell_value(row * 2 + 1, col + 1, "Row: %d Col: %d" % (row, col))
+ for col in range(col_max):
+ worksheet.set_cell_value(row * 2 + 2, col + 1, row + col)
+
+ workbook.save('pyexcelerate.xlsx')
+ elapsed = clock() - start_time
+
+ print_elapsed_time('pyexcelerate', elapsed)
+
+
+def time_xlwt():
+ """ Run xlwt in default mode. """
+ start_time = clock()
+
+ workbook = xlwt.Workbook()
+ worksheet = workbook.add_sheet('Sheet1')
+
+ for row in range(row_max // 2):
+ for col in range(col_max):
+ worksheet.write(row * 2, col, "Row: %d Col: %d" % (row, col))
+ for col in range(col_max):
+ worksheet.write(row * 2 + 1, col, row + col)
+
+ workbook.save('xlwt.xls')
+
+ elapsed = clock() - start_time
+ print_elapsed_time('xlwt', elapsed)
+
+
+print("")
+print("Versions:")
+print(" %-12s: %s" % ('python', sys.version[:5]))
+print(" %-12s: %s" % ('openpyxl', openpyxl.__version__))
+print(" %-12s: %s" % ('pyexcelerate', pyexcelerate.__version__))
+print(" %-12s: %s" % ('xlsxwriter', xlsxwriter.__version__))
+print(" %-12s: %s" % ('xlwt', xlwt.__VERSION__))
+print("")
+
+print("Dimensions:")
+print(" Rows = %d" % row_max)
+print(" Cols = %d" % col_max)
+print("")
+
+print("Times:")
+time_pyexcelerate()
+time_xlwt()
+time_xlsxwriter_optimised()
+time_xlsxwriter()
+time_openpyxl_optimised()
+time_openpyxl()
+print("")
diff --git a/dev/performance/perf_ewx.pl b/dev/performance/perf_ewx.pl
new file mode 100644
index 0000000..a31185d
--- /dev/null
+++ b/dev/performance/perf_ewx.pl
@@ -0,0 +1,62 @@
+#!/usr/bin/perl
+
+##############################################################################
+#
+# Simple Perl program to test the speed and memory usage of the
+# Excel::Writer::XLSX module.
+#
+# perl perf_ewx.pl [num_rows] [optimization_mode]
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+
+use strict;
+use warnings;
+use Excel::Writer::XLSX;
+use Time::HiRes qw(gettimeofday tv_interval);
+use Devel::Size qw(total_size);
+
+# Default to 1000 rows and non-optimised.
+my $row_max = $ARGV[0] || 1000;
+my $col_max = 50;
+my $optimise = $ARGV[1] || 0;
+
+# We double the rows below.
+$row_max /= 2;
+
+# Start timing after everything is loaded.
+my $start_time = [gettimeofday];
+
+# Start of program being tested.
+my $workbook = Excel::Writer::XLSX->new( 'pl_ewx.xlsx' );
+
+if ( $optimise ) {
+ $workbook->set_optimization();
+}
+
+my $worksheet = $workbook->add_worksheet();
+
+$worksheet->set_column( 0, $col_max, 18 );
+
+
+for my $row ( 0 .. $row_max -1) {
+ for my $col ( 0 .. $col_max -1 ) {
+ $worksheet->write( $row * 2, $col, "Row: $row Col: $col" );
+ }
+ for my $col ( 0 .. $col_max ) {
+ $worksheet->write( $row * 2 + 1, $col, $row + $col );
+ }
+}
+
+# Get total memory size for workbook object before closing it.
+my $total_size = total_size( $workbook );
+
+$workbook->close();
+
+# Get the elapsed time.
+my $elapsed = tv_interval( $start_time );
+
+# Print a simple CSV output for reporting.
+printf "%6d, %3d, %6.2f, %d\n", $row_max * 2, $col_max, $elapsed, $total_size;
+
+
+__END__
diff --git a/dev/performance/perf_pyx.py b/dev/performance/perf_pyx.py
new file mode 100644
index 0000000..d1887e3
--- /dev/null
+++ b/dev/performance/perf_pyx.py
@@ -0,0 +1,63 @@
+##############################################################################
+#
+# Simple Python program to test the speed and memory usage of
+# the XlsxWriter module.
+#
+# python perf_pyx.py [num_rows] [optimization_mode]
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+
+import sys
+import xlsxwriter
+from time import clock
+from pympler.asizeof import asizeof
+
+# Default to 1000 rows and non-optimised.
+if len(sys.argv) > 1:
+ row_max = int(sys.argv[1]) // 2
+else:
+ row_max = 1000
+
+if len(sys.argv) > 2 and int(sys.argv[2]) == 1:
+ optimise = 1
+else:
+ optimise = 0
+
+if len(sys.argv) > 3 and int(sys.argv[3]) == 1:
+ get_memory_size = 1
+else:
+ get_memory_size = 0
+
+
+col_max = 50
+
+# Start timing after everything is loaded.
+start_time = clock()
+
+# Start of program being tested.
+workbook = xlsxwriter.Workbook('py_ewx.xlsx',
+ {'constant_memory': optimise})
+worksheet = workbook.add_worksheet()
+
+worksheet.set_column(0, col_max, 18)
+
+for row in range(0, row_max):
+ for col in range(0, col_max):
+ worksheet.write_string(row * 2, col, "Row: %d Col: %d" % (row, col))
+ for col in range(0, col_max + 1):
+ worksheet.write_number(row * 2 + 1, col, row + col)
+
+# Get total memory size for workbook object before closing it.
+if get_memory_size:
+ total_size = asizeof(workbook)
+else:
+ total_size = 0
+
+workbook.close()
+
+# Get the elapsed time.
+elapsed = clock() - start_time
+
+# Print a simple CSV output for reporting.
+
+print("%6d, %3d, %6.2f, %d" % (row_max * 2, col_max, elapsed, total_size))
diff --git a/dev/performance/perf_test.sh b/dev/performance/perf_test.sh
new file mode 100755
index 0000000..ac2ac2d
--- /dev/null
+++ b/dev/performance/perf_test.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+
+##############################################################################
+#
+# Simple test runner for measuring speed and memory usage of XlsxWriter
+# and the Excel::Writer::XLSX modules.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+
+echo ""
+echo "Python and XlsxWriter. Speed only."
+echo "Rows, Columns, Time, Memory"
+sleep 1; python perf_pyx.py 200 0 0
+sleep 1; python perf_pyx.py 400 0 0
+sleep 1; python perf_pyx.py 800 0 0
+sleep 1; python perf_pyx.py 1600 0 0
+sleep 1; python perf_pyx.py 3200 0 0
+sleep 1; python perf_pyx.py 6400 0 0
+sleep 1; python perf_pyx.py 12800 0 0
+
+echo ""
+echo "Python and XlsxWriter. Memory only."
+echo "Rows, Columns, Time, Memory"
+sleep 1; python perf_pyx.py 200 0 1
+sleep 1; python perf_pyx.py 400 0 1
+sleep 1; python perf_pyx.py 800 0 1
+sleep 1; python perf_pyx.py 1600 0 1
+sleep 1; python perf_pyx.py 3200 0 1
+sleep 1; python perf_pyx.py 6400 0 1
+sleep 1; python perf_pyx.py 12800 0 1
+
+echo ""
+echo "Python and XlsxWriter in optimisation mode. Speed only."
+echo "Rows, Columns, Time, Memory"
+sleep 1; python perf_pyx.py 200 1 0
+sleep 1; python perf_pyx.py 400 1 0
+sleep 1; python perf_pyx.py 800 1 0
+sleep 1; python perf_pyx.py 1600 1 0
+sleep 1; python perf_pyx.py 3200 1 0
+sleep 1; python perf_pyx.py 6400 1 0
+sleep 1; python perf_pyx.py 12800 1 0
+
+echo ""
+echo "Python and XlsxWriter in optimisation mode. Memory only."
+echo "Rows, Columns, Time, Memory"
+sleep 1; python perf_pyx.py 200 1 1
+sleep 1; python perf_pyx.py 400 1 1
+sleep 1; python perf_pyx.py 800 1 1
+sleep 1; python perf_pyx.py 1600 1 1
+sleep 1; python perf_pyx.py 3200 1 1
+sleep 1; python perf_pyx.py 6400 1 1
+sleep 1; python perf_pyx.py 12800 1 1
+
+echo ""
+echo "Perl and Excel::Writer::XLSX"
+echo "Rows, Columns, Time, Memory"
+sleep 1; perl perf_ewx.pl 200
+sleep 1; perl perf_ewx.pl 400
+sleep 1; perl perf_ewx.pl 800
+sleep 1; perl perf_ewx.pl 1600
+sleep 1; perl perf_ewx.pl 3200
+sleep 1; perl perf_ewx.pl 6400
+sleep 1; perl perf_ewx.pl 12800
+
+echo ""
+echo "Perl Excel::Writer::XLSX in optimisation mode"
+echo "Rows, Columns, Time, Memory"
+sleep 1; perl perf_ewx.pl 200 1
+sleep 1; perl perf_ewx.pl 400 1
+sleep 1; perl perf_ewx.pl 800 1
+sleep 1; perl perf_ewx.pl 1600 1
+sleep 1; perl perf_ewx.pl 3200 1
+sleep 1; perl perf_ewx.pl 6400 1
+sleep 1; perl perf_ewx.pl 12800 1
+
+echo ""
+echo ""
diff --git a/dev/release/Readme.txt b/dev/release/Readme.txt
new file mode 100644
index 0000000..adfcf46
--- /dev/null
+++ b/dev/release/Readme.txt
@@ -0,0 +1,4 @@
+
+This directory contains some release utilities that are mainly useful
+to the library developer.
+
diff --git a/dev/release/modify_latex.pl b/dev/release/modify_latex.pl
new file mode 100644
index 0000000..d88f72f
--- /dev/null
+++ b/dev/release/modify_latex.pl
@@ -0,0 +1,57 @@
+#!/usr/bin/perl -i
+
+# Simple utility to modify the TeX output prior to creating a pdf file.
+
+use strict;
+use warnings;
+
+
+while (<>) {
+
+ # Convert escaped single quotes back to real single quote so that
+ # the Latex upquote package has an effect.
+ s/\\PYGZsq{}/'/g;
+
+
+ # Modify the Pygments formatting.
+ #
+ # Remove italic.
+ s/\\let\\PYG\@it=\\textit//g;
+
+ # Change the comments color.
+ s/0\.25,0\.50,0\.56/0.40,0.69,0.33/;
+
+
+ # Change scale of images and center them.
+ if ( s/^\\includegraphics/\\includegraphics[scale=0.75]/ ) {
+ print "\\begin{center}\n";
+ print;
+ print "\\end{center}\n";
+
+ next;
+ }
+
+ # Wrap Verbatim sections in "quote" to indent.
+ if ( /^\\begin{Verbatim}/ ) {
+ print "\\begin{quote}\n";
+ }
+
+ print;
+
+ if ( /^\\end{Verbatim}/ ) {
+ print "\\end{quote}\n";
+ }
+
+ # Modifiy the pre-amble. We could do this in the Sphinx conf.py
+ # but ReadTheDocs doesn't support the fonts.
+ if ( /^\\usepackage{sphinx}/ ) {
+ print "\\usepackage{upquote}\n";
+ print "\\usepackage{DejaVuSansMono}\n";
+ print "\\usepackage[T1]{fontenc}\n";
+ print "\\usepackage{helvet}\n";
+ print "\\renewcommand{\\familydefault}{\\sfdefault}\n";
+ }
+}
+
+
+__END__
diff --git a/dev/release/release_check.sh b/dev/release/release_check.sh
new file mode 100755
index 0000000..201e1a6
--- /dev/null
+++ b/dev/release/release_check.sh
@@ -0,0 +1,199 @@
+#!/bin/bash
+
+clear
+echo "|"
+echo "| Pre-release checks."
+echo "|"
+echo
+
+
+#############################################################
+#
+# Run tests.
+#
+function check_test_status {
+
+ echo
+ echo -n "Are all tests passing for all Pythons? [y/N]: "
+ read RESPONSE
+
+ if [ "$RESPONSE" != "y" ]; then
+
+ echo -n " Run all tests now? [y/N]: "
+ read RESPONSE
+
+ if [ "$RESPONSE" != "y" ]; then
+ echo
+ echo -e "Please run: make testpythonsall\n";
+ exit 1
+ else
+ echo " Running tests...";
+ make testpythonsall
+ check_test_status
+ fi
+ fi
+}
+
+
+#############################################################
+#
+# Run spellcheck.
+#
+function check_spellcheck {
+
+ echo
+ echo -n "Is the spellcheck ok? [y/N]: "
+ read RESPONSE
+
+ if [ "$RESPONSE" != "y" ]; then
+
+ echo -n " Run spellcheck now? [y/N]: "
+ read RESPONSE
+
+ if [ "$RESPONSE" != "y" ]; then
+ echo
+ echo -e "Please run: make spellcheck\n";
+ exit 1
+ else
+ echo " Running spellcheck...";
+ make spellcheck
+ check_spellcheck
+ fi
+ fi
+}
+
+
+#############################################################
+#
+# Check Changes file is up to date.
+#
+function check_changefile {
+ clear
+
+ echo "Latest change in Changes file: "
+ perl -ne '$rev++ if /^Release/; exit if $rev > 1; print " | $_"' Changes
+
+ echo
+ echo -n "Is the Changes file updated? [y/N]: "
+ read RESPONSE
+
+ if [ "$RESPONSE" != "y" ]; then
+ echo
+ echo -e "Please update the Change file to proceed.\n";
+ exit 1
+ fi
+}
+
+
+#############################################################
+#
+# Check the versions are up to date.
+#
+function check_versions {
+
+ clear
+ echo
+ echo "Latest file versions: "
+
+ grep -He "[0-9]\.[0-9]\.[0-9]" setup.py dev/docs/source/conf.py xlsxwriter/__init__.py | sed 's/:/ : /g' | sed 's/=/ = /' | awk '{printf " | %-24s %s\n", $1, $5}'
+
+ echo
+ echo -n "Are the versions up to date? [y/N]: "
+ read RESPONSE
+
+ if [ "$RESPONSE" != "y" ]; then
+ echo -n " Update versions? [y/N]: "
+ read RESPONSE
+
+ if [ "$RESPONSE" != "y" ]; then
+ echo
+ echo -e "Please update the versions to proceed.\n";
+ exit 1
+ else
+ echo " Updating versions...";
+ perl -i dev/release/update_revison.pl setup.py dev/docs/source/conf.py xlsxwriter/__init__.py
+ check_versions
+ fi
+ fi
+}
+
+#############################################################
+#
+# Check that the PDF doc is up to date.
+#
+function check_pdf_doc {
+
+ clear
+ echo
+ echo -n "Is the PDF doc up to date? [y/N]: "
+ read RESPONSE
+
+ if [ "$RESPONSE" != "y" ]; then
+ echo -n " Rebuild pdf? [y/N]: "
+ read RESPONSE
+
+ if [ "$RESPONSE" == "y" ]; then
+ make -C dev/docs latexpdf
+ cp -r dev/docs/build/latex/XlsxWriter.pdf docs
+ fi
+ fi
+}
+
+#############################################################
+#
+# Run release checks.
+#
+function check_git_status {
+ clear
+
+ echo "Git status: "
+ git status | awk '{print " | ", $0}'
+
+ echo "Git log: "
+ git log -1 | awk '{print " | ", $0}'
+
+ echo "Git latest tag: "
+ git tag -l -n1 | tail -1 | awk '{print " | ", $0}'
+
+ echo
+ echo -n "Is the git status okay? [y/N]: "
+ read RESPONSE
+
+ if [ "$RESPONSE" != "y" ]; then
+ echo
+ echo -e "Please fix git status.\n";
+
+ git tag -l -n1 | tail -1 | perl -lane 'printf "git commit -m \"Prep for release %s\"\ngit tag \"%s\"\n\n", $F[4], $F[0]' | perl dev/release/update_revison.pl
+ exit 1
+ fi
+}
+
+check_test_status
+check_spellcheck
+check_changefile
+check_versions
+check_pdf_doc
+check_git_status
+
+
+#############################################################
+#
+# All checks complete.
+#
+clear
+echo
+echo "Interface configured [OK]"
+echo "Versions updated [OK]"
+echo "Git status [OK]"
+echo
+echo "Everything is configured.";
+echo
+
+echo -n "Confirm release: [y/N]: ";
+read RESPONSE
+
+if [ "$RESPONSE" == "y" ]; then
+ exit 0
+else
+ exit 1
+fi
diff --git a/dev/release/update_revison.pl b/dev/release/update_revison.pl
new file mode 100755
index 0000000..7b1c30b
--- /dev/null
+++ b/dev/release/update_revison.pl
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+
+# Simple script to increment x.y.z style version numbers in a file.
+
+use strict;
+use warnings;
+use Perl::Version;
+
+while (<>) {
+
+ # Increment any x.y.z version strings.
+ if (m/(\d\.\d\.\d)/) {
+ my $version = Perl::Version->new( $1 );
+
+ # Components are: revision, version and subversion.
+ if ( $version->version == 9 && $version->subversion == 9 ) {
+ $version->inc_revision();
+ }
+ elsif ( $version->subversion == 9 ) {
+ $version->inc_version();
+ }
+ else {
+ $version->inc_subversion();
+ }
+
+ my $new_version = $version->stringify();
+ s/(\d\.\d\.\d)/$new_version/;
+ }
+
+ print;
+}
+
+
+__END__
diff --git a/docs/Readme.txt b/docs/Readme.txt
new file mode 100644
index 0000000..1f3e3c5
--- /dev/null
+++ b/docs/Readme.txt
@@ -0,0 +1,3 @@
+
+This directory contains the source files for the documentation.
+
diff --git a/docs/XlsxWriter.pdf b/docs/XlsxWriter.pdf
new file mode 100644
index 0000000..59ef974
Binary files /dev/null and b/docs/XlsxWriter.pdf differ
diff --git a/docs/_static/basic.css b/docs/_static/basic.css
new file mode 100644
index 0000000..3028df7
--- /dev/null
+++ b/docs/_static/basic.css
@@ -0,0 +1,541 @@
+/*
+ * basic.css
+ * ~~~~~~~~~
+ *
+ * Sphinx stylesheet -- basic theme.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+/* -- main layout ----------------------------------------------------------- */
+
+div.clearer {
+ clear: both;
+}
+
+/* -- relbar ---------------------------------------------------------------- */
+
+div.related {
+ width: 100%;
+ font-size: 90%;
+}
+
+div.related h3 {
+ display: none;
+}
+
+div.related ul {
+ margin: 0;
+ padding: 0 0 0 10px;
+ list-style: none;
+}
+
+div.related li {
+ display: inline;
+}
+
+div.related li.right {
+ float: right;
+ margin-right: 5px;
+}
+
+/* -- sidebar --------------------------------------------------------------- */
+
+div.sphinxsidebarwrapper {
+ padding: 10px 5px 0 10px;
+}
+
+div.sphinxsidebar {
+ float: left;
+ width: 230px;
+ margin-left: -100%;
+ font-size: 90%;
+}
+
+div.sphinxsidebar ul {
+ list-style: none;
+}
+
+div.sphinxsidebar ul ul,
+div.sphinxsidebar ul.want-points {
+ margin-left: 20px;
+ list-style: square;
+}
+
+div.sphinxsidebar ul ul {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+div.sphinxsidebar form {
+ margin-top: 10px;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #98dbcc;
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+div.sphinxsidebar #searchbox input[type="text"] {
+ width: 170px;
+}
+
+div.sphinxsidebar #searchbox input[type="submit"] {
+ width: 30px;
+}
+
+img {
+ border: 0;
+}
+
+/* -- search page ----------------------------------------------------------- */
+
+ul.search {
+ margin: 10px 0 0 20px;
+ padding: 0;
+}
+
+ul.search li {
+ padding: 5px 0 5px 20px;
+ background-image: url(file.png);
+ background-repeat: no-repeat;
+ background-position: 0 7px;
+}
+
+ul.search li a {
+ font-weight: bold;
+}
+
+ul.search li div.context {
+ color: #888;
+ margin: 2px 0 0 30px;
+ text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+ font-weight: bold;
+}
+
+/* -- index page ------------------------------------------------------------ */
+
+table.contentstable {
+ width: 90%;
+}
+
+table.contentstable p.biglink {
+ line-height: 150%;
+}
+
+a.biglink {
+ font-size: 1.3em;
+}
+
+span.linkdescr {
+ font-style: italic;
+ padding-top: 5px;
+ font-size: 90%;
+}
+
+/* -- general index --------------------------------------------------------- */
+
+table.indextable {
+ width: 100%;
+}
+
+table.indextable td {
+ text-align: left;
+ vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+ height: 10px;
+}
+
+table.indextable tr.cap {
+ margin-top: 10px;
+ background-color: #f2f2f2;
+}
+
+img.toggler {
+ margin-right: 3px;
+ margin-top: 3px;
+ cursor: pointer;
+}
+
+div.modindex-jumpbox {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ margin: 1em 0 1em 0;
+ padding: 0.4em;
+}
+
+div.genindex-jumpbox {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ margin: 1em 0 1em 0;
+ padding: 0.4em;
+}
+
+/* -- general body styles --------------------------------------------------- */
+
+a.headerlink {
+ visibility: hidden;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink {
+ visibility: visible;
+}
+
+div.body p.caption {
+ text-align: inherit;
+}
+
+div.body td {
+ text-align: left;
+}
+
+.field-list ul {
+ padding-left: 1em;
+}
+
+.first {
+ margin-top: 0 !important;
+}
+
+p.rubric {
+ margin-top: 30px;
+ font-weight: bold;
+}
+
+img.align-left, .figure.align-left, object.align-left {
+ clear: left;
+ float: left;
+ margin-right: 1em;
+}
+
+img.align-right, .figure.align-right, object.align-right {
+ clear: right;
+ float: right;
+ margin-left: 1em;
+}
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left;
+}
+
+.align-center {
+ text-align: center;
+}
+
+.align-right {
+ text-align: right;
+}
+
+/* -- sidebars -------------------------------------------------------------- */
+
+div.sidebar {
+ margin: 0 0 0.5em 1em;
+ border: 1px solid #ddb;
+ padding: 7px 7px 0 7px;
+ background-color: #ffe;
+ width: 40%;
+ float: right;
+}
+
+p.sidebar-title {
+ font-weight: bold;
+}
+
+/* -- topics ---------------------------------------------------------------- */
+
+div.topic {
+ border: 1px solid #ccc;
+ padding: 7px 7px 0 7px;
+ margin: 10px 0 10px 0;
+}
+
+p.topic-title {
+ font-size: 1.1em;
+ font-weight: bold;
+ margin-top: 10px;
+}
+
+/* -- admonitions ----------------------------------------------------------- */
+
+div.admonition {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding: 7px;
+}
+
+div.admonition dt {
+ font-weight: bold;
+}
+
+div.admonition dl {
+ margin-bottom: 0;
+}
+
+p.admonition-title {
+ margin: 0px 10px 5px 0px;
+ font-weight: bold;
+}
+
+div.body p.centered {
+ text-align: center;
+ margin-top: 25px;
+}
+
+/* -- tables ---------------------------------------------------------------- */
+
+table.docutils {
+ margin-left: 30px;
+ border: 0;
+ border-collapse: collapse;
+}
+
+table.docutils td, table.docutils th {
+ padding: 1px 8px 1px 5px;
+ border-top: 1px solid #aaa;
+ border-left: 1px solid #aaa;
+ border-right: 1px solid #aaa;
+ border-bottom: 1px solid #aaa;
+}
+
+table.field-list td, table.field-list th {
+ border: 0 !important;
+}
+
+table.footnote td, table.footnote th {
+ border: 0 !important;
+}
+
+th {
+ text-align: left;
+ padding-right: 5px;
+}
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px;
+}
+
+table.citation td {
+ border-bottom: none;
+}
+
+/* -- other body styles ----------------------------------------------------- */
+
+ol.arabic {
+ list-style: decimal;
+}
+
+ol.loweralpha {
+ list-style: lower-alpha;
+}
+
+ol.upperalpha {
+ list-style: upper-alpha;
+}
+
+ol.lowerroman {
+ list-style: lower-roman;
+}
+
+ol.upperroman {
+ list-style: upper-roman;
+}
+
+dl {
+ margin-bottom: 30px;
+}
+
+dd p {
+ margin-top: 0px;
+}
+
+dd ul, dd table {
+ margin-bottom: 10px;
+}
+
+dd {
+ margin-top: 3px;
+ margin-bottom: 10px;
+ margin-left: 30px;
+}
+
+dt:target, .highlighted {
+ background-color: #fbe54e;
+}
+
+dl.glossary dt {
+ font-weight: bold;
+ font-size: 1.1em;
+}
+
+.field-list ul {
+ margin: 0;
+ padding-left: 1em;
+}
+
+.field-list p {
+ margin: 0;
+}
+
+.refcount {
+ color: #060;
+}
+
+.optional {
+ font-size: 1.3em;
+}
+
+.versionmodified {
+ font-style: italic;
+}
+
+.system-message {
+ background-color: #fda;
+ padding: 5px;
+ border: 3px solid red;
+}
+
+.footnote:target {
+ background-color: #ffa;
+}
+
+.line-block {
+ display: block;
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+
+.line-block .line-block {
+ margin-top: 0;
+ margin-bottom: 0;
+ margin-left: 1.5em;
+}
+
+.guilabel, .menuselection {
+ font-family: sans-serif;
+}
+
+.accelerator {
+ text-decoration: underline;
+}
+
+.classifier {
+ font-style: oblique;
+}
+
+abbr, acronym {
+ border-bottom: dotted 1px;
+ cursor: help;
+}
+
+/* -- code displays --------------------------------------------------------- */
+
+pre {
+ overflow: auto;
+ overflow-y: hidden; /* fixes display issues on Chrome browsers */
+}
+
+td.linenos pre {
+ padding: 5px 0px;
+ border: 0;
+ background-color: transparent;
+ color: #aaa;
+}
+
+table.highlighttable {
+ margin-left: 0.5em;
+}
+
+table.highlighttable td {
+ padding: 0 0.5em 0 0.5em;
+}
+
+tt.descname {
+ background-color: transparent;
+ font-weight: bold;
+ font-size: 1.2em;
+}
+
+tt.descclassname {
+ background-color: transparent;
+}
+
+tt.xref, a tt {
+ background-color: transparent;
+ font-weight: bold;
+}
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+ background-color: transparent;
+}
+
+.viewcode-link {
+ float: right;
+}
+
+.viewcode-back {
+ float: right;
+ font-family: sans-serif;
+}
+
+div.viewcode-block:target {
+ margin: -1px -10px;
+ padding: 0 10px;
+}
+
+/* -- math display ---------------------------------------------------------- */
+
+img.math {
+ vertical-align: middle;
+}
+
+div.body div.math p {
+ text-align: center;
+}
+
+span.eqno {
+ float: right;
+}
+
+/* -- printout stylesheet --------------------------------------------------- */
+
+ at media print {
+ div.document,
+ div.documentwrapper,
+ div.bodywrapper {
+ margin: 0 !important;
+ width: 100%;
+ }
+
+ div.sphinxsidebar,
+ div.related,
+ div.footer,
+ #top-link {
+ display: none;
+ }
+}
\ No newline at end of file
diff --git a/docs/_static/default.css b/docs/_static/default.css
new file mode 100644
index 0000000..409ef80
--- /dev/null
+++ b/docs/_static/default.css
@@ -0,0 +1,258 @@
+/*
+ * default.css_t
+ * ~~~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- default theme.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+ at import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+ font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif;
+ font-size: 100%;
+ background-color: #FFFFFF;
+ color: #000;
+ margin: 0;
+ padding: 0;
+ min-width: 800px;
+}
+
+
+div.document {
+ background-color: #F2F2F2;
+}
+
+div.documentwrapper {
+ float: left;
+ width: 100%;
+}
+
+div.bodywrapper {
+ margin: 0 0 0 280px;
+}
+
+div.body {
+ background-color: #ffffff;
+ color: #000000;
+ padding: 0 20px 30px 20px;
+}
+
+div.footer {
+ color: #9CB640;
+ width: 100%;
+ padding: 9px 0 9px 0;
+ text-align: center;
+ font-size: 75%;
+}
+
+div.footer a {
+ color: #9CB640;
+ text-decoration: underline;
+}
+
+div.related {
+ background-color: #9CB640;
+ line-height: 80px;
+ color: #ffffff;
+}
+
+div.related a {
+ color: #ffffff;
+}
+
+div.sphinxsidebar {
+}
+
+div.sphinxsidebar h3 {
+ font-family: 'Trebuchet MS', sans-serif;
+ color: #9CB640;
+ font-size: 1.4em;
+ font-weight: normal;
+ margin: 0;
+ padding: 0;
+}
+
+div.sphinxsidebar h3 a {
+ color: #ffffff;
+}
+
+div.sphinxsidebar h4 {
+ font-family: 'Trebuchet MS', sans-serif;
+ color: #9CB640;
+ font-size: 1.3em;
+ font-weight: normal;
+ margin: 5px 0 0 0;
+ padding: 0;
+}
+
+div.sphinxsidebar p {
+ color: #ffffff;
+}
+
+div.sphinxsidebar p.topless {
+ margin: 5px 10px 10px 10px;
+}
+
+div.sphinxsidebar ul {
+ margin: 10px;
+ padding: 0;
+ color: #ffffff;
+}
+
+div.sphinxsidebar a {
+ color: #9CB640;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #9CB640;
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+
+
+/* -- hyperlink styles ------------------------------------------------------ */
+
+a {
+ color: #9CB640;
+ text-decoration: none;
+}
+
+a:visited {
+ color: #9CB640;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+
+
+/* -- body styles ----------------------------------------------------------- */
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+ font-family: 'Trebuchet MS', sans-serif;
+ background-color: #f2f2f2;
+ font-weight: normal;
+ color: #9CB640;
+ border-bottom: 1px solid #ccc;
+ margin: 20px -20px 10px -20px;
+ padding: 3px 0 3px 10px;
+}
+
+div.body h1 { margin-top: 0; font-size: 200%; padding: 20px;}
+div.body h2 { font-size: 160%; }
+div.body h3 { font-size: 140%; }
+div.body h4 { font-size: 120%; }
+div.body h5 { font-size: 110%; }
+div.body h6 { font-size: 100%; }
+
+a.headerlink {
+ color: #c60f0f;
+ font-size: 0.8em;
+ padding: 0 4px 0 4px;
+ text-decoration: none;
+}
+
+a.headerlink:hover {
+ background-color: #c60f0f;
+ color: white;
+}
+
+div.body p, div.body dd, div.body li {
+ text-align: justify;
+ line-height: 130%;
+}
+
+div.admonition p.admonition-title + p {
+ display: inline;
+}
+
+div.admonition p {
+ margin-bottom: 5px;
+}
+
+div.admonition pre {
+ margin-bottom: 5px;
+}
+
+div.admonition ul, div.admonition ol {
+ margin-bottom: 5px;
+}
+
+div.note {
+ background-color: #eee;
+ border: 1px solid #ccc;
+}
+
+div.seealso {
+ background-color: #ffc;
+ border: 1px solid #ff6;
+}
+
+div.topic {
+ background-color: #eee;
+}
+
+div.warning {
+ background-color: #ffe4e4;
+ border: 1px solid #f66;
+}
+
+p.admonition-title {
+ display: inline;
+}
+
+p.admonition-title:after {
+ content: ":";
+}
+
+pre {
+ margin-left: 30px;
+ width : 640px;
+ padding: 15px;
+ color: #333333;
+ line-height: 120%;
+ border: 1px solid #ac9;
+}
+
+cite, code, tt {
+ font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
+ font-size: 0.95em;
+ letter-spacing: 0.01em;
+}
+
+
+th {
+ background-color: #f2f2f2;
+}
+
+.warning tt {
+ background: #efc2c2;
+}
+
+.note tt {
+ background: #d6d6d6;
+}
+
+.viewcode-back {
+ font-family: sans-serif;
+}
+
+div.viewcode-block:target {
+ background-color: #f4debf;
+ border-top: 1px solid #ac9;
+ border-bottom: 1px solid #ac9;
+}
\ No newline at end of file
diff --git a/docs/_static/hello01.png b/docs/_static/hello01.png
new file mode 100644
index 0000000..93c3ed1
Binary files /dev/null and b/docs/_static/hello01.png differ
diff --git a/docs/_static/logo.png b/docs/_static/logo.png
new file mode 100644
index 0000000..8428d76
Binary files /dev/null and b/docs/_static/logo.png differ
diff --git a/docs/_static/pygments.css b/docs/_static/pygments.css
new file mode 100644
index 0000000..34b2ef6
--- /dev/null
+++ b/docs/_static/pygments.css
@@ -0,0 +1,61 @@
+.highlight .hll { border-left: 2px solid #ff0000; }
+.highlight .c { color: #65B154 } /* Comment */
+.highlight .err { border: 1px solid #FF0000 } /* Error */
+.highlight .k { color: #007020; font-weight: bold } /* Keyword */
+.highlight .o { color: #666666 } /* Operator */
+.highlight .cm { color: #65B154 } /* Comment.Multiline */
+.highlight .cp { color: #007020 } /* Comment.Preproc */
+.highlight .c1 { color: #65B154 } /* Comment.Single */
+.highlight .cs { color: #65B154; background-color: #fff0f0 } /* Comment.Special */
+.highlight .gd { color: #A00000 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #FF0000 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #333333 } /* Generic.Output */
+.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #0044DD } /* Generic.Traceback */
+.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
+.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
+.highlight .kp { color: #007020 } /* Keyword.Pseudo */
+.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #902000 } /* Keyword.Type */
+.highlight .m { color: #208050 } /* Literal.Number */
+.highlight .s { color: #4070a0 } /* Literal.String */
+.highlight .na { color: #4070a0 } /* Name.Attribute */
+.highlight .nb { color: #007020 } /* Name.Builtin */
+.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
+.highlight .no { color: #60add5 } /* Name.Constant */
+.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
+.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
+.highlight .ne { color: #007020 } /* Name.Exception */
+.highlight .nf { color: #06287e } /* Name.Function */
+.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
+.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
+.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #bb60d5 } /* Name.Variable */
+.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mf { color: #208050 } /* Literal.Number.Float */
+.highlight .mh { color: #208050 } /* Literal.Number.Hex */
+.highlight .mi { color: #208050 } /* Literal.Number.Integer */
+.highlight .mo { color: #208050 } /* Literal.Number.Oct */
+.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
+.highlight .sc { color: #4070a0 } /* Literal.String.Char */
+.highlight .sd { color: #4070a0 } /* Literal.String.Doc */
+.highlight .s2 { color: #4070a0 } /* Literal.String.Double */
+.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
+.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
+.highlight .si { color: #70a0d0 } /* Literal.String.Interpol */
+.highlight .sx { color: #c65d09 } /* Literal.String.Other */
+.highlight .sr { color: #235388 } /* Literal.String.Regex */
+.highlight .s1 { color: #4070a0 } /* Literal.String.Single */
+.highlight .ss { color: #517918 } /* Literal.String.Symbol */
+.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
+.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
+.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
+.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */
\ No newline at end of file
diff --git a/docs/readme.html b/docs/readme.html
new file mode 100644
index 0000000..a996bc2
--- /dev/null
+++ b/docs/readme.html
@@ -0,0 +1,152 @@
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Getting Started with XlsxWriter — XlsxWriter Readme</title>
+
+ <link rel="stylesheet" href="_static/default.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.3.2',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="XlsxWriter Readme" href="index.html" />
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li><a href="#">XlsxWriter Readme</a> »</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="getting-started-with-xlsxwriter">
+<h1>Getting Started with XlsxWriter</h1>
+<p>Here are some easy instructions to get you up and running with the XlsxWriter
+module.</p>
+<div class="section" id="installing-xlsxwriter">
+<h2>Installing XlsxWriter</h2>
+<p>The first step is to install the XlsxWriter module. There are several ways to
+do this.</p>
+<div class="section" id="using-pip">
+<h3>Using PIP</h3>
+<p>The <a class="reference external" href="http://www.pip-installer.org/en/latest/index.html">pip</a> installer is
+the preferred method for installing Python modules from
+<a class="reference external" href="http://pypi.python.org/pypi">PyPI</a>, the Python Package Index:</p>
+<div class="highlight-python"><pre>$ sudo pip install XlsxWriter</pre>
+</div>
+<div class="admonition note">
+<p class="first admonition-title">Note</p>
+<p class="last">Windows users can omit <tt class="docutils literal"><span class="pre">sudo</span></tt> at the start of the command.</p>
+</div>
+</div>
+<div class="section" id="using-easy-install">
+<h3>Using Easy_Install</h3>
+<p>If <tt class="docutils literal"><span class="pre">pip</span></tt> doesn’t work you can try
+<a class="reference external" href="http://peak.telecommunity.com/DevCenter/EasyInstall">easy_install</a>:</p>
+<div class="highlight-python"><pre>$ sudo easy_install install XlsxWriter</pre>
+</div>
+</div>
+<div class="section" id="installing-from-a-tarball">
+<h3>Installing from a tarball</h3>
+<p>If you download a tarball of the latest version of XlsxWriter you can install
+it as follows (change the version number to suit):</p>
+<div class="highlight-python"><pre>$ tar -zxvf XlsxWriter-1.2.3.tar.gz
+
+$ cd XlsxWriter-1.2.3
+$ sudo python setup.py install</pre>
+</div>
+<p>A tarball of the latest code can be downloaded from GitHub as follows:</p>
+<div class="highlight-python"><pre>$ curl -O -L http://github.com/jmcnamara/XlsxWriter/archive/master.tar.gz
+
+$ tar zxvf master.tar.gz
+$ cd XlsxWriter-master/
+$ sudo python setup.py install</pre>
+</div>
+</div>
+<div class="section" id="cloning-from-github">
+<h3>Cloning from GitHub</h3>
+<p>The XlsxWriter source code and bug tracker is in the
+<a class="reference external" href="http://github.com/jmcnamara/XlsxWriter">XlsxWriter repository</a> on GitHub.
+You can clone the repository and install from it as follows:</p>
+<div class="highlight-python"><pre>$ git clone https://github.com/jmcnamara/XlsxWriter.git
+
+$ cd XlsxWriter
+$ sudo python setup.py install</pre>
+</div>
+</div>
+</div>
+<div class="section" id="running-a-sample-program">
+<h2>Running a sample program</h2>
+<p>If the installation went correctly you can create a small sample program like
+the following to verify that the module works correctly:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="n">xlsxwriter</span>
+
+<span class="n">workbook</span> <span class="o">=</span> <span class="n">xlsxwriter.Workbook</span><span class="p">(</span><span class="s">'hello.xlsx'</span><span class="p">)</span>
+<span class="n">worksheet</span> <span class="o">=</span> <span class="n">workbook</span><span class="o">.</span><span class="n">add_worksheet</span><span class="p">()</span>
+
+<span class="n">worksheet</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">'A1'</span><span class="p">,</span> <span class="s">'Hello world'</span><span class="p">)</span>
+
+<span class="n">workbook</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>Save this to a file called <tt class="docutils literal"><span class="pre">hello.py</span></tt> and run it as follows:</p>
+<div class="highlight-python"><pre>$ python hello.py</pre>
+</div>
+<p>This will output a file called <tt class="docutils literal"><span class="pre">hello.xlsx</span></tt> which should look something like
+the following:</p>
+<img alt="_static/hello01.png" src="_static/hello01.png" />
+<p>If you downloaded a tarball or cloned the repo, as shown above, you should also
+have a directory called
+<a class="reference external" href="https://github.com/jmcnamara/XlsxWriter/tree/master/examples">examples</a>
+with some sample applications that demonstrate different features of
+XlsxWriter.</p>
+</div>
+</div>
+<div class="section" id="documentation">
+<h1>Documentation</h1>
+<p>The full version of XlsxWriter documentation is hosted on
+<a class="reference external" href="https://xlsxwriter.readthedocs.io">Read The Docs</a>. It is
+also available as a
+<a class="reference external" href="https://github.com/jmcnamara/XlsxWriter/raw/master/docs/XlsxWriter.pdf">PDF</a>.</p>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper">
+ <p class="logo"><a href="#">
+ <img class="logo" src="_static/logo.png" alt="Logo"/>
+ </a></p>
+
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ © Copyright 2013-2016, John McNamara.
+ </div>
+ </body>
+</html>
diff --git a/examples/array_formula.py b/examples/array_formula.py
new file mode 100644
index 0000000..05001e7
--- /dev/null
+++ b/examples/array_formula.py
@@ -0,0 +1,36 @@
+#######################################################################
+#
+# Example of how to use Python and the XlsxWriter module to write
+# simple array formulas.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+# Create a new workbook and add a worksheet
+workbook = xlsxwriter.Workbook('array_formula.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Write some test data.
+worksheet.write('B1', 500)
+worksheet.write('B2', 10)
+worksheet.write('B5', 1)
+worksheet.write('B6', 2)
+worksheet.write('B7', 3)
+worksheet.write('C1', 300)
+worksheet.write('C2', 15)
+worksheet.write('C5', 20234)
+worksheet.write('C6', 21003)
+worksheet.write('C7', 10000)
+
+
+# Write an array formula that returns a single value
+worksheet.write_formula('A1', '{=SUM(B1:C1*B2:C2)}')
+
+# Same as above but more verbose.
+worksheet.write_array_formula('A2:A2', '{=SUM(B1:C1*B2:C2)}')
+
+# Write an array formula that returns a range of values
+worksheet.write_array_formula('A5:A7', '{=TREND(C5:C7,B5:B7)}')
+
+workbook.close()
diff --git a/examples/autofilter.py b/examples/autofilter.py
new file mode 100644
index 0000000..6621a0a
--- /dev/null
+++ b/examples/autofilter.py
@@ -0,0 +1,237 @@
+###############################################################################
+#
+# An example of how to create autofilters with XlsxWriter.
+#
+# An autofilter is a way of adding drop down lists to the headers of a 2D
+# range of worksheet data. This allows users to filter the data based on
+# simple criteria so that some data is shown and some is hidden.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('autofilter.xlsx')
+
+# Add a worksheet for each autofilter example.
+worksheet1 = workbook.add_worksheet()
+worksheet2 = workbook.add_worksheet()
+worksheet3 = workbook.add_worksheet()
+worksheet4 = workbook.add_worksheet()
+worksheet5 = workbook.add_worksheet()
+worksheet6 = workbook.add_worksheet()
+
+# Add a bold format for the headers.
+bold = workbook.add_format({'bold': 1})
+
+# Open a text file with autofilter example data.
+textfile = open('autofilter_data.txt')
+
+# Read the headers from the first line of the input file.
+headers = textfile.readline().strip("\n").split()
+
+
+# Read the text file and store the field data.
+data = []
+for line in textfile:
+ # Split the input data based on whitespace.
+ row_data = line.strip("\n").split()
+
+ # Convert the number data from the text file.
+ for i, item in enumerate(row_data):
+ try:
+ row_data[i] = float(item)
+ except ValueError:
+ pass
+
+ data.append(row_data)
+
+
+# Set up several sheets with the same data.
+for worksheet in (workbook.worksheets()):
+ # Make the columns wider.
+ worksheet.set_column('A:D', 12)
+ # Make the header row larger.
+ worksheet.set_row(0, 20, bold)
+ # Make the headers bold.
+ worksheet.write_row('A1', headers)
+
+
+###############################################################################
+#
+# Example 1. Autofilter without conditions.
+#
+
+# Set the autofilter.
+worksheet1.autofilter('A1:D51')
+
+row = 1
+for row_data in (data):
+ worksheet1.write_row(row, 0, row_data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+
+###############################################################################
+#
+#
+# Example 2. Autofilter with a filter condition in the first column.
+#
+
+# Autofilter range using Row-Column notation.
+worksheet2.autofilter(0, 0, 50, 3)
+
+# Add filter criteria. The placeholder "Region" in the filter is
+# ignored and can be any string that adds clarity to the expression.
+worksheet2.filter_column(0, 'Region == East')
+
+# Hide the rows that don't match the filter criteria.
+row = 1
+for row_data in (data):
+ region = row_data[0]
+
+ # Check for rows that match the filter.
+ if region == 'East':
+ # Row matches the filter, no further action required.
+ pass
+ else:
+ # We need to hide rows that don't match the filter.
+ worksheet2.set_row(row, options={'hidden': True})
+
+ worksheet2.write_row(row, 0, row_data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+
+###############################################################################
+#
+#
+# Example 3. Autofilter with a dual filter condition in one of the columns.
+#
+
+# Set the autofilter.
+worksheet3.autofilter('A1:D51')
+
+# Add filter criteria.
+worksheet3.filter_column('A', 'x == East or x == South')
+
+# Hide the rows that don't match the filter criteria.
+row = 1
+for row_data in (data):
+ region = row_data[0]
+
+ # Check for rows that match the filter.
+ if region == 'East' or region == 'South':
+ # Row matches the filter, no further action required.
+ pass
+ else:
+ # We need to hide rows that don't match the filter.
+ worksheet3.set_row(row, options={'hidden': True})
+
+ worksheet3.write_row(row, 0, row_data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+
+###############################################################################
+#
+#
+# Example 4. Autofilter with filter conditions in two columns.
+#
+
+# Set the autofilter.
+worksheet4.autofilter('A1:D51')
+
+# Add filter criteria.
+worksheet4.filter_column('A', 'x == East')
+worksheet4.filter_column('C', 'x > 3000 and x < 8000')
+
+# Hide the rows that don't match the filter criteria.
+row = 1
+for row_data in (data):
+ region = row_data[0]
+ volume = int(row_data[2])
+
+ # Check for rows that match the filter.
+ if region == 'East' and volume > 3000 and volume < 8000:
+ # Row matches the filter, no further action required.
+ pass
+ else:
+ # We need to hide rows that don't match the filter.
+ worksheet4.set_row(row, options={'hidden': True})
+
+ worksheet4.write_row(row, 0, row_data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+
+###############################################################################
+#
+#
+# Example 5. Autofilter with filter for blanks.
+#
+# Create a blank cell in our test data.
+
+# Set the autofilter.
+worksheet5.autofilter('A1:D51')
+
+# Add filter criteria.
+worksheet5.filter_column('A', 'x == Blanks')
+
+# Simulate a blank cell in the data.
+data[5][0] = ''
+
+# Hide the rows that don't match the filter criteria.
+row = 1
+for row_data in (data):
+ region = row_data[0]
+
+ # Check for rows that match the filter.
+ if region == '':
+ # Row matches the filter, no further action required.
+ pass
+ else:
+ # We need to hide rows that don't match the filter.
+ worksheet5.set_row(row, options={'hidden': True})
+
+ worksheet5.write_row(row, 0, row_data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+
+###############################################################################
+#
+#
+# Example 6. Autofilter with filter for non-blanks.
+#
+
+# Set the autofilter.
+worksheet6.autofilter('A1:D51')
+
+# Add filter criteria.
+worksheet6.filter_column('A', 'x == NonBlanks')
+
+# Hide the rows that don't match the filter criteria.
+row = 1
+for row_data in (data):
+ region = row_data[0]
+
+ # Check for rows that match the filter.
+ if region != '':
+ # Row matches the filter, no further action required.
+ pass
+ else:
+ # We need to hide rows that don't match the filter.
+ worksheet6.set_row(row, options={'hidden': True})
+
+ worksheet6.write_row(row, 0, row_data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+
+workbook.close()
diff --git a/examples/autofilter_data.txt b/examples/autofilter_data.txt
new file mode 100644
index 0000000..2c970a2
--- /dev/null
+++ b/examples/autofilter_data.txt
@@ -0,0 +1,51 @@
+Region Item Volume Month
+East Apple 9000 July
+East Apple 5000 July
+South Orange 9000 September
+North Apple 2000 November
+West Apple 9000 November
+South Pear 7000 October
+North Pear 9000 August
+West Orange 1000 December
+West Grape 1000 November
+South Pear 10000 April
+West Grape 6000 January
+South Orange 3000 May
+North Apple 3000 December
+South Apple 7000 February
+West Grape 1000 December
+East Grape 8000 February
+South Grape 10000 June
+West Pear 7000 December
+South Apple 2000 October
+East Grape 7000 December
+North Grape 6000 April
+East Pear 8000 February
+North Apple 7000 August
+North Orange 7000 July
+North Apple 6000 June
+South Grape 8000 September
+West Apple 3000 October
+South Orange 10000 November
+West Grape 4000 July
+North Orange 5000 August
+East Orange 1000 November
+East Orange 4000 October
+North Grape 5000 August
+East Apple 1000 December
+South Apple 10000 March
+East Grape 7000 October
+West Grape 1000 September
+East Grape 10000 October
+South Orange 8000 March
+North Apple 4000 July
+South Orange 5000 July
+West Apple 4000 June
+East Apple 5000 April
+North Pear 3000 August
+East Grape 9000 November
+North Orange 8000 October
+East Apple 10000 June
+South Pear 1000 December
+North Grape 10000 July
+East Grape 6000 February
diff --git a/examples/cell_indentation.py b/examples/cell_indentation.py
new file mode 100644
index 0000000..eb464f9
--- /dev/null
+++ b/examples/cell_indentation.py
@@ -0,0 +1,23 @@
+##############################################################################
+#
+# A simple formatting example using XlsxWriter.
+#
+# This program demonstrates the indentation cell format.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('cell_indentation.xlsx')
+
+worksheet = workbook.add_worksheet()
+
+indent1 = workbook.add_format({'indent': 1})
+indent2 = workbook.add_format({'indent': 2})
+
+worksheet.set_column('A:A', 40)
+
+worksheet.write('A1', "This text is indented 1 level", indent1)
+worksheet.write('A2', "This text is indented 2 levels", indent2)
+
+workbook.close()
diff --git a/examples/chart.py b/examples/chart.py
new file mode 100644
index 0000000..ebf6036
--- /dev/null
+++ b/examples/chart.py
@@ -0,0 +1,34 @@
+#######################################################################
+#
+# An example of a simple Excel chart with Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Create a new Chart object.
+chart = workbook.add_chart({'type': 'column'})
+
+# Write some data to add to plot on the chart.
+data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+]
+
+worksheet.write_column('A1', data[0])
+worksheet.write_column('B1', data[1])
+worksheet.write_column('C1', data[2])
+
+# Configure the charts. In simplest case we just add some data series.
+chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+# Insert the chart into the worksheet.
+worksheet.insert_chart('A7', chart)
+
+workbook.close()
diff --git a/examples/chart_area.py b/examples/chart_area.py
new file mode 100644
index 0000000..c5c8984
--- /dev/null
+++ b/examples/chart_area.py
@@ -0,0 +1,119 @@
+#######################################################################
+#
+# An example of creating Excel Area charts with Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_area.xlsx')
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Number', 'Batch 1', 'Batch 2']
+data = [
+ [2, 3, 4, 5, 6, 7],
+ [40, 40, 50, 30, 25, 50],
+ [30, 25, 30, 10, 5, 10],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+worksheet.write_column('C2', data[2])
+
+#######################################################################
+#
+# Create an area chart.
+#
+chart1 = workbook.add_chart({'type': 'area'})
+
+# Configure the first series.
+chart1.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure a second series. Note use of alternative syntax to define ranges.
+chart1.add_series({
+ 'name': ['Sheet1', 0, 2],
+ 'categories': ['Sheet1', 1, 0, 6, 0],
+ 'values': ['Sheet1', 1, 2, 6, 2],
+})
+
+# Add a chart title and some axis labels.
+chart1.set_title ({'name': 'Results of sample analysis'})
+chart1.set_x_axis({'name': 'Test number'})
+chart1.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart1.set_style(11)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D2', chart1, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a stacked area chart sub-type.
+#
+chart2 = workbook.add_chart({'type': 'area', 'subtype': 'stacked'})
+
+# Configure the first series.
+chart2.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series.
+chart2.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title and some axis labels.
+chart2.set_title ({'name': 'Stacked Chart'})
+chart2.set_x_axis({'name': 'Test number'})
+chart2.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart2.set_style(12)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D18', chart2, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a percent stacked area chart sub-type.
+#
+chart3 = workbook.add_chart({'type': 'area', 'subtype': 'percent_stacked'})
+
+# Configure the first series.
+chart3.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series.
+chart3.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title and some axis labels.
+chart3.set_title ({'name': 'Percent Stacked Chart'})
+chart3.set_x_axis({'name': 'Test number'})
+chart3.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart3.set_style(13)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D34', chart3, {'x_offset': 25, 'y_offset': 10})
+
+workbook.close()
diff --git a/examples/chart_bar.py b/examples/chart_bar.py
new file mode 100644
index 0000000..e58d525
--- /dev/null
+++ b/examples/chart_bar.py
@@ -0,0 +1,119 @@
+#######################################################################
+#
+# An example of creating Excel Bar charts with Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_bar.xlsx')
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Number', 'Batch 1', 'Batch 2']
+data = [
+ [2, 3, 4, 5, 6, 7],
+ [10, 40, 50, 20, 10, 50],
+ [30, 60, 70, 50, 40, 30],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+worksheet.write_column('C2', data[2])
+
+#######################################################################
+#
+# Create a new bar chart.
+#
+chart1 = workbook.add_chart({'type': 'bar'})
+
+# Configure the first series.
+chart1.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure a second series. Note use of alternative syntax to define ranges.
+chart1.add_series({
+ 'name': ['Sheet1', 0, 2],
+ 'categories': ['Sheet1', 1, 0, 6, 0],
+ 'values': ['Sheet1', 1, 2, 6, 2],
+})
+
+# Add a chart title and some axis labels.
+chart1.set_title ({'name': 'Results of sample analysis'})
+chart1.set_x_axis({'name': 'Test number'})
+chart1.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart1.set_style(11)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D2', chart1, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a stacked chart sub-type.
+#
+chart2 = workbook.add_chart({'type': 'bar', 'subtype': 'stacked'})
+
+# Configure the first series.
+chart2.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series.
+chart2.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title and some axis labels.
+chart2.set_title ({'name': 'Stacked Chart'})
+chart2.set_x_axis({'name': 'Test number'})
+chart2.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart2.set_style(12)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D18', chart2, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a percentage stacked chart sub-type.
+#
+chart3 = workbook.add_chart({'type': 'bar', 'subtype': 'percent_stacked'})
+
+# Configure the first series.
+chart3.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series.
+chart3.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title and some axis labels.
+chart3.set_title ({'name': 'Percent Stacked Chart'})
+chart3.set_x_axis({'name': 'Test number'})
+chart3.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart3.set_style(13)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D34', chart3, {'x_offset': 25, 'y_offset': 10})
+
+workbook.close()
diff --git a/examples/chart_clustered.py b/examples/chart_clustered.py
new file mode 100644
index 0000000..7b77306
--- /dev/null
+++ b/examples/chart_clustered.py
@@ -0,0 +1,61 @@
+#######################################################################
+#
+# A demo of a clustered category chart in XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from xlsxwriter.workbook import Workbook
+
+workbook = Workbook('chart_clustered.xlsx')
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Types', 'Sub Type', 'Value 1', 'Value 2', 'Value 3']
+data = [
+ ['Type 1', 'Sub Type A', 5000, 8000, 6000],
+ ['', 'Sub Type B', 2000, 3000, 4000],
+ ['', 'Sub Type C', 250, 1000, 2000],
+ ['Type 2', 'Sub Type D', 6000, 6000, 6500],
+ ['', 'Sub Type E', 500, 300, 200],
+]
+
+worksheet.write_row('A1', headings, bold)
+
+for row_num, row_data in enumerate(data):
+ worksheet.write_row(row_num + 1, 0, row_data)
+
+# Create a new chart object. In this case an embedded chart.
+chart = workbook.add_chart({'type': 'column'})
+
+# Configure the series. Note, that the categories are 2D ranges (from column A
+# to column B). This creates the clusters. The series are shown as formula
+# strings for clarity but you can also use the list syntax. See the docs.
+chart.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$B$6',
+ 'values': '=Sheet1!$C$2:$C$6',
+})
+
+chart.add_series({
+ 'name': '=Sheet1!$D$1',
+ 'categories': '=Sheet1!$A$2:$B$6',
+ 'values': '=Sheet1!$D$2:$D$6',
+})
+
+chart.add_series({
+ 'name': '=Sheet1!$E$1',
+ 'categories': '=Sheet1!$A$2:$B$6',
+ 'values': '=Sheet1!$E$2:$E$6',
+})
+
+# Set the Excel chart style.
+chart.set_style(37)
+
+# Turn off the legend.
+chart.set_legend({'position': 'none'})
+
+# Insert the chart into the worksheet.
+worksheet.insert_chart('G3', chart)
+
+workbook.close()
diff --git a/examples/chart_column.py b/examples/chart_column.py
new file mode 100644
index 0000000..99a30af
--- /dev/null
+++ b/examples/chart_column.py
@@ -0,0 +1,119 @@
+#######################################################################
+#
+# An example of creating Excel Column charts with Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_column.xlsx')
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Number', 'Batch 1', 'Batch 2']
+data = [
+ [2, 3, 4, 5, 6, 7],
+ [10, 40, 50, 20, 10, 50],
+ [30, 60, 70, 50, 40, 30],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+worksheet.write_column('C2', data[2])
+
+#######################################################################
+#
+# Create a new column chart.
+#
+chart1 = workbook.add_chart({'type': 'column'})
+
+# Configure the first series.
+chart1.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure a second series. Note use of alternative syntax to define ranges.
+chart1.add_series({
+ 'name': ['Sheet1', 0, 2],
+ 'categories': ['Sheet1', 1, 0, 6, 0],
+ 'values': ['Sheet1', 1, 2, 6, 2],
+})
+
+# Add a chart title and some axis labels.
+chart1.set_title ({'name': 'Results of sample analysis'})
+chart1.set_x_axis({'name': 'Test number'})
+chart1.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart1.set_style(11)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D2', chart1, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a stacked chart sub-type.
+#
+chart2 = workbook.add_chart({'type': 'column', 'subtype': 'stacked'})
+
+# Configure the first series.
+chart2.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series.
+chart2.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title and some axis labels.
+chart2.set_title ({'name': 'Stacked Chart'})
+chart2.set_x_axis({'name': 'Test number'})
+chart2.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart2.set_style(12)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D18', chart2, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a percentage stacked chart sub-type.
+#
+chart3 = workbook.add_chart({'type': 'column', 'subtype': 'percent_stacked'})
+
+# Configure the first series.
+chart3.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series.
+chart3.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title and some axis labels.
+chart3.set_title ({'name': 'Percent Stacked Chart'})
+chart3.set_x_axis({'name': 'Test number'})
+chart3.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart3.set_style(13)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D34', chart3, {'x_offset': 25, 'y_offset': 10})
+
+workbook.close()
diff --git a/examples/chart_combined.py b/examples/chart_combined.py
new file mode 100644
index 0000000..41c3076
--- /dev/null
+++ b/examples/chart_combined.py
@@ -0,0 +1,107 @@
+#######################################################################
+#
+# An example of a Combined chart in XlsxWriter.
+#
+# Copyright 2013, John McNamara, jmcnamara at cpan.org
+#
+from xlsxwriter.workbook import Workbook
+
+workbook = Workbook('chart_combined.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Add a format for the headings.
+bold = workbook.add_format({'bold': True})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Number', 'Batch 1', 'Batch 2']
+data = [
+ [2, 3, 4, 5, 6, 7],
+ [10, 40, 50, 20, 10, 50],
+ [30, 60, 70, 50, 40, 30],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+worksheet.write_column('C2', data[2])
+
+#
+# In the first example we will create a combined column and line chart.
+# They will share the same X and Y axes.
+#
+
+# Create a new column chart. This will use this as the primary chart.
+column_chart1 = workbook.add_chart({'type': 'column'})
+
+# Configure the data series for the primary chart.
+column_chart1.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Create a new column chart. This will use this as the secondary chart.
+line_chart1 = workbook.add_chart({'type': 'line'})
+
+# Configure the data series for the secondary chart.
+line_chart1.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Combine the charts.
+column_chart1.combine(line_chart1)
+
+# Add a chart title and some axis labels. Note, this is done via the
+# primary chart.
+column_chart1.set_title({ 'name': 'Combined chart - same Y axis'})
+column_chart1.set_x_axis({'name': 'Test number'})
+column_chart1.set_y_axis({'name': 'Sample length (mm)'})
+
+# Insert the chart into the worksheet
+worksheet.insert_chart('E2', column_chart1)
+
+#
+# In the second example we will create a similar combined column and line
+# chart except that the secondary chart will have a secondary Y axis.
+#
+
+# Create a new column chart. This will use this as the primary chart.
+column_chart2 = workbook.add_chart({'type': 'column'})
+
+# Configure the data series for the primary chart.
+column_chart2.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Create a new column chart. This will use this as the secondary chart.
+line_chart2 = workbook.add_chart({'type': 'line'})
+
+# Configure the data series for the secondary chart. We also set a
+# secondary Y axis via (y2_axis). This is the only difference between
+# this and the first example, apart from the axis label below.
+line_chart2.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+ 'y2_axis': True,
+})
+
+# Combine the charts.
+column_chart2.combine(line_chart2)
+
+# Add a chart title and some axis labels.
+column_chart2.set_title({ 'name': 'Combine chart - secondary Y axis'})
+column_chart2.set_x_axis({ 'name': 'Test number'})
+column_chart2.set_y_axis({ 'name': 'Sample length (mm)'})
+
+# Note: the y2 properties are on the secondary chart.
+line_chart2.set_y2_axis({'name': 'Target length (mm)'})
+
+# Insert the chart into the worksheet
+worksheet.insert_chart('E18', column_chart2)
+
+workbook.close()
diff --git a/examples/chart_data_table.py b/examples/chart_data_table.py
new file mode 100644
index 0000000..4b2afdc
--- /dev/null
+++ b/examples/chart_data_table.py
@@ -0,0 +1,93 @@
+#######################################################################
+#
+# An example of creating Excel Column charts with data tables using
+# Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_data_table.xlsx')
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Number', 'Batch 1', 'Batch 2']
+data = [
+ [2, 3, 4, 5, 6, 7],
+ [10, 40, 50, 20, 10, 50],
+ [30, 60, 70, 50, 40, 30],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+worksheet.write_column('C2', data[2])
+
+
+#######################################################################
+#
+# Create a column chart with a data table.
+#
+chart1 = workbook.add_chart({'type': 'column'})
+
+# Configure the first series.
+chart1.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series. Note use of alternative syntax to define ranges.
+chart1.add_series({
+ 'name': ['Sheet1', 0, 2],
+ 'categories': ['Sheet1', 1, 0, 6, 0],
+ 'values': ['Sheet1', 1, 2, 6, 2],
+})
+
+# Add a chart title and some axis labels.
+chart1.set_title({'name': 'Chart with Data Table'})
+chart1.set_x_axis({'name': 'Test number'})
+chart1.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set a default data table on the X-Axis.
+chart1.set_table()
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D2', chart1, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a column chart with a data table and legend keys.
+#
+chart2 = workbook.add_chart({'type': 'column'})
+
+# Configure the first series.
+chart2.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series.
+chart2.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title and some axis labels.
+chart2.set_title({'name': 'Data Table with legend keys'})
+chart2.set_x_axis({'name': 'Test number'})
+chart2.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set a data table on the X-Axis with the legend keys shown.
+chart2.set_table({'show_keys': True})
+
+# Hide the chart legend since the keys are shown on the data table.
+chart2.set_legend({'position': 'none'})
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D18', chart2, {'x_offset': 25, 'y_offset': 10})
+
+workbook.close()
diff --git a/examples/chart_data_tools.py b/examples/chart_data_tools.py
new file mode 100644
index 0000000..fb18036
--- /dev/null
+++ b/examples/chart_data_tools.py
@@ -0,0 +1,198 @@
+#######################################################################
+#
+# A demo of an various Excel chart data tools that are available via
+# an XlsxWriter chart.
+#
+# These include, Trendlines, Data Labels, Error Bars, Drop Lines,
+# High-Low Lines and Up-Down Bars.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_data_tools.xlsx')
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Number', 'Data 1', 'Data 2']
+data = [
+ [2, 3, 4, 5, 6, 7],
+ [10, 40, 50, 20, 10, 50],
+ [30, 60, 70, 50, 40, 30],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+worksheet.write_column('C2', data[2])
+
+
+#######################################################################
+#
+# Trendline example.
+#
+# Create a Line chart.
+chart1 = workbook.add_chart({'type': 'line'})
+
+# Configure the first series with a polynomial trendline.
+chart1.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+ 'trendline': {
+ 'type': 'polynomial',
+ 'order': 3,
+ },
+})
+
+# Configure the second series with a moving average trendline.
+chart1.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+ 'trendline': {'type': 'linear'},
+})
+
+# Add a chart title. and some axis labels.
+chart1.set_title({'name': 'Chart with Trendlines'})
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D2', chart1, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Data Labels and Markers example.
+#
+# Create a Line chart.
+chart2 = workbook.add_chart({'type': 'line'})
+
+# Configure the first series.
+chart2.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+ 'data_labels': {'value': 1},
+ 'marker': {'type': 'automatic'},
+})
+
+# Configure the second series.
+chart2.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title. and some axis labels.
+chart2.set_title({'name': 'Chart with Data Labels and Markers'})
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D18', chart2, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Error Bars example.
+#
+# Create a Line chart.
+chart3 = workbook.add_chart({'type': 'line'})
+
+# Configure the first series.
+chart3.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+ 'y_error_bars': {'type': 'standard_error'},
+})
+
+# Configure the second series.
+chart3.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title. and some axis labels.
+chart3.set_title({'name': 'Chart with Error Bars'})
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D34', chart3, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Up-Down Bars example.
+#
+# Create a Line chart.
+chart4 = workbook.add_chart({'type': 'line'})
+
+# Add the Up-Down Bars.
+chart4.set_up_down_bars()
+
+# Configure the first series.
+chart4.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure the second series.
+chart4.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title. and some axis labels.
+chart4.set_title({'name': 'Chart with Up-Down Bars'})
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D50', chart4, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# High-Low Lines example.
+#
+# Create a Line chart.
+chart5 = workbook.add_chart({'type': 'line'})
+
+# Add the High-Low lines.
+chart5.set_high_low_lines()
+
+# Configure the first series.
+chart5.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure the second series.
+chart5.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title. and some axis labels.
+chart5.set_title({'name': 'Chart with High-Low Lines'})
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D66', chart5, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Drop Lines example.
+#
+# Create a Line chart.
+chart6 = workbook.add_chart({'type': 'line'})
+
+# Add Drop Lines.
+chart6.set_drop_lines()
+
+# Configure the first series.
+chart6.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure the second series.
+chart6.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title. and some axis labels.
+chart6.set_title({'name': 'Chart with Drop Lines'})
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D82', chart6, {'x_offset': 25, 'y_offset': 10})
+
+workbook.close()
diff --git a/examples/chart_date_axis.py b/examples/chart_date_axis.py
new file mode 100644
index 0000000..7c32f35
--- /dev/null
+++ b/examples/chart_date_axis.py
@@ -0,0 +1,58 @@
+#######################################################################
+#
+# An example of creating an Excel charts with a date axis using
+# Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from datetime import date
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_date_axis.xlsx')
+
+worksheet = workbook.add_worksheet()
+chart = workbook.add_chart({'type': 'line'})
+date_format = workbook.add_format({'num_format': 'dd/mm/yyyy'})
+
+# Widen the first column to display the dates.
+worksheet.set_column('A:A', 12)
+
+# Some data to be plotted in the worksheet.
+dates = [date(2013, 1, 1),
+ date(2013, 1, 2),
+ date(2013, 1, 3),
+ date(2013, 1, 4),
+ date(2013, 1, 5),
+ date(2013, 1, 6),
+ date(2013, 1, 7),
+ date(2013, 1, 8),
+ date(2013, 1, 9),
+ date(2013, 1, 10)]
+
+values = [10, 30, 20, 40, 20, 60, 50, 40, 30, 30]
+
+# Write the date to the worksheet.
+worksheet.write_column('A1', dates, date_format)
+worksheet.write_column('B1', values)
+
+# Add a series to the chart.
+chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$10',
+ 'values': '=Sheet1!$B$1:$B$10',
+})
+
+# Configure the X axis as a Date axis and set the max and min limits.
+chart.set_x_axis({
+ 'date_axis': True,
+ 'min': date(2013, 1, 2),
+ 'max': date(2013, 1, 9),
+})
+
+# Turn off the legend.
+chart.set_legend({'none': True})
+
+# Insert the chart into the worksheet.
+worksheet.insert_chart('D2', chart)
+
+workbook.close()
diff --git a/examples/chart_doughnut.py b/examples/chart_doughnut.py
new file mode 100644
index 0000000..324712d
--- /dev/null
+++ b/examples/chart_doughnut.py
@@ -0,0 +1,140 @@
+#######################################################################
+#
+# An example of creating Excel Doughnut charts with Python and XlsxWriter.
+#
+# The demo also shows how to set segment colors. It is possible to
+# define chart colors for most types of XlsxWriter charts
+# via the add_series() method. However, Pie/Doughnut charts are a special
+# case since each segment is represented as a point so it is necessary to
+# assign formatting to each point in the series.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_doughnut.xlsx')
+
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Category', 'Values']
+data = [
+ ['Glazed', 'Chocolate', 'Cream'],
+ [50, 35, 15],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+
+#######################################################################
+#
+# Create a new chart object.
+#
+chart1 = workbook.add_chart({'type': 'doughnut'})
+
+# Configure the series. Note the use of the list syntax to define ranges:
+chart1.add_series({
+ 'name': 'Doughnut sales data',
+ 'categories': ['Sheet1', 1, 0, 3, 0],
+ 'values': ['Sheet1', 1, 1, 3, 1],
+})
+
+# Add a title.
+chart1.set_title({'name': 'Popular Doughnut Types'})
+
+# Set an Excel chart style. Colors with white outline and shadow.
+chart1.set_style(10)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('C2', chart1, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a Doughnut chart with user defined segment colors.
+#
+
+# Create an example Doughnut chart like above.
+chart2 = workbook.add_chart({'type': 'doughnut'})
+
+# Configure the series and add user defined segment colors.
+chart2.add_series({
+ 'name': 'Doughnut sales data',
+ 'categories': '=Sheet1!$A$2:$A$4',
+ 'values': '=Sheet1!$B$2:$B$4',
+ 'points': [
+ {'fill': {'color': '#FA58D0'}},
+ {'fill': {'color': '#61210B'}},
+ {'fill': {'color': '#F5F6CE'}},
+ ],
+})
+
+# Add a title.
+chart2.set_title({'name': 'Doughnut Chart with user defined colors'})
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('C18', chart2, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a Doughnut chart with rotation of the segments.
+#
+
+# Create an example Doughnut chart like above.
+chart3 = workbook.add_chart({'type': 'doughnut'})
+
+# Configure the series.
+chart3.add_series({
+ 'name': 'Doughnut sales data',
+ 'categories': '=Sheet1!$A$2:$A$4',
+ 'values': '=Sheet1!$B$2:$B$4',
+})
+
+# Add a title.
+chart3.set_title({'name': 'Doughnut Chart with segment rotation'})
+
+# Change the angle/rotation of the first segment.
+chart3.set_rotation(90)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('C34', chart3, {'x_offset': 25, 'y_offset': 10})
+
+
+#######################################################################
+#
+# Create a Doughnut chart with user defined hole size and other options.
+#
+
+# Create an example Doughnut chart like above.
+chart4 = workbook.add_chart({'type': 'doughnut'})
+
+# Configure the series.
+chart4.add_series({
+ 'name': 'Doughnut sales data',
+ 'categories': '=Sheet1!$A$2:$A$4',
+ 'values': '=Sheet1!$B$2:$B$4',
+ 'points': [
+ {'fill': {'color': '#FA58D0'}},
+ {'fill': {'color': '#61210B'}},
+ {'fill': {'color': '#F5F6CE'}},
+ ],
+})
+
+# Set a 3D style.
+chart4.set_style(26)
+
+# Add a title.
+chart4.set_title({'name': 'Doughnut Chart with options applied'})
+
+# Change the angle/rotation of the first segment.
+chart4.set_rotation(28)
+
+# Change the hole size.
+chart4.set_hole_size(33)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('C50', chart4, {'x_offset': 25, 'y_offset': 10})
+
+
+workbook.close()
diff --git a/examples/chart_gradient.py b/examples/chart_gradient.py
new file mode 100644
index 0000000..b780403
--- /dev/null
+++ b/examples/chart_gradient.py
@@ -0,0 +1,63 @@
+#######################################################################
+#
+# An example of creating an Excel charts with gradient fills using
+# Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_gradient.xlsx')
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Number', 'Batch 1', 'Batch 2']
+data = [
+ [2, 3, 4, 5, 6, 7],
+ [10, 40, 50, 20, 10, 50],
+ [30, 60, 70, 50, 40, 30],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+worksheet.write_column('C2', data[2])
+
+
+# Create a new column chart.
+chart = workbook.add_chart({'type': 'column'})
+
+# Configure the first series, including a gradient.
+chart.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+ 'gradient': {'colors': ['#963735', '#F1DCDB']}
+})
+
+# Configure the second series, including a gradient.
+chart.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+ 'gradient': {'colors': ['#E36C0A', '#FCEADA']}
+})
+
+# Set a gradient for the plotarea.
+chart.set_plotarea({
+ 'gradient': {'colors': ['#FFEFD1', '#F0EBD5', '#B69F66']}
+})
+
+
+# Add some axis labels.
+chart.set_x_axis({'name': 'Test number'})
+chart.set_y_axis({'name': 'Sample length (mm)'})
+
+# Turn off the chart legend.
+chart.set_legend({'none': True})
+
+# Insert the chart into the worksheet.
+worksheet.insert_chart('E2', chart)
+
+workbook.close()
diff --git a/examples/chart_line.py b/examples/chart_line.py
new file mode 100644
index 0000000..8d5419d
--- /dev/null
+++ b/examples/chart_line.py
@@ -0,0 +1,55 @@
+#######################################################################
+#
+# An example of creating Excel Line charts with Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_line.xlsx')
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Number', 'Batch 1', 'Batch 2']
+data = [
+ [2, 3, 4, 5, 6, 7],
+ [10, 40, 50, 20, 10, 50],
+ [30, 60, 70, 50, 40, 30],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+worksheet.write_column('C2', data[2])
+
+# Create a new chart object. In this case an embedded chart.
+chart1 = workbook.add_chart({'type': 'line'})
+
+# Configure the first series.
+chart1.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series. Note use of alternative syntax to define ranges.
+chart1.add_series({
+ 'name': ['Sheet1', 0, 2],
+ 'categories': ['Sheet1', 1, 0, 6, 0],
+ 'values': ['Sheet1', 1, 2, 6, 2],
+})
+
+# Add a chart title and some axis labels.
+chart1.set_title ({'name': 'Results of sample analysis'})
+chart1.set_x_axis({'name': 'Test number'})
+chart1.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style. Colors with white outline and shadow.
+chart1.set_style(10)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D2', chart1, {'x_offset': 25, 'y_offset': 10})
+
+
+workbook.close()
diff --git a/examples/chart_pareto.py b/examples/chart_pareto.py
new file mode 100644
index 0000000..8c0c932
--- /dev/null
+++ b/examples/chart_pareto.py
@@ -0,0 +1,78 @@
+#######################################################################
+#
+# An example of creating of a Pareto chart with Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_pareto.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Formats used in the workbook.
+bold = workbook.add_format({'bold': True})
+percent_format = workbook.add_format({'num_format': '0.0%'})
+
+# Widen the columns for visibility.
+worksheet.set_column('A:A', 15)
+worksheet.set_column('B:C', 10)
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Reason', 'Number', 'Percentage']
+
+reasons = [
+ 'Traffic', 'Child care', 'Public Transport', 'Weather',
+ 'Overslept', 'Emergency',
+]
+
+numbers = [60, 40, 20, 15, 10, 5]
+percents = [0.44, 0.667, 0.8, 0.9, 0.967, 1]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', reasons)
+worksheet.write_column('B2', numbers)
+worksheet.write_column('C2', percents, percent_format)
+
+
+# Create a new column chart. This will be the primary chart.
+column_chart = workbook.add_chart({'type': 'column'})
+
+# Add a series.
+column_chart.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Add a chart title.
+column_chart.set_title({'name': 'Reasons for lateness'})
+
+# Turn off the chart legend.
+column_chart.set_legend({'position': 'none'})
+
+# Set the title and scale of the Y axes. Note, the secondary axis is set from
+# the primary chart.
+column_chart.set_y_axis({
+ 'name': 'Respondents (number)',
+ 'min': 0,
+ 'max': 120
+})
+column_chart.set_y2_axis({'max': 1})
+
+# Create a new line chart. This will be the secondary chart.
+line_chart = workbook.add_chart({'type': 'line'})
+
+# Add a series, on the secondary axis.
+line_chart.add_series({
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+ 'marker': {'type': 'automatic'},
+ 'y2_axis': 1,
+})
+
+# Combine the charts.
+column_chart.combine(line_chart)
+
+# Insert the chart into the worksheet.
+worksheet.insert_chart('F2', column_chart)
+
+workbook.close()
diff --git a/examples/chart_pattern.py b/examples/chart_pattern.py
new file mode 100644
index 0000000..2b569b6
--- /dev/null
+++ b/examples/chart_pattern.py
@@ -0,0 +1,60 @@
+#######################################################################
+#
+# An example of an Excel chart with patterns using Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_pattern.xlsx')
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Shingle', 'Brick']
+data = [
+ [105, 150, 130, 90 ],
+ [50, 120, 100, 110],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+
+# Create a new Chart object.
+chart = workbook.add_chart({'type': 'column'})
+
+# Configure the charts. Add two series with patterns. The gap is used to make
+# the patterns more visible.
+chart.add_series({
+ 'name': '=Sheet1!$A$1',
+ 'values': '=Sheet1!$A$2:$A$5',
+ 'pattern': {
+ 'pattern': 'shingle',
+ 'fg_color': '#804000',
+ 'bg_color': '#c68c53'
+ },
+ 'border': {'color': '#804000'},
+ 'gap': 70,
+})
+
+chart.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'values': '=Sheet1!$B$2:$B$5',
+ 'pattern': {
+ 'pattern': 'horizontal_brick',
+ 'fg_color': '#b30000',
+ 'bg_color': '#ff6666'
+ },
+ 'border': {'color': '#b30000'},
+})
+
+# Add a chart title and some axis labels.
+chart.set_title ({'name': 'Cladding types'})
+chart.set_x_axis({'name': 'Region'})
+chart.set_y_axis({'name': 'Number of houses'})
+
+# Insert the chart into the worksheet.
+worksheet.insert_chart('D2', chart)
+
+workbook.close()
diff --git a/examples/chart_pie.py b/examples/chart_pie.py
new file mode 100644
index 0000000..a7b2c19
--- /dev/null
+++ b/examples/chart_pie.py
@@ -0,0 +1,103 @@
+#######################################################################
+#
+# An example of creating Excel Pie charts with Python and XlsxWriter.
+#
+# The demo also shows how to set segment colors. It is possible to
+# define chart colors for most types of XlsxWriter charts
+# via the add_series() method. However, Pie/Doughnut charts are a special
+# case since each segment is represented as a point so it is necessary to
+# assign formatting to each point in the series.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_pie.xlsx')
+
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Category', 'Values']
+data = [
+ ['Apple', 'Cherry', 'Pecan'],
+ [60, 30, 10],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+
+#######################################################################
+#
+# Create a new chart object.
+#
+chart1 = workbook.add_chart({'type': 'pie'})
+
+# Configure the series. Note the use of the list syntax to define ranges:
+chart1.add_series({
+ 'name': 'Pie sales data',
+ 'categories': ['Sheet1', 1, 0, 3, 0],
+ 'values': ['Sheet1', 1, 1, 3, 1],
+})
+
+# Add a title.
+chart1.set_title({'name': 'Popular Pie Types'})
+
+# Set an Excel chart style. Colors with white outline and shadow.
+chart1.set_style(10)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('C2', chart1, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a Pie chart with user defined segment colors.
+#
+
+# Create an example Pie chart like above.
+chart2 = workbook.add_chart({'type': 'pie'})
+
+# Configure the series and add user defined segment colors.
+chart2.add_series({
+ 'name': 'Pie sales data',
+ 'categories': '=Sheet1!$A$2:$A$4',
+ 'values': '=Sheet1!$B$2:$B$4',
+ 'points': [
+ {'fill': {'color': '#5ABA10'}},
+ {'fill': {'color': '#FE110E'}},
+ {'fill': {'color': '#CA5C05'}},
+ ],
+})
+
+# Add a title.
+chart2.set_title({'name': 'Pie Chart with user defined colors'})
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('C18', chart2, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a Pie chart with rotation of the segments.
+#
+
+# Create an example Pie chart like above.
+chart3 = workbook.add_chart({'type': 'pie'})
+
+# Configure the series.
+chart3.add_series({
+ 'name': 'Pie sales data',
+ 'categories': '=Sheet1!$A$2:$A$4',
+ 'values': '=Sheet1!$B$2:$B$4',
+})
+
+# Add a title.
+chart3.set_title({'name': 'Pie Chart with segment rotation'})
+
+# Change the angle/rotation of the first segment.
+chart3.set_rotation(90)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('C34', chart3, {'x_offset': 25, 'y_offset': 10})
+
+workbook.close()
diff --git a/examples/chart_radar.py b/examples/chart_radar.py
new file mode 100644
index 0000000..1b2606c
--- /dev/null
+++ b/examples/chart_radar.py
@@ -0,0 +1,119 @@
+#######################################################################
+#
+# An example of creating Excel Radar charts with Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_radar.xlsx')
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Number', 'Batch 1', 'Batch 2']
+data = [
+ [2, 3, 4, 5, 6, 7],
+ [30, 60, 70, 50, 40, 30],
+ [25, 40, 50, 30, 50, 40],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+worksheet.write_column('C2', data[2])
+
+#######################################################################
+#
+# Create a new radar chart.
+#
+chart1 = workbook.add_chart({'type': 'radar'})
+
+# Configure the first series.
+chart1.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series. Note use of alternative syntax to define ranges.
+chart1.add_series({
+ 'name': ['Sheet1', 0, 2],
+ 'categories': ['Sheet1', 1, 0, 6, 0],
+ 'values': ['Sheet1', 1, 2, 6, 2],
+})
+
+# Add a chart title and some axis labels.
+chart1.set_title ({'name': 'Results of sample analysis'})
+chart1.set_x_axis({'name': 'Test number'})
+chart1.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart1.set_style(11)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D2', chart1, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a radar chart with markers chart sub-type.
+#
+chart2 = workbook.add_chart({'type': 'radar', 'subtype': 'with_markers'})
+
+# Configure the first series.
+chart2.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series.
+chart2.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title and some axis labels.
+chart2.set_title ({'name': 'Radar Chart With Markers'})
+chart2.set_x_axis({'name': 'Test number'})
+chart2.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart2.set_style(12)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D18', chart2, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a filled radar chart sub-type.
+#
+chart3 = workbook.add_chart({'type': 'radar', 'subtype': 'filled'})
+
+# Configure the first series.
+chart3.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series.
+chart3.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title and some axis labels.
+chart3.set_title ({'name': 'Filled Radar Chart'})
+chart3.set_x_axis({'name': 'Test number'})
+chart3.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart3.set_style(13)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D34', chart3, {'x_offset': 25, 'y_offset': 10})
+
+workbook.close()
diff --git a/examples/chart_scatter.py b/examples/chart_scatter.py
new file mode 100644
index 0000000..ff4283a
--- /dev/null
+++ b/examples/chart_scatter.py
@@ -0,0 +1,186 @@
+#######################################################################
+#
+# An example of creating Excel Scatter charts with Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_scatter.xlsx')
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Number', 'Batch 1', 'Batch 2']
+data = [
+ [2, 3, 4, 5, 6, 7],
+ [10, 40, 50, 20, 10, 50],
+ [30, 60, 70, 50, 40, 30],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+worksheet.write_column('C2', data[2])
+
+
+#######################################################################
+#
+# Create a new scatter chart.
+#
+chart1 = workbook.add_chart({'type': 'scatter'})
+
+# Configure the first series.
+chart1.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series. Note use of alternative syntax to define ranges.
+chart1.add_series({
+ 'name': ['Sheet1', 0, 2],
+ 'categories': ['Sheet1', 1, 0, 6, 0],
+ 'values': ['Sheet1', 1, 2, 6, 2],
+})
+
+# Add a chart title and some axis labels.
+chart1.set_title ({'name': 'Results of sample analysis'})
+chart1.set_x_axis({'name': 'Test number'})
+chart1.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart1.set_style(11)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D2', chart1, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a scatter chart sub-type with straight lines and markers.
+#
+chart2 = workbook.add_chart({'type': 'scatter',
+ 'subtype': 'straight_with_markers'})
+
+# Configure the first series.
+chart2.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series.
+chart2.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title and some axis labels.
+chart2.set_title ({'name': 'Straight line with markers'})
+chart2.set_x_axis({'name': 'Test number'})
+chart2.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart2.set_style(12)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D18', chart2, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a scatter chart sub-type with straight lines and no markers.
+#
+chart3 = workbook.add_chart({'type': 'scatter',
+ 'subtype': 'straight'})
+
+# Configure the first series.
+chart3.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series.
+chart3.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title and some axis labels.
+chart3.set_title ({'name': 'Straight line'})
+chart3.set_x_axis({'name': 'Test number'})
+chart3.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart3.set_style(13)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D34', chart3, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a scatter chart sub-type with smooth lines and markers.
+#
+chart4 = workbook.add_chart({'type': 'scatter',
+ 'subtype': 'smooth_with_markers'})
+
+# Configure the first series.
+chart4.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series.
+chart4.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title and some axis labels.
+chart4.set_title ({'name': 'Smooth line with markers'})
+chart4.set_x_axis({'name': 'Test number'})
+chart4.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart4.set_style(14)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D50', chart4, {'x_offset': 25, 'y_offset': 10})
+
+#######################################################################
+#
+# Create a scatter chart sub-type with smooth lines and no markers.
+#
+chart5 = workbook.add_chart({'type': 'scatter',
+ 'subtype': 'smooth'})
+
+# Configure the first series.
+chart5.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure second series.
+chart5.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$C$2:$C$7',
+})
+
+# Add a chart title and some axis labels.
+chart5.set_title ({'name': 'Smooth line'})
+chart5.set_x_axis({'name': 'Test number'})
+chart5.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart5.set_style(15)
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D66', chart5, {'x_offset': 25, 'y_offset': 10})
+
+workbook.close()
diff --git a/examples/chart_secondary_axis.py b/examples/chart_secondary_axis.py
new file mode 100644
index 0000000..a358322
--- /dev/null
+++ b/examples/chart_secondary_axis.py
@@ -0,0 +1,52 @@
+#######################################################################
+#
+# An example of creating an Excel Line chart with a secondary axis
+# using Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_secondary_axis.xlsx')
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Aliens', 'Humans']
+data = [
+ [2, 3, 4, 5, 6, 7],
+ [10, 40, 50, 20, 10, 50],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+
+
+# Create a new chart object. In this case an embedded chart.
+chart = workbook.add_chart({'type': 'line'})
+
+# Configure a series with a secondary axis
+chart.add_series({
+ 'name': '=Sheet1!$A$1',
+ 'values': '=Sheet1!$A$2:$A$7',
+ 'y2_axis': 1,
+})
+
+chart.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+chart.set_legend({'position': 'right'})
+
+# Add a chart title and some axis labels.
+chart.set_title({'name': 'Survey results'})
+chart.set_x_axis({'name': 'Days', })
+chart.set_y_axis({'name': 'Population', 'major_gridlines': {'visible': 0}})
+chart.set_y2_axis({'name': 'Laser wounds'})
+
+# Insert the chart into the worksheet (with an offset).
+worksheet.insert_chart('D2', chart, {'x_offset': 25, 'y_offset': 10})
+
+workbook.close()
diff --git a/examples/chart_stock.py b/examples/chart_stock.py
new file mode 100644
index 0000000..7816127
--- /dev/null
+++ b/examples/chart_stock.py
@@ -0,0 +1,62 @@
+#######################################################################
+#
+# An example of creating Excel Stock charts with Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from datetime import datetime
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_stock.xlsx')
+worksheet = workbook.add_worksheet()
+
+bold = workbook.add_format({'bold': 1})
+date_format = workbook.add_format({'num_format': 'dd/mm/yyyy'})
+
+chart = workbook.add_chart({'type': 'stock'})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Date', 'High', 'Low', 'Close']
+data = [
+ ['2007-01-01', '2007-01-02', '2007-01-03', '2007-01-04', '2007-01-05'],
+ [27.2, 25.03, 19.05, 20.34, 18.5],
+ [23.49, 19.55, 15.12, 17.84, 16.34],
+ [25.45, 23.05, 17.32, 20.45, 17.34],
+]
+
+worksheet.write_row('A1', headings, bold)
+
+for row in range(5):
+ date = datetime.strptime(data[0][row], "%Y-%m-%d")
+
+ worksheet.write(row + 1, 0, date, date_format)
+ worksheet.write(row + 1, 1, data[1][row])
+ worksheet.write(row + 1, 2, data[2][row])
+ worksheet.write(row + 1, 3, data[3][row])
+
+worksheet.set_column('A:D', 11)
+
+# Add a series for each of the High-Low-Close columns.
+chart.add_series({
+ 'categories': '=Sheet1!$A$2:$A$6',
+ 'values': '=Sheet1!$B$2:$B$6',
+})
+
+chart.add_series({
+ 'categories': '=Sheet1!$A$2:$A$6',
+ 'values': '=Sheet1!$C$2:$C$6',
+})
+
+chart.add_series({
+ 'categories': '=Sheet1!$A$2:$A$6',
+ 'values': '=Sheet1!$D$2:$D$6',
+})
+
+# Add a chart title and some axis labels.
+chart.set_title ({'name': 'High-Low-Close'})
+chart.set_x_axis({'name': 'Date'})
+chart.set_y_axis({'name': 'Share price'})
+
+worksheet.insert_chart('E9', chart)
+
+workbook.close()
diff --git a/examples/chart_styles.py b/examples/chart_styles.py
new file mode 100644
index 0000000..7d984b7
--- /dev/null
+++ b/examples/chart_styles.py
@@ -0,0 +1,42 @@
+#######################################################################
+#
+# An example showing all 48 default chart styles available in Excel 2007
+# using Python and XlsxWriter. Note, these styles are not the same as
+# the styles available in Excel 2013.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chart_styles.xlsx')
+
+# Show the styles for all of these chart types.
+chart_types = ['column', 'area', 'line', 'pie']
+
+for chart_type in chart_types:
+
+ # Add a worksheet for each chart type.
+ worksheet = workbook.add_worksheet(chart_type.title())
+ worksheet.set_zoom(30)
+ style_number = 1
+
+ # Create 48 charts, each with a different style.
+ for row_num in range(0, 90, 15):
+ for col_num in range(0, 64, 8):
+
+ chart = workbook.add_chart({'type': chart_type})
+ chart.add_series({'values': '=Data!$A$1:$A$6'})
+ chart.set_title ({'name': 'Style %d' % style_number})
+ chart.set_legend({'none': True})
+ chart.set_style(style_number)
+
+ worksheet.insert_chart(row_num, col_num , chart)
+ style_number += 1
+
+# Create a worksheet with data for the charts.
+data_worksheet = workbook.add_worksheet('Data')
+data = [10, 40, 50, 20, 10, 50]
+data_worksheet.write_column('A1', data)
+data_worksheet.hide()
+
+workbook.close()
diff --git a/examples/chartsheet.py b/examples/chartsheet.py
new file mode 100644
index 0000000..4149c1f
--- /dev/null
+++ b/examples/chartsheet.py
@@ -0,0 +1,63 @@
+#######################################################################
+#
+# An example of creating an Excel chart in a chartsheet with Python
+# and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('chartsheet.xlsx')
+
+# Add a worksheet to hold the data.
+worksheet = workbook.add_worksheet()
+
+# Add a chartsheet. A worksheet that only holds a chart.
+chartsheet = workbook.add_chartsheet()
+
+# Add a format for the headings.
+bold = workbook.add_format({'bold': 1})
+
+# Add the worksheet data that the charts will refer to.
+headings = ['Number', 'Batch 1', 'Batch 2']
+data = [
+ [2, 3, 4, 5, 6, 7],
+ [10, 40, 50, 20, 10, 50],
+ [30, 60, 70, 50, 40, 30],
+]
+
+worksheet.write_row('A1', headings, bold)
+worksheet.write_column('A2', data[0])
+worksheet.write_column('B2', data[1])
+worksheet.write_column('C2', data[2])
+
+
+# Create a new bar chart.
+chart1 = workbook.add_chart({'type': 'bar'})
+
+# Configure the first series.
+chart1.add_series({
+ 'name': '=Sheet1!$B$1',
+ 'categories': '=Sheet1!$A$2:$A$7',
+ 'values': '=Sheet1!$B$2:$B$7',
+})
+
+# Configure a second series. Note use of alternative syntax to define ranges.
+chart1.add_series({
+ 'name': ['Sheet1', 0, 2],
+ 'categories': ['Sheet1', 1, 0, 6, 0],
+ 'values': ['Sheet1', 1, 2, 6, 2],
+})
+
+# Add a chart title and some axis labels.
+chart1.set_title ({'name': 'Results of sample analysis'})
+chart1.set_x_axis({'name': 'Test number'})
+chart1.set_y_axis({'name': 'Sample length (mm)'})
+
+# Set an Excel chart style.
+chart1.set_style(11)
+
+# Add the chart to the chartsheet.
+chartsheet.set_chart(chart1)
+
+workbook.close()
diff --git a/examples/comments1.py b/examples/comments1.py
new file mode 100644
index 0000000..79bd420
--- /dev/null
+++ b/examples/comments1.py
@@ -0,0 +1,18 @@
+###############################################################################
+#
+# An example of writing cell comments to a worksheet using Python and
+# XlsxWriter.
+#
+# For more advanced comment options see comments2.py.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('comments1.xlsx')
+worksheet = workbook.add_worksheet()
+
+worksheet.write('A1', 'Hello')
+worksheet.write_comment('A1', 'This is a comment')
+
+workbook.close()
diff --git a/examples/comments2.py b/examples/comments2.py
new file mode 100644
index 0000000..874bf3e
--- /dev/null
+++ b/examples/comments2.py
@@ -0,0 +1,261 @@
+###############################################################################
+#
+# An example of writing cell comments to a worksheet using Python and
+# XlsxWriter.
+#
+# Each of the worksheets demonstrates different features of cell comments.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('comments2.xlsx')
+
+worksheet1 = workbook.add_worksheet()
+worksheet2 = workbook.add_worksheet()
+worksheet3 = workbook.add_worksheet()
+worksheet4 = workbook.add_worksheet()
+worksheet5 = workbook.add_worksheet()
+worksheet6 = workbook.add_worksheet()
+worksheet7 = workbook.add_worksheet()
+worksheet8 = workbook.add_worksheet()
+
+text_wrap = workbook.add_format({'text_wrap': 1, 'valign': 'top'})
+
+
+###############################################################################
+#
+# Example 1. Demonstrates a simple cell comments without formatting.
+# comments.
+#
+
+# Set up some formatting.
+worksheet1.set_column('C:C', 25)
+worksheet1.set_row(2, 50)
+worksheet1.set_row(5, 50)
+
+# Simple ASCII string.
+cell_text = 'Hold the mouse over this cell to see the comment.'
+
+comment = 'This is a comment.'
+
+worksheet1.write('C3', cell_text, text_wrap)
+worksheet1.write_comment('C3', comment)
+
+
+###############################################################################
+#
+# Example 2. Demonstrates visible and hidden comments.
+#
+
+# Set up some formatting.
+worksheet2.set_column('C:C', 25)
+worksheet2.set_row(2, 50)
+worksheet2.set_row(5, 50)
+
+cell_text = 'This cell comment is visible.'
+comment = 'Hello.'
+
+worksheet2.write('C3', cell_text, text_wrap)
+worksheet2.write_comment('C3', comment, {'visible': True})
+
+cell_text = "This cell comment isn't visible (the default)."
+
+worksheet2.write('C6', cell_text, text_wrap)
+worksheet2.write_comment('C6', comment)
+
+
+###############################################################################
+#
+# Example 3. Demonstrates visible and hidden comments set at the worksheet
+# level.
+#
+
+# Set up some formatting.
+worksheet3.set_column('C:C', 25)
+worksheet3.set_row(2, 50)
+worksheet3.set_row(5, 50)
+worksheet3.set_row(8, 50)
+
+# Make all comments on the worksheet visible.
+worksheet3.show_comments()
+
+cell_text = 'This cell comment is visible, explicitly.'
+comment = 'Hello.'
+
+worksheet3.write('C3', cell_text, text_wrap)
+worksheet3.write_comment('C3', comment, {'visible': 1})
+
+cell_text = 'This cell comment is also visible because of show_comments().'
+
+worksheet3.write('C6', cell_text, text_wrap)
+worksheet3.write_comment('C6', comment)
+
+cell_text = 'However, we can still override it locally.'
+
+worksheet3.write('C9', cell_text, text_wrap)
+worksheet3.write_comment('C9', comment, {'visible': False})
+
+
+###############################################################################
+#
+# Example 4. Demonstrates changes to the comment box dimensions.
+#
+
+# Set up some formatting.
+worksheet4.set_column('C:C', 25)
+worksheet4.set_row(2, 50)
+worksheet4.set_row(5, 50)
+worksheet4.set_row(8, 50)
+worksheet4.set_row(15, 50)
+
+worksheet4.show_comments()
+
+cell_text = 'This cell comment is default size.'
+comment = 'Hello.'
+
+worksheet4.write('C3', cell_text, text_wrap)
+worksheet4.write_comment('C3', comment)
+
+cell_text = 'This cell comment is twice as wide.'
+
+worksheet4.write('C6', cell_text, text_wrap)
+worksheet4.write_comment('C6', comment, {'x_scale': 2})
+
+cell_text = 'This cell comment is twice as high.'
+
+worksheet4.write('C9', cell_text, text_wrap)
+worksheet4.write_comment('C9', comment, {'y_scale': 2})
+
+cell_text = 'This cell comment is scaled in both directions.'
+
+worksheet4.write('C16', cell_text, text_wrap)
+worksheet4.write_comment('C16', comment, {'x_scale': 1.2, 'y_scale': 0.8})
+
+cell_text = 'This cell comment has width and height specified in pixels.'
+
+worksheet4.write('C19', cell_text, text_wrap)
+worksheet4.write_comment('C19', comment, {'width': 200, 'height': 20})
+
+
+###############################################################################
+#
+# Example 5. Demonstrates changes to the cell comment position.
+#
+worksheet5.set_column('C:C', 25)
+worksheet5.set_row(2, 50)
+worksheet5.set_row(5, 50)
+worksheet5.set_row(8, 50)
+worksheet5.set_row(11, 50)
+
+worksheet5.show_comments()
+
+cell_text = 'This cell comment is in the default position.'
+comment = 'Hello.'
+
+worksheet5.write('C3', cell_text, text_wrap)
+worksheet5.write_comment('C3', comment)
+
+cell_text = 'This cell comment has been moved to another cell.'
+
+worksheet5.write('C6', cell_text, text_wrap)
+worksheet5.write_comment('C6', comment, {'start_cell': 'E4'})
+
+cell_text = 'This cell comment has been moved to another cell.'
+
+worksheet5.write('C9', cell_text, text_wrap)
+worksheet5.write_comment('C9', comment, {'start_row': 8, 'start_col': 4})
+
+cell_text = 'This cell comment has been shifted within its default cell.'
+
+worksheet5.write('C12', cell_text, text_wrap)
+worksheet5.write_comment('C12', comment, {'x_offset': 30, 'y_offset': 12})
+
+
+###############################################################################
+#
+# Example 6. Demonstrates changes to the comment background color.
+#
+worksheet6.set_column('C:C', 25)
+worksheet6.set_row(2, 50)
+worksheet6.set_row(5, 50)
+worksheet6.set_row(8, 50)
+
+worksheet6.show_comments()
+
+cell_text = 'This cell comment has a different color.'
+comment = 'Hello.'
+
+worksheet6.write('C3', cell_text, text_wrap)
+worksheet6.write_comment('C3', comment, {'color': 'green'})
+
+cell_text = 'This cell comment has the default color.'
+
+worksheet6.write('C6', cell_text, text_wrap)
+worksheet6.write_comment('C6', comment)
+
+cell_text = 'This cell comment has a different color.'
+
+worksheet6.write('C9', cell_text, text_wrap)
+worksheet6.write_comment('C9', comment, {'color': '#CCFFCC'})
+
+
+###############################################################################
+#
+# Example 7. Demonstrates how to set the cell comment author.
+#
+worksheet7.set_column('C:C', 30)
+worksheet7.set_row(2, 50)
+worksheet7.set_row(5, 50)
+worksheet7.set_row(8, 50)
+
+author = ''
+cell = 'C3'
+
+cell_text = ("Move the mouse over this cell and you will see 'Cell commented "
+ "by (blank)' in the status bar at the bottom")
+
+comment = 'Hello.'
+
+worksheet7.write(cell, cell_text, text_wrap)
+worksheet7.write_comment(cell, comment)
+
+author = 'Python'
+cell = 'C6'
+cell_text = ("Move the mouse over this cell and you will see 'Cell commented "
+ "by Python' in the status bar at the bottom")
+
+worksheet7.write(cell, cell_text, text_wrap)
+worksheet7.write_comment(cell, comment, {'author': author})
+
+
+###############################################################################
+#
+# Example 8. Demonstrates the need to explicitly set the row height.
+#
+# Set up some formatting.
+worksheet8.set_column('C:C', 25)
+worksheet8.set_row(2, 80)
+
+worksheet8.show_comments()
+
+cell_text = ('The height of this row has been adjusted explicitly using '
+ 'set_row(). The size of the comment box is adjusted '
+ 'accordingly by XlsxWriter.')
+
+comment = 'Hello.'
+
+worksheet8.write('C3', cell_text, text_wrap)
+worksheet8.write_comment('C3', comment)
+
+cell_text = ('The height of this row has been adjusted by Excel due to the '
+ 'text wrap property being set. Unfortunately this means that '
+ 'the height of the row is unknown to XlsxWriter at run time '
+ "and thus the comment box is stretched as well.\n\n"
+ 'Use set_row() to specify the row height explicitly to avoid '
+ 'this problem.')
+
+worksheet8.write('C6', cell_text, text_wrap)
+worksheet8.write_comment('C6', comment)
+
+workbook.close()
diff --git a/examples/conditional_format.py b/examples/conditional_format.py
new file mode 100644
index 0000000..4789e53
--- /dev/null
+++ b/examples/conditional_format.py
@@ -0,0 +1,241 @@
+###############################################################################
+#
+# Example of how to add conditional formatting to an XlsxWriter file.
+#
+# Conditional formatting allows you to apply a format to a cell or a
+# range of cells based on certain criteria.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('conditional_format.xlsx')
+worksheet1 = workbook.add_worksheet()
+worksheet2 = workbook.add_worksheet()
+worksheet3 = workbook.add_worksheet()
+worksheet4 = workbook.add_worksheet()
+worksheet5 = workbook.add_worksheet()
+worksheet6 = workbook.add_worksheet()
+worksheet7 = workbook.add_worksheet()
+worksheet8 = workbook.add_worksheet()
+
+# Add a format. Light red fill with dark red text.
+format1 = workbook.add_format({'bg_color': '#FFC7CE',
+ 'font_color': '#9C0006'})
+
+# Add a format. Green fill with dark green text.
+format2 = workbook.add_format({'bg_color': '#C6EFCE',
+ 'font_color': '#006100'})
+
+# Some sample data to run the conditional formatting against.
+data = [
+ [34, 72, 38, 30, 75, 48, 75, 66, 84, 86],
+ [6, 24, 1, 84, 54, 62, 60, 3, 26, 59],
+ [28, 79, 97, 13, 85, 93, 93, 22, 5, 14],
+ [27, 71, 40, 17, 18, 79, 90, 93, 29, 47],
+ [88, 25, 33, 23, 67, 1, 59, 79, 47, 36],
+ [24, 100, 20, 88, 29, 33, 38, 54, 54, 88],
+ [6, 57, 88, 28, 10, 26, 37, 7, 41, 48],
+ [52, 78, 1, 96, 26, 45, 47, 33, 96, 36],
+ [60, 54, 81, 66, 81, 90, 80, 93, 12, 55],
+ [70, 5, 46, 14, 71, 19, 66, 36, 41, 21],
+]
+
+
+###############################################################################
+#
+# Example 1.
+#
+caption = ('Cells with values >= 50 are in light red. '
+ 'Values < 50 are in light green.')
+
+# Write the data.
+worksheet1.write('A1', caption)
+
+for row, row_data in enumerate(data):
+ worksheet1.write_row(row + 2, 1, row_data)
+
+# Write a conditional format over a range.
+worksheet1.conditional_format('B3:K12', {'type': 'cell',
+ 'criteria': '>=',
+ 'value': 50,
+ 'format': format1})
+
+# Write another conditional format over the same range.
+worksheet1.conditional_format('B3:K12', {'type': 'cell',
+ 'criteria': '<',
+ 'value': 50,
+ 'format': format2})
+
+
+###############################################################################
+#
+# Example 2.
+#
+caption = ('Values between 30 and 70 are in light red. '
+ 'Values outside that range are in light green.')
+
+worksheet2.write('A1', caption)
+
+for row, row_data in enumerate(data):
+ worksheet2.write_row(row + 2, 1, row_data)
+
+worksheet2.conditional_format('B3:K12', {'type': 'cell',
+ 'criteria': 'between',
+ 'minimum': 30,
+ 'maximum': 70,
+ 'format': format1})
+
+worksheet2.conditional_format('B3:K12', {'type': 'cell',
+ 'criteria': 'not between',
+ 'minimum': 30,
+ 'maximum': 70,
+ 'format': format2})
+
+
+###############################################################################
+#
+# Example 3.
+#
+caption = ('Duplicate values are in light red. '
+ 'Unique values are in light green.')
+
+worksheet3.write('A1', caption)
+
+for row, row_data in enumerate(data):
+ worksheet3.write_row(row + 2, 1, row_data)
+
+worksheet3.conditional_format('B3:K12', {'type': 'duplicate',
+ 'format': format1})
+
+worksheet3.conditional_format('B3:K12', {'type': 'unique',
+ 'format': format2})
+
+
+###############################################################################
+#
+# Example 4.
+#
+caption = ('Above average values are in light red. '
+ 'Below average values are in light green.')
+
+worksheet4.write('A1', caption)
+
+for row, row_data in enumerate(data):
+ worksheet4.write_row(row + 2, 1, row_data)
+
+worksheet4.conditional_format('B3:K12', {'type': 'average',
+ 'criteria': 'above',
+ 'format': format1})
+
+worksheet4.conditional_format('B3:K12', {'type': 'average',
+ 'criteria': 'below',
+ 'format': format2})
+
+
+###############################################################################
+#
+# Example 5.
+#
+caption = ('Top 10 values are in light red. '
+ 'Bottom 10 values are in light green.')
+
+worksheet5.write('A1', caption)
+
+for row, row_data in enumerate(data):
+ worksheet5.write_row(row + 2, 1, row_data)
+
+worksheet5.conditional_format('B3:K12', {'type': 'top',
+ 'value': '10',
+ 'format': format1})
+
+worksheet5.conditional_format('B3:K12', {'type': 'bottom',
+ 'value': '10',
+ 'format': format2})
+
+
+###############################################################################
+#
+# Example 6.
+#
+caption = ('Cells with values >= 50 are in light red. '
+ 'Values < 50 are in light green. Non-contiguous ranges.')
+
+# Write the data.
+worksheet6.write('A1', caption)
+
+for row, row_data in enumerate(data):
+ worksheet6.write_row(row + 2, 1, row_data)
+
+# Write a conditional format over a range.
+worksheet6.conditional_format('B3:K6', {'type': 'cell',
+ 'criteria': '>=',
+ 'value': 50,
+ 'format': format1,
+ 'multi_range': 'B3:K6 B9:K12'})
+
+# Write another conditional format over the same range.
+worksheet6.conditional_format('B3:K6', {'type': 'cell',
+ 'criteria': '<',
+ 'value': 50,
+ 'format': format2,
+ 'multi_range': 'B3:K6 B9:K12'})
+
+
+###############################################################################
+#
+# Example 7.
+#
+caption = 'Examples of color scales and data bars. Default colors.'
+
+data = range(1, 13)
+
+worksheet7.write('A1', caption)
+
+worksheet7.write('B2', "2 Color Scale")
+worksheet7.write('D2', "3 Color Scale")
+worksheet7.write('F2', "Data Bars")
+
+for row, row_data in enumerate(data):
+ worksheet7.write(row + 2, 1, row_data)
+ worksheet7.write(row + 2, 3, row_data)
+ worksheet7.write(row + 2, 5, row_data)
+
+worksheet7.conditional_format('B3:B14', {'type': '2_color_scale'})
+worksheet7.conditional_format('D3:D14', {'type': '3_color_scale'})
+worksheet7.conditional_format('F3:F14', {'type': 'data_bar'})
+
+
+###############################################################################
+#
+# Example 8.
+#
+caption = 'Examples of color scales and data bars. Modified colors.'
+
+data = range(1, 13)
+
+
+worksheet8.write('A1', caption)
+
+worksheet8.write('B2', "2 Color Scale")
+worksheet8.write('D2', "3 Color Scale")
+worksheet8.write('F2', "Data Bars")
+
+for row, row_data in enumerate(data):
+ worksheet8.write(row + 2, 1, row_data)
+ worksheet8.write(row + 2, 3, row_data)
+ worksheet8.write(row + 2, 5, row_data)
+
+worksheet8.conditional_format('B3:B14', {'type': '2_color_scale',
+ 'min_color': "#FF0000",
+ 'max_color': "#00FF00"})
+
+worksheet8.conditional_format('D3:D14', {'type': '3_color_scale',
+ 'min_color': "#C5D9F1",
+ 'mid_color': "#8DB4E3",
+ 'max_color': "#538ED5"})
+
+worksheet8.conditional_format('F3:F14', {'type': 'data_bar',
+ 'bar_color': '#63C384'})
+
+workbook.close()
diff --git a/examples/context_manager.py b/examples/context_manager.py
new file mode 100644
index 0000000..9166f3e
--- /dev/null
+++ b/examples/context_manager.py
@@ -0,0 +1,13 @@
+##############################################################################
+#
+# A simple example using the XlsxWriter Python module and the "with" context
+# manager. This doesn't require an explicit close().
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+with xlsxwriter.Workbook('hello_world.xlsx') as workbook:
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Hello world')
diff --git a/examples/data_validate.py b/examples/data_validate.py
new file mode 100644
index 0000000..d82012a
--- /dev/null
+++ b/examples/data_validate.py
@@ -0,0 +1,207 @@
+###############################################################################
+#
+# Example of how to add data validation and dropdown lists to an
+# XlsxWriter file.
+#
+# Data validation is a feature of Excel which allows you to restrict
+# the data that a user enters in a cell and to display help and
+# warning messages. It also allows you to restrict input to values in
+# a drop down list.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from datetime import date, time
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('data_validate.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Add a format for the header cells.
+header_format = workbook.add_format({
+ 'border': 1,
+ 'bg_color': '#C6EFCE',
+ 'bold': True,
+ 'text_wrap': True,
+ 'valign': 'vcenter',
+ 'indent': 1,
+})
+
+# Set up layout of the worksheet.
+worksheet.set_column('A:A', 68)
+worksheet.set_column('B:B', 15)
+worksheet.set_column('D:D', 15)
+worksheet.set_row(0, 36)
+
+# Write the header cells and some data that will be used in the examples.
+heading1 = 'Some examples of data validation in XlsxWriter'
+heading2 = 'Enter values in this column'
+heading3 = 'Sample Data'
+
+worksheet.write('A1', heading1, header_format)
+worksheet.write('B1', heading2, header_format)
+worksheet.write('D1', heading3, header_format)
+
+worksheet.write_row('D3', ['Integers', 1, 10])
+worksheet.write_row('D4', ['List data', 'open', 'high', 'close'])
+worksheet.write_row('D5', ['Formula', '=AND(F5=50,G5=60)', 50, 60])
+
+
+# Example 1. Limiting input to an integer in a fixed range.
+#
+txt = 'Enter an integer between 1 and 10'
+
+worksheet.write('A3', txt)
+worksheet.data_validation('B3', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10})
+
+
+# Example 2. Limiting input to an integer outside a fixed range.
+#
+txt = 'Enter an integer that is not between 1 and 10 (using cell references)'
+
+
+worksheet.write('A5', txt)
+worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'not between',
+ 'minimum': '=E3',
+ 'maximum': '=F3'})
+
+
+# Example 3. Limiting input to an integer greater than a fixed value.
+#
+txt = 'Enter an integer greater than 0'
+
+worksheet.write('A7', txt)
+worksheet.data_validation('B7', {'validate': 'integer',
+ 'criteria': '>',
+ 'value': 0})
+
+
+# Example 4. Limiting input to an integer less than a fixed value.
+#
+txt = 'Enter an integer less than 10'
+
+worksheet.write('A9', txt)
+worksheet.data_validation('B9', {'validate': 'integer',
+ 'criteria': '<',
+ 'value': 10})
+
+
+# Example 5. Limiting input to a decimal in a fixed range.
+#
+txt = 'Enter a decimal between 0.1 and 0.5'
+
+worksheet.write('A11', txt)
+worksheet.data_validation('B11', {'validate': 'decimal',
+ 'criteria': 'between',
+ 'minimum': 0.1,
+ 'maximum': 0.5})
+
+
+# Example 6. Limiting input to a value in a dropdown list.
+#
+txt = 'Select a value from a drop down list'
+
+worksheet.write('A13', txt)
+worksheet.data_validation('B13', {'validate': 'list',
+ 'source': ['open', 'high', 'close']})
+
+
+# Example 7. Limiting input to a value in a dropdown list.
+#
+txt = 'Select a value from a drop down list (using a cell range)'
+
+worksheet.write('A15', txt)
+worksheet.data_validation('B15', {'validate': 'list',
+ 'source': '=$E$4:$G$4'})
+
+
+# Example 8. Limiting input to a date in a fixed range.
+#
+txt = 'Enter a date between 1/1/2008 and 12/12/2008'
+
+worksheet.write('A17', txt)
+worksheet.data_validation('B17', {'validate': 'date',
+ 'criteria': 'between',
+ 'minimum': date(2013, 1, 1),
+ 'maximum': date(2013, 12, 12)})
+
+
+# Example 9. Limiting input to a time in a fixed range.
+#
+txt = 'Enter a time between 6:00 and 12:00'
+
+worksheet.write('A19', txt)
+worksheet.data_validation('B19', {'validate': 'time',
+ 'criteria': 'between',
+ 'minimum': time(6, 0),
+ 'maximum': time(12, 0)})
+
+
+# Example 10. Limiting input to a string greater than a fixed length.
+#
+txt = 'Enter a string longer than 3 characters'
+
+worksheet.write('A21', txt)
+worksheet.data_validation('B21', {'validate': 'length',
+ 'criteria': '>',
+ 'value': 3})
+
+
+# Example 11. Limiting input based on a formula.
+#
+txt = 'Enter a value if the following is true "=AND(F5=50,G5=60)"'
+
+worksheet.write('A23', txt)
+worksheet.data_validation('B23', {'validate': 'custom',
+ 'value': '=AND(F5=50,G5=60)'})
+
+
+# Example 12. Displaying and modifying data validation messages.
+#
+txt = 'Displays a message when you select the cell'
+
+worksheet.write('A25', txt)
+worksheet.data_validation('B25', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 100,
+ 'input_title': 'Enter an integer:',
+ 'input_message': 'between 1 and 100'})
+
+
+# Example 13. Displaying and modifying data validation messages.
+#
+txt = "Display a custom error message when integer isn't between 1 and 100"
+
+worksheet.write('A27', txt)
+worksheet.data_validation('B27', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 100,
+ 'input_title': 'Enter an integer:',
+ 'input_message': 'between 1 and 100',
+ 'error_title': 'Input value is not valid!',
+ 'error_message':
+ 'It should be an integer between 1 and 100'})
+
+
+# Example 14. Displaying and modifying data validation messages.
+#
+txt = "Display a custom info message when integer isn't between 1 and 100"
+
+worksheet.write('A29', txt)
+worksheet.data_validation('B29', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 100,
+ 'input_title': 'Enter an integer:',
+ 'input_message': 'between 1 and 100',
+ 'error_title': 'Input value is not valid!',
+ 'error_message':
+ 'It should be an integer between 1 and 100',
+ 'error_type': 'information'})
+
+workbook.close()
diff --git a/examples/datetimes.py b/examples/datetimes.py
new file mode 100644
index 0000000..e886493
--- /dev/null
+++ b/examples/datetimes.py
@@ -0,0 +1,65 @@
+##############################################################################
+#
+# A simple program to write some dates and times to an Excel file
+# using the XlsxWriter Python module.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from datetime import datetime
+import xlsxwriter
+
+# Create a workbook and add a worksheet.
+workbook = xlsxwriter.Workbook('datetimes.xlsx')
+worksheet = workbook.add_worksheet()
+bold = workbook.add_format({'bold': True})
+
+# Expand the first columns so that the dates are visible.
+worksheet.set_column('A:B', 30)
+
+# Write the column headers.
+worksheet.write('A1', 'Formatted date', bold)
+worksheet.write('B1', 'Format', bold)
+
+# Create a datetime object to use in the examples.
+
+date_time = datetime.strptime('2013-01-23 12:30:05.123',
+ '%Y-%m-%d %H:%M:%S.%f')
+
+# Examples date and time formats. In the output file compare how changing
+# the format codes change the appearance of the date.
+date_formats = (
+ 'dd/mm/yy',
+ 'mm/dd/yy',
+ 'dd m yy',
+ 'd mm yy',
+ 'd mmm yy',
+ 'd mmmm yy',
+ 'd mmmm yyy',
+ 'd mmmm yyyy',
+ 'dd/mm/yy hh:mm',
+ 'dd/mm/yy hh:mm:ss',
+ 'dd/mm/yy hh:mm:ss.000',
+ 'hh:mm',
+ 'hh:mm:ss',
+ 'hh:mm:ss.000',
+)
+
+# Start from first row after headers.
+row = 1
+
+# Write the same date and time using each of the above formats.
+for date_format_str in date_formats:
+
+ # Create a format for the date or time.
+ date_format = workbook.add_format({'num_format': date_format_str,
+ 'align': 'left'})
+
+ # Write the same date using different formats.
+ worksheet.write_datetime(row, 0, date_time, date_format)
+
+ # Also write the format string for comparison.
+ worksheet.write_string(row, 1, date_format_str)
+
+ row += 1
+
+workbook.close()
diff --git a/examples/defined_name.py b/examples/defined_name.py
new file mode 100644
index 0000000..ee2ab67
--- /dev/null
+++ b/examples/defined_name.py
@@ -0,0 +1,33 @@
+##############################################################################
+#
+# Example of how to create defined names with the XlsxWriter Python module.
+#
+# This method is used to define a user friendly name to represent a value,
+# a single cell or a range of cells in a workbook.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+
+workbook = xlsxwriter.Workbook('defined_name.xlsx')
+worksheet1 = workbook.add_worksheet()
+worksheet2 = workbook.add_worksheet()
+
+# Define some global/workbook names.
+workbook.define_name('Exchange_rate', '=0.96')
+workbook.define_name('Sales', '=Sheet1!$G$1:$H$10')
+
+# Define a local/worksheet name. Over-rides the "Sales" name above.
+workbook.define_name('Sheet2!Sales', '=Sheet2!$G$1:$G$10')
+
+# Write some text in the file and one of the defined names in a formula.
+for worksheet in workbook.worksheets():
+ worksheet.set_column('A:A', 45)
+ worksheet.write('A1', 'This worksheet contains some defined names.')
+ worksheet.write('A2', 'See Formulas -> Name Manager above.')
+ worksheet.write('A3', 'Example formula in cell B3 ->')
+
+ worksheet.write('B3', '=Exchange_rate')
+
+workbook.close()
diff --git a/examples/demo.py b/examples/demo.py
new file mode 100644
index 0000000..86fcc7b
--- /dev/null
+++ b/examples/demo.py
@@ -0,0 +1,33 @@
+##############################################################################
+#
+# A simple example of some of the features of the XlsxWriter Python module.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+
+# Create an new Excel file and add a worksheet.
+workbook = xlsxwriter.Workbook('demo.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Widen the first column to make the text clearer.
+worksheet.set_column('A:A', 20)
+
+# Add a bold format to use to highlight cells.
+bold = workbook.add_format({'bold': True})
+
+# Write some simple text.
+worksheet.write('A1', 'Hello')
+
+# Text with formatting.
+worksheet.write('A2', 'World', bold)
+
+# Write some numbers, with row/column notation.
+worksheet.write(2, 0, 123)
+worksheet.write(3, 0, 123.456)
+
+# Insert an image.
+worksheet.insert_image('B5', 'logo.png')
+
+workbook.close()
diff --git a/examples/diagonal_border.py b/examples/diagonal_border.py
new file mode 100644
index 0000000..1f4fdf7
--- /dev/null
+++ b/examples/diagonal_border.py
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# A simple formatting example that demonstrates how to add diagonal cell
+# borders with XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('diag_border.xlsx')
+worksheet = workbook.add_worksheet()
+
+format1 = workbook.add_format({'diag_type': 1})
+format2 = workbook.add_format({'diag_type': 2})
+format3 = workbook.add_format({'diag_type': 3})
+
+format4 = workbook.add_format({
+ 'diag_type': 3,
+ 'diag_border': 7,
+ 'diag_color': 'red',
+})
+
+worksheet.write('B3', 'Text', format1)
+worksheet.write('B6', 'Text', format2)
+worksheet.write('B9', 'Text', format3)
+worksheet.write('B12', 'Text', format4)
+
+workbook.close()
diff --git a/examples/doc_properties.py b/examples/doc_properties.py
new file mode 100644
index 0000000..4a57aa8
--- /dev/null
+++ b/examples/doc_properties.py
@@ -0,0 +1,27 @@
+##############################################################################
+#
+# An example of adding document properties to a XlsxWriter file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('doc_properties.xlsx')
+worksheet = workbook.add_worksheet()
+
+workbook.set_properties({
+ 'title': 'This is an example spreadsheet',
+ 'subject': 'With document properties',
+ 'author': 'John McNamara',
+ 'manager': 'Dr. Heinz Doofenshmirtz',
+ 'company': 'of Wolves',
+ 'category': 'Example spreadsheets',
+ 'keywords': 'Sample, Example, Properties',
+ 'comments': 'Created with Python and XlsxWriter',
+ 'status': 'Quo',
+})
+
+worksheet.set_column('A:A', 70)
+worksheet.write('A1', "Select 'Workbook Properties' to see properties.")
+
+workbook.close()
diff --git a/examples/headers_footers.py b/examples/headers_footers.py
new file mode 100644
index 0000000..58597a6
--- /dev/null
+++ b/examples/headers_footers.py
@@ -0,0 +1,128 @@
+######################################################################
+#
+# This program shows several examples of how to set up headers and
+# footers with XlsxWriter.
+#
+# The control characters used in the header/footer strings are:
+#
+# Control Category Description
+# ======= ======== ===========
+# &L Justification Left
+# &C Center
+# &R Right
+#
+# &P Information Page number
+# &N Total number of pages
+# &D Date
+# &T Time
+# &F File name
+# &A Worksheet name
+#
+# &fontsize Font Font size
+# &"font,style" Font name and style
+# &U Single underline
+# &E Double underline
+# &S Strikethrough
+# &X Superscript
+# &Y Subscript
+#
+# &[Picture] Images Image placeholder
+# &G Same as &[Picture]
+#
+# && Miscellaneous Literal ampersand &
+#
+# See the main XlsxWriter documentation for more information.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('headers_footers.xlsx')
+preview = 'Select Print Preview to see the header and footer'
+
+######################################################################
+#
+# A simple example to start
+#
+worksheet1 = workbook.add_worksheet('Simple')
+header1 = '&CHere is some centered text.'
+footer1 = '&LHere is some left aligned text.'
+
+worksheet1.set_header(header1)
+worksheet1.set_footer(footer1)
+
+worksheet1.set_column('A:A', 50)
+worksheet1.write('A1', preview)
+
+
+######################################################################
+#
+# Insert a header image.
+#
+worksheet2 = workbook.add_worksheet('Image')
+header2 = '&L&G'
+
+# Adjust the page top margin to allow space for the header image.
+worksheet2.set_margins(top=1.3)
+
+worksheet2.set_header(header2, {'image_left': 'python-200x80.png'})
+
+worksheet2.set_column('A:A', 50)
+worksheet2.write('A1', preview)
+
+
+######################################################################
+#
+# This is an example of some of the header/footer variables.
+#
+worksheet3 = workbook.add_worksheet('Variables')
+header3 = '&LPage &P of &N' + '&CFilename: &F' + '&RSheetname: &A'
+footer3 = '&LCurrent date: &D' + '&RCurrent time: &T'
+
+worksheet3.set_header(header3)
+worksheet3.set_footer(footer3)
+
+worksheet3.set_column('A:A', 50)
+worksheet3.write('A1', preview)
+worksheet3.write('A21', 'Next sheet')
+worksheet3.set_h_pagebreaks([20])
+
+######################################################################
+#
+# This example shows how to use more than one font
+#
+worksheet4 = workbook.add_worksheet('Mixed fonts')
+header4 = '&C&"Courier New,Bold"Hello &"Arial,Italic"World'
+footer4 = '&C&"Symbol"e&"Arial" = mc&X2'
+
+worksheet4.set_header(header4)
+worksheet4.set_footer(footer4)
+
+worksheet4.set_column('A:A', 50)
+worksheet4.write('A1', preview)
+
+######################################################################
+#
+# Example of line wrapping
+#
+worksheet5 = workbook.add_worksheet('Word wrap')
+header5 = "&CHeading 1\nHeading 2"
+
+worksheet5.set_header(header5)
+
+worksheet5.set_column('A:A', 50)
+worksheet5.write('A1', preview)
+
+######################################################################
+#
+# Example of inserting a literal ampersand &
+#
+worksheet6 = workbook.add_worksheet('Ampersand')
+header6 = '&CCuriouser && Curiouser - Attorneys at Law'
+
+worksheet6.set_header(header6)
+
+worksheet6.set_column('A:A', 50)
+worksheet6.write('A1', preview)
+
+workbook.close()
diff --git a/examples/hello_world.py b/examples/hello_world.py
new file mode 100644
index 0000000..94ced7c
--- /dev/null
+++ b/examples/hello_world.py
@@ -0,0 +1,14 @@
+##############################################################################
+#
+# A hello world spreadsheet using the XlsxWriter Python module.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('hello_world.xlsx')
+worksheet = workbook.add_worksheet()
+
+worksheet.write('A1', 'Hello world')
+
+workbook.close()
diff --git a/examples/hide_row_col.py b/examples/hide_row_col.py
new file mode 100644
index 0000000..67802af
--- /dev/null
+++ b/examples/hide_row_col.py
@@ -0,0 +1,29 @@
+###############################################################################
+#
+# Example of how to hide rows and columns in XlsxWriter. In order to
+# hide rows without setting each one, (of approximately 1 million rows),
+# Excel uses an optimizations to hide all rows that don't have data.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('hide_row_col.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Write some data.
+worksheet.write('D1', 'Some hidden columns.')
+worksheet.write('A8', 'Some hidden rows.')
+
+# Hide all rows without data.
+worksheet.set_default_row(hide_unused_rows=True)
+
+# Set the height of empty rows that we do want to display even if it is
+# the default height.
+for row in range(1, 7):
+ worksheet.set_row(row, 15)
+
+# Columns can be hidden explicitly. This doesn't increase the file size..
+worksheet.set_column('G:XFD', None, None, {'hidden': True})
+
+workbook.close()
diff --git a/examples/hide_sheet.py b/examples/hide_sheet.py
new file mode 100644
index 0000000..5387894
--- /dev/null
+++ b/examples/hide_sheet.py
@@ -0,0 +1,25 @@
+#######################################################################
+#
+# Example of how to hide a worksheet with XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('hide_sheet.xlsx')
+worksheet1 = workbook.add_worksheet()
+worksheet2 = workbook.add_worksheet()
+worksheet3 = workbook.add_worksheet()
+
+worksheet1.set_column('A:A', 30)
+worksheet2.set_column('A:A', 30)
+worksheet3.set_column('A:A', 30)
+
+# Hide Sheet2. It won't be visible until it is unhidden in Excel.
+worksheet2.hide()
+
+worksheet1.write('A1', 'Sheet2 is hidden')
+worksheet2.write('A1', "Now it's my turn to find you!")
+worksheet3.write('A1', 'Sheet2 is hidden')
+
+workbook.close()
diff --git a/examples/http_server_py2.py b/examples/http_server_py2.py
new file mode 100644
index 0000000..aa75b10
--- /dev/null
+++ b/examples/http_server_py2.py
@@ -0,0 +1,53 @@
+##############################################################################
+#
+# Example of using Python and XlsxWriter to create an Excel XLSX file in an in
+# memory string suitable for serving via SimpleHTTPServer or Django or with
+# the Google App Engine.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Note: This is a Python 2 example. For Python 3 see http_server_py3.py.
+
+import SimpleHTTPServer
+import SocketServer
+import StringIO
+
+import xlsxwriter
+
+
+class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+
+ def do_GET(self):
+ # Create an in-memory output file for the new workbook.
+ output = StringIO.StringIO()
+
+ # Even though the final file will be in memory the module uses temp
+ # files during assembly for efficiency. To avoid this on servers that
+ # don't allow temp files, for example the Google APP Engine, set the
+ # 'in_memory' constructor option to True:
+ workbook = xlsxwriter.Workbook(output, {'in_memory': True})
+ worksheet = workbook.add_worksheet()
+
+ # Write some test data.
+ worksheet.write(0, 0, 'Hello, world!')
+
+ # Close the workbook before streaming the data.
+ workbook.close()
+
+ # Rewind the buffer.
+ output.seek(0)
+
+ # Construct a server response.
+ self.send_response(200)
+ self.send_header('Content-Disposition', 'attachment; filename=test.xlsx')
+ self.send_header('Content-type',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+ self.end_headers()
+ self.wfile.write(output.read())
+ return
+
+
+print('Server listening on port 8000...')
+httpd = SocketServer.TCPServer(('', 8000), Handler)
+httpd.serve_forever()
diff --git a/examples/http_server_py3.py b/examples/http_server_py3.py
new file mode 100644
index 0000000..838e487
--- /dev/null
+++ b/examples/http_server_py3.py
@@ -0,0 +1,53 @@
+##############################################################################
+#
+# Example of using Python and XlsxWriter to create an Excel XLSX file in an in
+# memory string suitable for serving via SimpleHTTPRequestHandler or Django or
+# with the Google App Engine.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Note: This is a Python 3 example. For Python 2 see http_server_py2.py.
+
+import http.server
+import socketserver
+import io
+
+import xlsxwriter
+
+
+class Handler(http.server.SimpleHTTPRequestHandler):
+
+ def do_GET(self):
+ # Create an in-memory output file for the new workbook.
+ output = io.BytesIO()
+
+ # Even though the final file will be in memory the module uses temp
+ # files during assembly for efficiency. To avoid this on servers that
+ # don't allow temp files, for example the Google APP Engine, set the
+ # 'in_memory' constructor option to True:
+ workbook = xlsxwriter.Workbook(output, {'in_memory': True})
+ worksheet = workbook.add_worksheet()
+
+ # Write some test data.
+ worksheet.write(0, 0, 'Hello, world!')
+
+ # Close the workbook before streaming the data.
+ workbook.close()
+
+ # Rewind the buffer.
+ output.seek(0)
+
+ # Construct a server response.
+ self.send_response(200)
+ self.send_header('Content-Disposition', 'attachment; filename=test.xlsx')
+ self.send_header('Content-type',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+ self.end_headers()
+ self.wfile.write(output.read())
+ return
+
+
+print('Server listening on port 8000...')
+httpd = socketserver.TCPServer(('', 8000), Handler)
+httpd.serve_forever()
diff --git a/examples/hyperlink.py b/examples/hyperlink.py
new file mode 100644
index 0000000..8cd4d36
--- /dev/null
+++ b/examples/hyperlink.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Example of how to use the XlsxWriter module to write hyperlinks
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+# Create a new workbook and add a worksheet
+workbook = xlsxwriter.Workbook('hyperlink.xlsx')
+worksheet = workbook.add_worksheet('Hyperlinks')
+
+# Format the first column
+worksheet.set_column('A:A', 30)
+
+# Add the standard url link format.
+url_format = workbook.add_format({
+ 'font_color': 'blue',
+ 'underline': 1
+})
+
+# Add a sample alternative link format.
+red_format = workbook.add_format({
+ 'font_color': 'red',
+ 'bold': 1,
+ 'underline': 1,
+ 'font_size': 12,
+})
+
+# Add an alternate description string to the URL.
+string = 'Python home'
+
+# Add a "tool tip" to the URL.
+tip = 'Get the latest Python news here.'
+
+# Write some hyperlinks
+worksheet.write_url('A1', 'http://www.python.org/') # Implicit format.
+worksheet.write_url('A3', 'http://www.python.org/', url_format, string)
+worksheet.write_url('A5', 'http://www.python.org/', url_format, string, tip)
+worksheet.write_url('A7', 'http://www.python.org/', red_format)
+worksheet.write_url('A9', 'mailto:jmcnamara at cpan.org', url_format, 'Mail me')
+
+# Write a URL that isn't a hyperlink
+worksheet.write_string('A11', 'http://www.python.org/')
+
+workbook.close()
diff --git a/examples/images.py b/examples/images.py
new file mode 100644
index 0000000..3acf895
--- /dev/null
+++ b/examples/images.py
@@ -0,0 +1,30 @@
+##############################################################################
+#
+# An example of inserting images into a worksheet using the XlsxWriter
+# Python module.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+
+# Create an new Excel file and add a worksheet.
+workbook = xlsxwriter.Workbook('images.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Widen the first column to make the text clearer.
+worksheet.set_column('A:A', 30)
+
+# Insert an image.
+worksheet.write('A2', 'Insert an image in a cell:')
+worksheet.insert_image('B2', 'python.png')
+
+# Insert an image offset in the cell.
+worksheet.write('A12', 'Insert an image with an offset:')
+worksheet.insert_image('B12', 'python.png', {'x_offset': 15, 'y_offset': 10})
+
+# Insert an image with scaling.
+worksheet.write('A23', 'Insert a scaled image:')
+worksheet.insert_image('B23', 'python.png', {'x_scale': 0.5, 'y_scale': 0.5})
+
+workbook.close()
diff --git a/examples/images_bytesio.py b/examples/images_bytesio.py
new file mode 100644
index 0000000..f836226
--- /dev/null
+++ b/examples/images_bytesio.py
@@ -0,0 +1,50 @@
+##############################################################################
+#
+# An example of inserting images from a Python BytesIO byte stream into a
+# worksheet using the XlsxWriter module.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Import the byte stream handler.
+from io import BytesIO
+
+# Import urlopen() for either Python 2 or 3.
+try:
+ from urllib.request import urlopen
+except ImportError:
+ from urllib2 import urlopen
+
+
+import xlsxwriter
+
+# Create the workbook and add a worksheet.
+workbook = xlsxwriter.Workbook('images_bytesio.xlsx')
+worksheet = workbook.add_worksheet()
+
+
+# Read an image from a remote url.
+url = 'https://raw.githubusercontent.com/jmcnamara/XlsxWriter/' + \
+ 'master/examples/logo.png'
+
+image_data = BytesIO(urlopen(url).read())
+
+# Write the byte stream image to a cell. Note, the filename must be
+# specified. In this case it will be read from url string.
+worksheet.insert_image('B2', url, {'image_data': image_data})
+
+
+# Read a local image file into a a byte stream. Note, the insert_image()
+# method can do this directly. This is for illustration purposes only.
+filename = 'python.png'
+
+image_file = open(filename, 'rb')
+image_data = BytesIO(image_file.read())
+image_file.close()
+
+
+# Write the byte stream image to a cell. The filename must be specified.
+worksheet.insert_image('B8', filename, {'image_data': image_data})
+
+
+workbook.close()
diff --git a/examples/logo.png b/examples/logo.png
new file mode 100644
index 0000000..ed01e53
Binary files /dev/null and b/examples/logo.png differ
diff --git a/examples/macros.py b/examples/macros.py
new file mode 100644
index 0000000..d169562
--- /dev/null
+++ b/examples/macros.py
@@ -0,0 +1,33 @@
+#######################################################################
+#
+# An example of adding macros to an XlsxWriter file using a VBA project
+# file extracted from an existing Excel xlsm file.
+#
+# The vba_extract.py utility supplied with XlsxWriter can be used to extract
+# the vbaProject.bin file.
+#
+# An embedded macro is connected to a form button on the worksheet.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+# Note the file extension should be .xlsm.
+workbook = xlsxwriter.Workbook('macros.xlsm')
+worksheet = workbook.add_worksheet()
+
+worksheet.set_column('A:A', 30)
+
+# Add the VBA project binary.
+workbook.add_vba_project('./vbaProject.bin')
+
+# Show text for the end user.
+worksheet.write('A3', 'Press the button to say hello.')
+
+# Add a button tied to a macro in the VBA project.
+worksheet.insert_button('B3', {'macro': 'say_hello',
+ 'caption': 'Press Me',
+ 'width': 80,
+ 'height': 30})
+
+workbook.close()
diff --git a/examples/merge1.py b/examples/merge1.py
new file mode 100644
index 0000000..568e0b4
--- /dev/null
+++ b/examples/merge1.py
@@ -0,0 +1,37 @@
+##############################################################################
+#
+# A simple example of merging cells with the XlsxWriter Python module.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+
+# Create an new Excel file and add a worksheet.
+workbook = xlsxwriter.Workbook('merge1.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Increase the cell size of the merged cells to highlight the formatting.
+worksheet.set_column('B:D', 12)
+worksheet.set_row(3, 30)
+worksheet.set_row(6, 30)
+worksheet.set_row(7, 30)
+
+
+# Create a format to use in the merged range.
+merge_format = workbook.add_format({
+ 'bold': 1,
+ 'border': 1,
+ 'align': 'center',
+ 'valign': 'vcenter',
+ 'fg_color': 'yellow'})
+
+
+# Merge 3 cells.
+worksheet.merge_range('B4:D4', 'Merged Range', merge_format)
+
+# Merge 3 cells over two rows.
+worksheet.merge_range('B7:D8', 'Merged Range', merge_format)
+
+
+workbook.close()
diff --git a/examples/merge_rich_string.py b/examples/merge_rich_string.py
new file mode 100644
index 0000000..aaaa18f
--- /dev/null
+++ b/examples/merge_rich_string.py
@@ -0,0 +1,34 @@
+##############################################################################
+#
+# An example of merging cells which contain a rich string using the
+# XlsxWriter Python module.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+
+# Create an new Excel file and add a worksheet.
+workbook = xlsxwriter.Workbook('merge_rich_string.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Set up some formats to use.
+red = workbook.add_format({'color': 'red'})
+blue = workbook.add_format({'color': 'blue'})
+cell_format = workbook.add_format({'align': 'center',
+ 'valign': 'vcenter',
+ 'border': 1})
+
+# We can only write simple types to merged ranges so we write a blank string.
+worksheet.merge_range('B2:E5', "", cell_format)
+
+# We then overwrite the first merged cell with a rich string. Note that we
+# must also pass the cell format used in the merged cells format at the end.
+worksheet.write_rich_string('B2',
+ 'This is ',
+ red, 'red',
+ ' and this is ',
+ blue, 'blue',
+ cell_format)
+
+workbook.close()
diff --git a/examples/outline.py b/examples/outline.py
new file mode 100644
index 0000000..be10bae
--- /dev/null
+++ b/examples/outline.py
@@ -0,0 +1,190 @@
+###############################################################################
+#
+# Example of how use Python and XlsxWriter to generate Excel outlines and
+# grouping.
+#
+# Excel allows you to group rows or columns so that they can be hidden or
+# displayed with a single mouse click. This feature is referred to as outlines.
+#
+# Outlines can reduce complex data down to a few salient sub-totals or
+# summaries.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+# Create a new workbook and add some worksheets
+workbook = xlsxwriter.Workbook('outline.xlsx')
+worksheet1 = workbook.add_worksheet('Outlined Rows')
+worksheet2 = workbook.add_worksheet('Collapsed Rows')
+worksheet3 = workbook.add_worksheet('Outline Columns')
+worksheet4 = workbook.add_worksheet('Outline levels')
+
+# Add a general format
+bold = workbook.add_format({'bold': 1})
+
+
+###############################################################################
+#
+# Example 1: A worksheet with outlined rows. It also includes SUBTOTAL()
+# functions so that it looks like the type of automatic outlines that are
+# generated when you use the Excel Data->SubTotals menu item.
+#
+# For outlines the important parameters are 'level' and 'hidden'. Rows with
+# the same 'level' are grouped together. The group will be collapsed if
+# 'hidden' is enabled. The parameters 'height' and 'cell_format' are assigned
+# default values if they are None.
+#
+worksheet1.set_row(1, None, None, {'level': 2})
+worksheet1.set_row(2, None, None, {'level': 2})
+worksheet1.set_row(3, None, None, {'level': 2})
+worksheet1.set_row(4, None, None, {'level': 2})
+worksheet1.set_row(5, None, None, {'level': 1})
+
+worksheet1.set_row(6, None, None, {'level': 2})
+worksheet1.set_row(7, None, None, {'level': 2})
+worksheet1.set_row(8, None, None, {'level': 2})
+worksheet1.set_row(9, None, None, {'level': 2})
+worksheet1.set_row(10, None, None, {'level': 1})
+
+# Adjust the column width for clarity
+worksheet1.set_column('A:A', 20)
+
+# Add the data, labels and formulas
+worksheet1.write('A1', 'Region', bold)
+worksheet1.write('A2', 'North')
+worksheet1.write('A3', 'North')
+worksheet1.write('A4', 'North')
+worksheet1.write('A5', 'North')
+worksheet1.write('A6', 'North Total', bold)
+
+worksheet1.write('B1', 'Sales', bold)
+worksheet1.write('B2', 1000)
+worksheet1.write('B3', 1200)
+worksheet1.write('B4', 900)
+worksheet1.write('B5', 1200)
+worksheet1.write('B6', '=SUBTOTAL(9,B2:B5)', bold)
+
+worksheet1.write('A7', 'South')
+worksheet1.write('A8', 'South')
+worksheet1.write('A9', 'South')
+worksheet1.write('A10', 'South')
+worksheet1.write('A11', 'South Total', bold)
+
+worksheet1.write('B7', 400)
+worksheet1.write('B8', 600)
+worksheet1.write('B9', 500)
+worksheet1.write('B10', 600)
+worksheet1.write('B11', '=SUBTOTAL(9,B7:B10)', bold)
+
+worksheet1.write('A12', 'Grand Total', bold)
+worksheet1.write('B12', '=SUBTOTAL(9,B2:B10)', bold)
+
+
+###############################################################################
+#
+# Example 2: A worksheet with outlined rows. This is the same as the
+# previous example except that the rows are collapsed.
+# Note: We need to indicate the rows that contains the collapsed symbol '+'
+# with the optional parameter, 'collapsed'. The group will be then be
+# collapsed if 'hidden' is True.
+#
+worksheet2.set_row(1, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(2, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(3, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(4, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(5, None, None, {'level': 1, 'hidden': True})
+
+worksheet2.set_row(6, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(7, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(8, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(9, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(10, None, None, {'level': 1, 'hidden': True})
+worksheet2.set_row(11, None, None, {'collapsed': True})
+
+# Adjust the column width for clarity
+worksheet2.set_column('A:A', 20)
+
+# Add the data, labels and formulas
+worksheet2.write('A1', 'Region', bold)
+worksheet2.write('A2', 'North')
+worksheet2.write('A3', 'North')
+worksheet2.write('A4', 'North')
+worksheet2.write('A5', 'North')
+worksheet2.write('A6', 'North Total', bold)
+
+worksheet2.write('B1', 'Sales', bold)
+worksheet2.write('B2', 1000)
+worksheet2.write('B3', 1200)
+worksheet2.write('B4', 900)
+worksheet2.write('B5', 1200)
+worksheet2.write('B6', '=SUBTOTAL(9,B2:B5)', bold)
+
+worksheet2.write('A7', 'South')
+worksheet2.write('A8', 'South')
+worksheet2.write('A9', 'South')
+worksheet2.write('A10', 'South')
+worksheet2.write('A11', 'South Total', bold)
+
+worksheet2.write('B7', 400)
+worksheet2.write('B8', 600)
+worksheet2.write('B9', 500)
+worksheet2.write('B10', 600)
+worksheet2.write('B11', '=SUBTOTAL(9,B7:B10)', bold)
+
+worksheet2.write('A12', 'Grand Total', bold)
+worksheet2.write('B12', '=SUBTOTAL(9,B2:B10)', bold)
+
+
+###############################################################################
+#
+# Example 3: Create a worksheet with outlined columns.
+#
+data = [
+ ['Month', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Total'],
+ ['North', 50, 20, 15, 25, 65, 80, '=SUM(B2:G2)'],
+ ['South', 10, 20, 30, 50, 50, 50, '=SUM(B3:G3)'],
+ ['East', 45, 75, 50, 15, 75, 100, '=SUM(B4:G4)'],
+ ['West', 15, 15, 55, 35, 20, 50, '=SUM(B5:G5)']]
+
+# Add bold format to the first row.
+worksheet3.set_row(0, None, bold)
+
+# Set column formatting and the outline level.
+worksheet3.set_column('A:A', 10, bold)
+worksheet3.set_column('B:G', 5, None, {'level': 1})
+worksheet3.set_column('H:H', 10)
+
+# Write the data and a formula
+for row, data_row in enumerate(data):
+ worksheet3.write_row(row, 0, data_row)
+
+worksheet3.write('H6', '=SUM(H2:H5)', bold)
+
+
+###############################################################################
+#
+# Example 4: Show all possible outline levels.
+#
+levels = [
+ 'Level 1', 'Level 2', 'Level 3', 'Level 4', 'Level 5', 'Level 6',
+ 'Level 7', 'Level 6', 'Level 5', 'Level 4', 'Level 3', 'Level 2',
+ 'Level 1']
+
+worksheet4.write_column('A1', levels)
+
+worksheet4.set_row(0, None, None, {'level': 1})
+worksheet4.set_row(1, None, None, {'level': 2})
+worksheet4.set_row(2, None, None, {'level': 3})
+worksheet4.set_row(3, None, None, {'level': 4})
+worksheet4.set_row(4, None, None, {'level': 5})
+worksheet4.set_row(5, None, None, {'level': 6})
+worksheet4.set_row(6, None, None, {'level': 7})
+worksheet4.set_row(7, None, None, {'level': 6})
+worksheet4.set_row(8, None, None, {'level': 5})
+worksheet4.set_row(9, None, None, {'level': 4})
+worksheet4.set_row(10, None, None, {'level': 3})
+worksheet4.set_row(11, None, None, {'level': 2})
+worksheet4.set_row(12, None, None, {'level': 1})
+
+workbook.close()
diff --git a/examples/outline_collapsed.py b/examples/outline_collapsed.py
new file mode 100644
index 0000000..b71774e
--- /dev/null
+++ b/examples/outline_collapsed.py
@@ -0,0 +1,204 @@
+###############################################################################
+#
+# Example of how to use Python and XlsxWriter to generate Excel outlines and
+# grouping.
+#
+# These examples focus mainly on collapsed outlines. See also the
+# outlines.py example program for more general examples.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+# Create a new workbook and add some worksheets
+workbook = xlsxwriter.Workbook('outline_collapsed.xlsx')
+worksheet1 = workbook.add_worksheet('Outlined Rows')
+worksheet2 = workbook.add_worksheet('Collapsed Rows 1')
+worksheet3 = workbook.add_worksheet('Collapsed Rows 2')
+worksheet4 = workbook.add_worksheet('Collapsed Rows 3')
+worksheet5 = workbook.add_worksheet('Outline Columns')
+worksheet6 = workbook.add_worksheet('Collapsed Columns')
+
+# Add a general format
+bold = workbook.add_format({'bold': 1})
+
+
+# This function will generate the same data and sub-totals on each worksheet.
+# Used in the first 4 examples.
+#
+def create_sub_totals(worksheet):
+ # Adjust the column width for clarity.
+ worksheet.set_column('A:A', 20)
+
+ # Add the data, labels and formulas.
+ worksheet.write('A1', 'Region', bold)
+ worksheet.write('A2', 'North')
+ worksheet.write('A3', 'North')
+ worksheet.write('A4', 'North')
+ worksheet.write('A5', 'North')
+ worksheet.write('A6', 'North Total', bold)
+
+ worksheet.write('B1', 'Sales', bold)
+ worksheet.write('B2', 1000)
+ worksheet.write('B3', 1200)
+ worksheet.write('B4', 900)
+ worksheet.write('B5', 1200)
+ worksheet.write('B6', '=SUBTOTAL(9,B2:B5)', bold)
+
+ worksheet.write('A7', 'South')
+ worksheet.write('A8', 'South')
+ worksheet.write('A9', 'South')
+ worksheet.write('A10', 'South')
+ worksheet.write('A11', 'South Total', bold)
+
+ worksheet.write('B7', 400)
+ worksheet.write('B8', 600)
+ worksheet.write('B9', 500)
+ worksheet.write('B10', 600)
+ worksheet.write('B11', '=SUBTOTAL(9,B7:B10)', bold)
+
+ worksheet.write('A12', 'Grand Total', bold)
+ worksheet.write('B12', '=SUBTOTAL(9,B2:B10)', bold)
+
+###############################################################################
+#
+# Example 1: A worksheet with outlined rows. It also includes SUBTOTAL()
+# functions so that it looks like the type of automatic outlines that are
+# generated when you use the Excel Data->SubTotals menu item.
+#
+worksheet1.set_row(1, None, None, {'level': 2})
+worksheet1.set_row(2, None, None, {'level': 2})
+worksheet1.set_row(3, None, None, {'level': 2})
+worksheet1.set_row(4, None, None, {'level': 2})
+worksheet1.set_row(5, None, None, {'level': 1})
+
+worksheet1.set_row(6, None, None, {'level': 2})
+worksheet1.set_row(7, None, None, {'level': 2})
+worksheet1.set_row(8, None, None, {'level': 2})
+worksheet1.set_row(9, None, None, {'level': 2})
+worksheet1.set_row(10, None, None, {'level': 1})
+
+# Write the sub-total data that is common to the row examples.
+create_sub_totals(worksheet1)
+
+
+###############################################################################
+#
+# Example 2: Create a worksheet with collapsed outlined rows.
+# This is the same as the example 1 except that the all rows are collapsed.
+# Note: We need to indicate the rows that contains the collapsed symbol '+'
+# with the optional parameter, 'collapsed'.
+#
+worksheet2.set_row(1, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(2, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(3, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(4, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(5, None, None, {'level': 1, 'hidden': True})
+
+worksheet2.set_row(6, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(7, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(8, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(9, None, None, {'level': 2, 'hidden': True})
+worksheet2.set_row(10, None, None, {'level': 1, 'hidden': True})
+
+worksheet2.set_row(11, None, None, {'collapsed': True})
+
+# Write the sub-total data that is common to the row examples.
+create_sub_totals(worksheet2)
+
+
+###############################################################################
+#
+# Example 3: Create a worksheet with collapsed outlined rows.
+# Same as the example 1 except that the two sub-totals are collapsed.
+#
+worksheet3.set_row(1, None, None, {'level': 2, 'hidden': True})
+worksheet3.set_row(2, None, None, {'level': 2, 'hidden': True})
+worksheet3.set_row(3, None, None, {'level': 2, 'hidden': True})
+worksheet3.set_row(4, None, None, {'level': 2, 'hidden': True})
+worksheet3.set_row(5, None, None, {'level': 1, 'collapsed': True})
+
+worksheet3.set_row(6, None, None, {'level': 2, 'hidden': True})
+worksheet3.set_row(7, None, None, {'level': 2, 'hidden': True})
+worksheet3.set_row(8, None, None, {'level': 2, 'hidden': True})
+worksheet3.set_row(9, None, None, {'level': 2, 'hidden': True})
+worksheet3.set_row(10, None, None, {'level': 1, 'collapsed': True})
+
+# Write the sub-total data that is common to the row examples.
+create_sub_totals(worksheet3)
+
+
+###############################################################################
+#
+# Example 4: Create a worksheet with outlined rows.
+# Same as the example 1 except that the two sub-totals are collapsed.
+#
+worksheet4.set_row(1, None, None, {'level': 2, 'hidden': True})
+worksheet4.set_row(2, None, None, {'level': 2, 'hidden': True})
+worksheet4.set_row(3, None, None, {'level': 2, 'hidden': True})
+worksheet4.set_row(4, None, None, {'level': 2, 'hidden': True})
+worksheet4.set_row(5, None, None, {'level': 1, 'hidden': True,
+ 'collapsed': True})
+
+worksheet4.set_row(6, None, None, {'level': 2, 'hidden': True})
+worksheet4.set_row(7, None, None, {'level': 2, 'hidden': True})
+worksheet4.set_row(8, None, None, {'level': 2, 'hidden': True})
+worksheet4.set_row(9, None, None, {'level': 2, 'hidden': True})
+worksheet4.set_row(10, None, None, {'level': 1, 'hidden': True,
+ 'collapsed': True})
+
+worksheet4.set_row(11, None, None, {'collapsed': True})
+
+
+# Write the sub-total data that is common to the row examples.
+create_sub_totals(worksheet4)
+
+
+###############################################################################
+#
+# Example 5: Create a worksheet with outlined columns.
+#
+data = [
+ ['Month', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Total'],
+ ['North', 50, 20, 15, 25, 65, 80, '=SUM(B2:G2)'],
+ ['South', 10, 20, 30, 50, 50, 50, '=SUM(B3:G3)'],
+ ['East', 45, 75, 50, 15, 75, 100, '=SUM(B4:G4)'],
+ ['West', 15, 15, 55, 35, 20, 50, '=SUM(B5:G5)']]
+
+# Add bold format to the first row.
+worksheet5.set_row(0, None, bold)
+
+# Set column formatting and the outline level.
+worksheet5.set_column('A:A', 10, bold)
+worksheet5.set_column('B:G', 5, None, {'level': 1})
+worksheet5.set_column('H:H', 10)
+
+# Write the data and a formula.
+for row, data_row in enumerate(data):
+ worksheet5.write_row(row, 0, data_row)
+
+worksheet5.write('H6', '=SUM(H2:H5)', bold)
+
+###############################################################################
+#
+# Example 6: Create a worksheet with collapsed outlined columns.
+# This is the same as the previous example except with collapsed columns.
+#
+
+# Reuse the data from the previous example.
+
+# Add bold format to the first row.
+worksheet6.set_row(0, None, bold)
+
+# Set column formatting and the outline level.
+worksheet6.set_column('A:A', 10, bold)
+worksheet6.set_column('B:G', 5, None, {'level': 1, 'hidden': True})
+worksheet6.set_column('H:H', 10, None, {'collapsed': True})
+
+# Write the data and a formula.
+for row, data_row in enumerate(data):
+ worksheet6.write_row(row, 0, data_row)
+
+worksheet6.write('H6', '=SUM(H2:H5)', bold)
+
+workbook.close()
diff --git a/examples/pandas_chart.py b/examples/pandas_chart.py
new file mode 100644
index 0000000..71a94eb
--- /dev/null
+++ b/examples/pandas_chart.py
@@ -0,0 +1,35 @@
+##############################################################################
+#
+# An example of converting a Pandas dataframe to an xlsx file with a chart
+# using Pandas and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import pandas as pd
+
+
+# Create a Pandas dataframe from some data.
+df = pd.DataFrame({'Data': [10, 20, 30, 20, 15, 30, 45]})
+
+# Create a Pandas Excel writer using XlsxWriter as the engine.
+writer = pd.ExcelWriter('pandas_chart.xlsx', engine='xlsxwriter')
+
+# Convert the dataframe to an XlsxWriter Excel object.
+df.to_excel(writer, sheet_name='Sheet1')
+
+# Get the xlsxwriter workbook and worksheet objects.
+workbook = writer.book
+worksheet = writer.sheets['Sheet1']
+
+# Create a chart object.
+chart = workbook.add_chart({'type': 'column'})
+
+# Configure the series of the chart from the dataframe data.
+chart.add_series({'values': '=Sheet1!$B$2:$B$8'})
+
+# Insert the chart into the worksheet.
+worksheet.insert_chart('D2', chart)
+
+# Close the Pandas Excel writer and output the Excel file.
+writer.save()
diff --git a/examples/pandas_chart_columns.py b/examples/pandas_chart_columns.py
new file mode 100644
index 0000000..26f862d
--- /dev/null
+++ b/examples/pandas_chart_columns.py
@@ -0,0 +1,57 @@
+##############################################################################
+#
+# An example of converting a Pandas dataframe to an xlsx file with a grouped
+# column chart using Pandas and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import pandas as pd
+from vincent.colors import brews
+
+# Some sample data to plot.
+farm_1 = {'Apples': 10, 'Berries': 32, 'Squash': 21, 'Melons': 13, 'Corn': 18}
+farm_2 = {'Apples': 15, 'Berries': 43, 'Squash': 17, 'Melons': 10, 'Corn': 22}
+farm_3 = {'Apples': 6, 'Berries': 24, 'Squash': 22, 'Melons': 16, 'Corn': 30}
+farm_4 = {'Apples': 12, 'Berries': 30, 'Squash': 15, 'Melons': 9, 'Corn': 15}
+
+data = [farm_1, farm_2, farm_3, farm_4]
+index = ['Farm 1', 'Farm 2', 'Farm 3', 'Farm 4']
+
+# Create a Pandas dataframe from the data.
+df = pd.DataFrame(data, index=index)
+
+# Create a Pandas Excel writer using XlsxWriter as the engine.
+sheet_name = 'Sheet1'
+writer = pd.ExcelWriter('pandas_chart_columns.xlsx', engine='xlsxwriter')
+df.to_excel(writer, sheet_name=sheet_name)
+
+# Access the XlsxWriter workbook and worksheet objects from the dataframe.
+workbook = writer.book
+worksheet = writer.sheets[sheet_name]
+
+# Create a chart object.
+chart = workbook.add_chart({'type': 'column'})
+
+# Some alternative colors for the chart.
+colors = ['#E41A1C', '#377EB8', '#4DAF4A', '#984EA3', '#FF7F00']
+
+# Configure the series of the chart from the dataframe data.
+for col_num in range(1, len(farm_1) + 1):
+ chart.add_series({
+ 'name': ['Sheet1', 0, col_num],
+ 'categories': ['Sheet1', 1, 0, 4, 0],
+ 'values': ['Sheet1', 1, col_num, 4, col_num],
+ 'fill': {'color': colors[col_num - 1]},
+ 'overlap': -10,
+ })
+
+# Configure the chart axes.
+chart.set_x_axis({'name': 'Total Produce'})
+chart.set_y_axis({'name': 'Farms', 'major_gridlines': {'visible': False}})
+
+# Insert the chart into the worksheet.
+worksheet.insert_chart('H2', chart)
+
+# Close the Pandas Excel writer and output the Excel file.
+writer.save()
diff --git a/examples/pandas_chart_line.py b/examples/pandas_chart_line.py
new file mode 100644
index 0000000..e6f1fba
--- /dev/null
+++ b/examples/pandas_chart_line.py
@@ -0,0 +1,55 @@
+##############################################################################
+#
+# An example of converting a Pandas dataframe to an xlsx file with a line
+# chart using Pandas and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import pandas as pd
+import random
+
+# Create some sample data to plot.
+max_row = 21
+categories = ['Node 1', 'Node 2', 'Node 3', 'Node 4']
+index_1 = range(0, max_row, 1)
+multi_iter1 = {'index': index_1}
+
+for category in categories:
+ multi_iter1[category] = [random.randint(10, 100) for x in index_1]
+
+# Create a Pandas dataframe from the data.
+index_2 = multi_iter1.pop('index')
+df = pd.DataFrame(multi_iter1, index=index_2)
+df = df.reindex(columns=sorted(df.columns))
+
+# Create a Pandas Excel writer using XlsxWriter as the engine.
+sheet_name = 'Sheet1'
+writer = pd.ExcelWriter('pandas_chart_line.xlsx', engine='xlsxwriter')
+df.to_excel(writer, sheet_name=sheet_name)
+
+# Access the XlsxWriter workbook and worksheet objects from the dataframe.
+workbook = writer.book
+worksheet = writer.sheets[sheet_name]
+
+# Create a chart object.
+chart = workbook.add_chart({'type': 'line'})
+
+# Configure the series of the chart from the dataframe data.
+for i in range(len(categories)):
+ col = i + 1
+ chart.add_series({
+ 'name': ['Sheet1', 0, col],
+ 'categories': ['Sheet1', 1, 0, max_row, 0],
+ 'values': ['Sheet1', 1, col, max_row, col],
+ })
+
+# Configure the chart axes.
+chart.set_x_axis({'name': 'Index'})
+chart.set_y_axis({'name': 'Value', 'major_gridlines': {'visible': False}})
+
+# Insert the chart into the worksheet.
+worksheet.insert_chart('G2', chart)
+
+# Close the Pandas Excel writer and output the Excel file.
+writer.save()
diff --git a/examples/pandas_chart_stock.py b/examples/pandas_chart_stock.py
new file mode 100644
index 0000000..30897cf
--- /dev/null
+++ b/examples/pandas_chart_stock.py
@@ -0,0 +1,58 @@
+##############################################################################
+#
+# An example of converting a Pandas dataframe with stock data taken from the
+# web to an xlsx file with a line chart using Pandas and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import pandas as pd
+import pandas.io.data as web
+
+# Create some sample data to plot.
+all_data = {}
+for ticker in ['AAPL', 'GOOGL', 'IBM', 'YHOO', 'MSFT']:
+ all_data[ticker] = web.get_data_yahoo(ticker, '5/1/2014', '5/1/2015')
+
+# Create a Pandas dataframe from the data.
+df = pd.DataFrame({tic: data['Adj Close']
+ for tic, data in all_data.items()})
+
+# Create a Pandas Excel writer using XlsxWriter as the engine.
+sheet_name = 'Sheet1'
+writer = pd.ExcelWriter('pandas_chart_stock.xlsx', engine='xlsxwriter')
+df.to_excel(writer, sheet_name=sheet_name)
+
+# Access the XlsxWriter workbook and worksheet objects from the dataframe.
+workbook = writer.book
+worksheet = writer.sheets[sheet_name]
+
+# Adjust the width of the first column to make the date values clearer.
+worksheet.set_column('A:A', 20)
+
+# Create a chart object.
+chart = workbook.add_chart({'type': 'line'})
+
+# Configure the series of the chart from the dataframe data.
+max_row = len(df) + 1
+for i in range(len(['AAPL', 'GOOGL'])):
+ col = i + 1
+ chart.add_series({
+ 'name': ['Sheet1', 0, col],
+ 'categories': ['Sheet1', 2, 0, max_row, 0],
+ 'values': ['Sheet1', 2, col, max_row, col],
+ 'line': {'width': 1.00},
+ })
+
+# Configure the chart axes.
+chart.set_x_axis({'name': 'Date', 'date_axis': True})
+chart.set_y_axis({'name': 'Price', 'major_gridlines': {'visible': False}})
+
+# Position the legend at the top of the chart.
+chart.set_legend({'position': 'top'})
+
+# Insert the chart into the worksheet.
+worksheet.insert_chart('H2', chart)
+
+# Close the Pandas Excel writer and output the Excel file.
+writer.save()
diff --git a/examples/pandas_column_formats.py b/examples/pandas_column_formats.py
new file mode 100644
index 0000000..18ff3f6
--- /dev/null
+++ b/examples/pandas_column_formats.py
@@ -0,0 +1,40 @@
+##############################################################################
+#
+# An example of converting a Pandas dataframe to an xlsx file
+# with column formats using Pandas and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import pandas as pd
+
+# Create a Pandas dataframe from some data.
+df = pd.DataFrame({'Numbers': [1010, 2020, 3030, 2020, 1515, 3030, 4545],
+ 'Percentage': [.1, .2, .33, .25, .5, .75, .45 ],
+})
+
+# Create a Pandas Excel writer using XlsxWriter as the engine.
+writer = pd.ExcelWriter("pandas_column_formats.xlsx", engine='xlsxwriter')
+
+# Convert the dataframe to an XlsxWriter Excel object.
+df.to_excel(writer, sheet_name='Sheet1')
+
+# Get the xlsxwriter workbook and worksheet objects.
+workbook = writer.book
+worksheet = writer.sheets['Sheet1']
+
+# Add some cell formats.
+format1 = workbook.add_format({'num_format': '#,##0.00'})
+format2 = workbook.add_format({'num_format': '0%'})
+
+# Note: It isn't possible to format any cells that already have a format such
+# as the index or headers or any cells that contain dates or datetimes.
+
+# Set the column width and format.
+worksheet.set_column('B:B', 18, format1)
+
+# Set the format but not the column width.
+worksheet.set_column('C:C', None, format2)
+
+# Close the Pandas Excel writer and output the Excel file.
+writer.save()
diff --git a/examples/pandas_conditional_format.py b/examples/pandas_conditional_format.py
new file mode 100644
index 0000000..d461bea
--- /dev/null
+++ b/examples/pandas_conditional_format.py
@@ -0,0 +1,29 @@
+##############################################################################
+#
+# An example of converting a Pandas dataframe to an xlsx file with a
+# conditional formatting using Pandas and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import pandas as pd
+
+
+# Create a Pandas dataframe from some data.
+df = pd.DataFrame({'Data': [10, 20, 30, 20, 15, 30, 45]})
+
+# Create a Pandas Excel writer using XlsxWriter as the engine.
+writer = pd.ExcelWriter('pandas_conditional.xlsx', engine='xlsxwriter')
+
+# Convert the dataframe to an XlsxWriter Excel object.
+df.to_excel(writer, sheet_name='Sheet1')
+
+# Get the xlsxwriter workbook and worksheet objects.
+workbook = writer.book
+worksheet = writer.sheets['Sheet1']
+
+# Apply a conditional format to the cell range.
+worksheet.conditional_format('B2:B8', {'type': '3_color_scale'})
+
+# Close the Pandas Excel writer and output the Excel file.
+writer.save()
diff --git a/examples/pandas_datetime.py b/examples/pandas_datetime.py
new file mode 100644
index 0000000..81fe566
--- /dev/null
+++ b/examples/pandas_datetime.py
@@ -0,0 +1,43 @@
+##############################################################################
+#
+# An example of converting a Pandas dataframe with datetimes to an xlsx file
+# with a default datetime and date format using Pandas and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import pandas as pd
+from datetime import datetime, date
+
+# Create a Pandas dataframe from some datetime data.
+df = pd.DataFrame({'Date and time': [datetime(2015, 1, 1, 11, 30, 55),
+ datetime(2015, 1, 2, 1, 20, 33),
+ datetime(2015, 1, 3, 11, 10 ),
+ datetime(2015, 1, 4, 16, 45, 35),
+ datetime(2015, 1, 5, 12, 10, 15)],
+ 'Dates only': [date(2015, 2, 1),
+ date(2015, 2, 2),
+ date(2015, 2, 3),
+ date(2015, 2, 4),
+ date(2015, 2, 5)],
+ })
+
+# Create a Pandas Excel writer using XlsxWriter as the engine.
+# Also set the default datetime and date formats.
+writer = pd.ExcelWriter("pandas_datetime.xlsx",
+ engine='xlsxwriter',
+ datetime_format='mmm d yyyy hh:mm:ss',
+ date_format='mmmm dd yyyy')
+
+# Convert the dataframe to an XlsxWriter Excel object.
+df.to_excel(writer, sheet_name='Sheet1')
+
+# Get the xlsxwriter workbook and worksheet objects in order to set the column
+# widths, to make the dates clearer.
+workbook = writer.book
+worksheet = writer.sheets['Sheet1']
+
+worksheet.set_column('B:C', 20)
+
+# Close the Pandas Excel writer and output the Excel file.
+writer.save()
diff --git a/examples/pandas_multiple.py b/examples/pandas_multiple.py
new file mode 100644
index 0000000..7155d04
--- /dev/null
+++ b/examples/pandas_multiple.py
@@ -0,0 +1,26 @@
+##############################################################################
+#
+# An example of writing multiple dataframes to worksheets using Pandas and
+# XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import pandas as pd
+
+
+# Create some Pandas dataframes from some data.
+df1 = pd.DataFrame({'Data': [11, 12, 13, 14]})
+df2 = pd.DataFrame({'Data': [21, 22, 23, 24]})
+df3 = pd.DataFrame({'Data': [31, 32, 33, 34]})
+
+# Create a Pandas Excel writer using XlsxWriter as the engine.
+writer = pd.ExcelWriter('pandas_multiple.xlsx', engine='xlsxwriter')
+
+# Write each dataframe to a different worksheet.
+df1.to_excel(writer, sheet_name='Sheet1')
+df2.to_excel(writer, sheet_name='Sheet2')
+df3.to_excel(writer, sheet_name='Sheet3')
+
+# Close the Pandas Excel writer and output the Excel file.
+writer.save()
diff --git a/examples/pandas_positioning.py b/examples/pandas_positioning.py
new file mode 100644
index 0000000..66cab01
--- /dev/null
+++ b/examples/pandas_positioning.py
@@ -0,0 +1,31 @@
+##############################################################################
+#
+# An example of positioning dataframes in a worksheet using Pandas and
+# XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import pandas as pd
+
+
+# Create some Pandas dataframes from some data.
+df1 = pd.DataFrame({'Data': [11, 12, 13, 14]})
+df2 = pd.DataFrame({'Data': [21, 22, 23, 24]})
+df3 = pd.DataFrame({'Data': [31, 32, 33, 34]})
+df4 = pd.DataFrame({'Data': [41, 42, 43, 44]})
+
+# Create a Pandas Excel writer using XlsxWriter as the engine.
+writer = pd.ExcelWriter('pandas_positioning.xlsx', engine='xlsxwriter')
+
+# Position the dataframes in the worksheet.
+df1.to_excel(writer, sheet_name='Sheet1') # Default position, cell A1.
+df2.to_excel(writer, sheet_name='Sheet1', startcol=3)
+df3.to_excel(writer, sheet_name='Sheet1', startrow=6)
+
+# It is also possible to write the dataframe without the header and index.
+df4.to_excel(writer, sheet_name='Sheet1',
+ startrow=7, startcol=4, header=False, index=False)
+
+# Close the Pandas Excel writer and output the Excel file.
+writer.save()
diff --git a/examples/pandas_simple.py b/examples/pandas_simple.py
new file mode 100644
index 0000000..e61c2ab
--- /dev/null
+++ b/examples/pandas_simple.py
@@ -0,0 +1,22 @@
+##############################################################################
+#
+# A simple example of converting a Pandas dataframe to an xlsx file using
+# Pandas and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import pandas as pd
+
+
+# Create a Pandas dataframe from some data.
+df = pd.DataFrame({'Data': [10, 20, 30, 20, 15, 30, 45]})
+
+# Create a Pandas Excel writer using XlsxWriter as the engine.
+writer = pd.ExcelWriter('pandas_simple.xlsx', engine='xlsxwriter')
+
+# Convert the dataframe to an XlsxWriter Excel object.
+df.to_excel(writer, sheet_name='Sheet1')
+
+# Close the Pandas Excel writer and output the Excel file.
+writer.save()
diff --git a/examples/panes.py b/examples/panes.py
new file mode 100644
index 0000000..c1bd3c5
--- /dev/null
+++ b/examples/panes.py
@@ -0,0 +1,111 @@
+#######################################################################
+#
+# Example of using Python and the XlsxWriter module to create
+# worksheet panes.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('panes.xlsx')
+
+worksheet1 = workbook.add_worksheet('Panes 1')
+worksheet2 = workbook.add_worksheet('Panes 2')
+worksheet3 = workbook.add_worksheet('Panes 3')
+worksheet4 = workbook.add_worksheet('Panes 4')
+
+#######################################################################
+#
+# Set up some formatting and text to highlight the panes.
+#
+header_format = workbook.add_format({'bold': True,
+ 'align': 'center',
+ 'valign': 'vcenter',
+ 'fg_color': '#D7E4BC',
+ 'border': 1})
+
+center_format = workbook.add_format({'align': 'center'})
+
+
+#######################################################################
+#
+# Example 1. Freeze pane on the top row.
+#
+worksheet1.freeze_panes(1, 0)
+
+# Other sheet formatting.
+worksheet1.set_column('A:I', 16)
+worksheet1.set_row(0, 20)
+worksheet1.set_selection('C3')
+
+
+# Some text to demonstrate scrolling.
+for col in range(0, 9):
+ worksheet1.write(0, col, 'Scroll down', header_format)
+
+for row in range(1, 100):
+ for col in range(0, 9):
+ worksheet1.write(row, col, row + 1, center_format)
+
+
+#######################################################################
+#
+# Example 2. Freeze pane on the left column.
+#
+worksheet2.freeze_panes(0, 1)
+
+# Other sheet formatting.
+worksheet2.set_column('A:A', 16)
+worksheet2.set_selection('C3')
+
+# Some text to demonstrate scrolling.
+for row in range(0, 50):
+ worksheet2.write(row, 0, 'Scroll right', header_format)
+ for col in range(1, 26):
+ worksheet2.write(row, col, col, center_format)
+
+
+#######################################################################
+#
+# Example 3. Freeze pane on the top row and left column.
+#
+worksheet3.freeze_panes(1, 1)
+
+# Other sheet formatting.
+worksheet3.set_column('A:Z', 16)
+worksheet3.set_row(0, 20)
+worksheet3.set_selection('C3')
+worksheet3.write(0, 0, '', header_format)
+
+# Some text to demonstrate scrolling.
+for col in range(1, 26):
+ worksheet3.write(0, col, 'Scroll down', header_format)
+
+for row in range(1, 50):
+ worksheet3.write(row, 0, 'Scroll right', header_format)
+ for col in range(1, 26):
+ worksheet3.write(row, col, col, center_format)
+
+
+#######################################################################
+#
+# Example 4. Split pane on the top row and left column.
+#
+# The divisions must be specified in terms of row and column dimensions.
+# The default row height is 15 and the default column width is 8.43
+#
+worksheet4.split_panes(15, 8.43)
+
+# Other sheet formatting.
+worksheet4.set_selection('C3')
+
+# Some text to demonstrate scrolling.
+for col in range(1, 26):
+ worksheet4.write(0, col, 'Scroll', center_format)
+
+for row in range(1, 50):
+ worksheet4.write(row, 0, 'Scroll', center_format)
+ for col in range(1, 26):
+ worksheet4.write(row, col, col, center_format)
+
+workbook.close()
diff --git a/examples/python-200x80.png b/examples/python-200x80.png
new file mode 100644
index 0000000..c56d4c0
Binary files /dev/null and b/examples/python-200x80.png differ
diff --git a/examples/python.png b/examples/python.png
new file mode 100644
index 0000000..cd36228
Binary files /dev/null and b/examples/python.png differ
diff --git a/examples/rich_strings.py b/examples/rich_strings.py
new file mode 100644
index 0000000..e3d8c79
--- /dev/null
+++ b/examples/rich_strings.py
@@ -0,0 +1,48 @@
+#######################################################################
+#
+# An example of using Python and XlsxWriter to write some "rich strings",
+# i.e., strings with multiple formats.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('rich_strings.xlsx')
+worksheet = workbook.add_worksheet()
+
+worksheet.set_column('A:A', 30)
+
+# Set up some formats to use.
+bold = workbook.add_format({'bold': True})
+italic = workbook.add_format({'italic': True})
+red = workbook.add_format({'color': 'red'})
+blue = workbook.add_format({'color': 'blue'})
+center = workbook.add_format({'align': 'center'})
+superscript = workbook.add_format({'font_script': 1})
+
+# Write some strings with multiple formats.
+worksheet.write_rich_string('A1',
+ 'This is ',
+ bold, 'bold',
+ ' and this is ',
+ italic, 'italic')
+
+worksheet.write_rich_string('A3',
+ 'This is ',
+ red, 'red',
+ ' and this is ',
+ blue, 'blue')
+
+worksheet.write_rich_string('A5',
+ 'Some ',
+ bold, 'bold text',
+ ' centered',
+ center)
+
+worksheet.write_rich_string('A7',
+ italic,
+ 'j = k',
+ superscript, '(n-1)',
+ center)
+
+workbook.close()
diff --git a/examples/right_to_left.py b/examples/right_to_left.py
new file mode 100644
index 0000000..ce827b3
--- /dev/null
+++ b/examples/right_to_left.py
@@ -0,0 +1,28 @@
+#######################################################################
+#
+# Example of how to use Python and the XlsxWriter module to change the
+# default worksheet direction from left-to-right to right-to-left as
+# required by some middle eastern versions of Excel.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('right_to_left.xlsx')
+
+# Add two worksheets.
+worksheet1 = workbook.add_worksheet()
+worksheet2 = workbook.add_worksheet()
+
+# Change the direction for worksheet2.
+worksheet2.right_to_left()
+
+# Write some data to show the difference.
+
+# Standard direction: A1, B1, C1, ...
+worksheet1.write('A1', 'Hello')
+
+# Right to left direction: ..., C1, B1, A1
+worksheet2.write('A1', 'Hello')
+
+workbook.close()
diff --git a/examples/sparklines1.py b/examples/sparklines1.py
new file mode 100644
index 0000000..2b13153
--- /dev/null
+++ b/examples/sparklines1.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Example of how to add sparklines to a Python XlsxWriter file.
+#
+# Sparklines are small charts that fit in a single cell and are
+# used to show trends in data.
+#
+# See sparklines2.py for examples of more complex sparkline formatting.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('sparklines1.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Some sample data to plot.
+data = [
+ [-2, 2, 3, -1, 0],
+ [30, 20, 33, 20, 15],
+ [1, -1, -1, 1, -1],
+]
+
+
+# Write the sample data to the worksheet.
+worksheet.write_row('A1', data[0])
+worksheet.write_row('A2', data[1])
+worksheet.write_row('A3', data[2])
+
+
+# Add a line sparkline (the default) with markers.
+worksheet.add_sparkline('F1', {'range': 'Sheet1!A1:E1',
+ 'markers': True})
+
+
+# Add a column sparkline with non-default style.
+worksheet.add_sparkline('F2', {'range': 'Sheet1!A2:E2',
+ 'type': 'column',
+ 'style': 12})
+
+
+# Add a win/loss sparkline with negative values highlighted.
+worksheet.add_sparkline('F3', {'range': 'Sheet1!A3:E3',
+ 'type': 'win_loss',
+ 'negative_points': True})
+
+workbook.close()
diff --git a/examples/sparklines2.py b/examples/sparklines2.py
new file mode 100644
index 0000000..4ebe38a
--- /dev/null
+++ b/examples/sparklines2.py
@@ -0,0 +1,312 @@
+###############################################################################
+#
+# Example of how to add sparklines to an XlsxWriter file with Python.
+#
+# Sparklines are small charts that fit in a single cell and are
+# used to show trends in data. This example shows the majority of
+# options that can be applied to sparklines.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('sparklines2.xlsx')
+worksheet1 = workbook.add_worksheet()
+worksheet2 = workbook.add_worksheet()
+bold = workbook.add_format({'bold': True})
+row = 1
+
+# Set the columns widths to make the output clearer.
+worksheet1.set_column('A:A', 14)
+worksheet1.set_column('B:B', 50)
+worksheet1.set_zoom(150)
+
+# Headings.
+worksheet1.write('A1', 'Sparkline', bold)
+worksheet1.write('B1', 'Description', bold)
+
+
+###############################################################################
+#
+text = 'A default "line" sparkline.'
+
+worksheet1.add_sparkline('A2', {'range': 'Sheet2!A1:J1'})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'A default "column" sparkline.'
+
+worksheet1.add_sparkline('A3', {'range': 'Sheet2!A2:J2',
+ 'type': 'column'})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'A default "win/loss" sparkline.'
+
+worksheet1.add_sparkline('A4', {'range': 'Sheet2!A3:J3',
+ 'type': 'win_loss'})
+
+worksheet1.write(row, 1, text)
+row += 2
+
+
+###############################################################################
+#
+text = 'Line with markers.'
+
+worksheet1.add_sparkline('A6', {'range': 'Sheet2!A1:J1',
+ 'markers': True})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'Line with high and low points.'
+
+worksheet1.add_sparkline('A7', {'range': 'Sheet2!A1:J1',
+ 'high_point': True,
+ 'low_point': True})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'Line with first and last point markers.'
+
+worksheet1.add_sparkline('A8', {'range': 'Sheet2!A1:J1',
+ 'first_point': True,
+ 'last_point': True})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'Line with negative point markers.'
+
+worksheet1.add_sparkline('A9', {'range': 'Sheet2!A1:J1',
+ 'negative_points': True})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'Line with axis.'
+
+worksheet1.add_sparkline('A10', {'range': 'Sheet2!A1:J1',
+ 'axis': True})
+
+worksheet1.write(row, 1, text)
+row += 2
+
+
+###############################################################################
+#
+text = 'Column with default style (1).'
+
+worksheet1.add_sparkline('A12', {'range': 'Sheet2!A2:J2',
+ 'type': 'column'})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'Column with style 2.'
+
+worksheet1.add_sparkline('A13', {'range': 'Sheet2!A2:J2',
+ 'type': 'column',
+ 'style': 2})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'Column with style 3.'
+
+worksheet1.add_sparkline('A14', {'range': 'Sheet2!A2:J2',
+ 'type': 'column',
+ 'style': 3})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'Column with style 4.'
+
+worksheet1.add_sparkline('A15', {'range': 'Sheet2!A2:J2',
+ 'type': 'column',
+ 'style': 4})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'Column with style 5.'
+
+worksheet1.add_sparkline('A16', {'range': 'Sheet2!A2:J2',
+ 'type': 'column',
+ 'style': 5})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'Column with style 6.'
+
+worksheet1.add_sparkline('A17', {'range': 'Sheet2!A2:J2',
+ 'type': 'column',
+ 'style': 6})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'Column with a user defined color.'
+
+worksheet1.add_sparkline('A18', {'range': 'Sheet2!A2:J2',
+ 'type': 'column',
+ 'series_color': '#E965E0'})
+
+worksheet1.write(row, 1, text)
+row += 2
+
+
+###############################################################################
+#
+text = 'A win/loss sparkline.'
+
+worksheet1.add_sparkline('A20', {'range': 'Sheet2!A3:J3',
+ 'type': 'win_loss'})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'A win/loss sparkline with negative points highlighted.'
+
+worksheet1.add_sparkline('A21', {'range': 'Sheet2!A3:J3',
+ 'type': 'win_loss',
+ 'negative_points': True})
+
+worksheet1.write(row, 1, text)
+row += 2
+
+
+###############################################################################
+#
+text = 'A left to right column (the default).'
+
+worksheet1.add_sparkline('A23', {'range': 'Sheet2!A4:J4',
+ 'type': 'column',
+ 'style': 20})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'A right to left column.'
+
+worksheet1.add_sparkline('A24', {'range': 'Sheet2!A4:J4',
+ 'type': 'column',
+ 'style': 20,
+ 'reverse': True})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+text = 'Sparkline and text in one cell.'
+
+worksheet1.add_sparkline('A25', {'range': 'Sheet2!A4:J4',
+ 'type': 'column',
+ 'style': 20})
+
+worksheet1.write(row, 0, 'Growth')
+worksheet1.write(row, 1, text)
+row += 2
+
+
+###############################################################################
+#
+text = 'A grouped sparkline. Changes are applied to all three.'
+
+worksheet1.add_sparkline('A27', {'location': ['A27', 'A28', 'A29'],
+ 'range': ['Sheet2!A5:J5',
+ 'Sheet2!A6:J6',
+ 'Sheet2!A7:J7'],
+ 'markers': True})
+
+worksheet1.write(row, 1, text)
+row += 1
+
+
+###############################################################################
+#
+# Create a second worksheet with data to plot.
+#
+worksheet2.set_column('A:J', 11)
+
+data = [
+
+ # Simple line data.
+ [-2, 2, 3, -1, 0, -2, 3, 2, 1, 0],
+
+ # Simple column data.
+ [30, 20, 33, 20, 15, 5, 5, 15, 10, 15],
+
+ # Simple win/loss data.
+ [1, 1, -1, -1, 1, -1, 1, 1, 1, -1],
+
+ # Unbalanced histogram.
+ [5, 6, 7, 10, 15, 20, 30, 50, 70, 100],
+
+ # Data for the grouped sparkline example.
+ [-2, 2, 3, -1, 0, -2, 3, 2, 1, 0],
+ [3, -1, 0, -2, 3, 2, 1, 0, 2, 1],
+ [0, -2, 3, 2, 1, 0, 1, 2, 3, 1],
+
+]
+
+# Write the sample data to the worksheet.
+worksheet2.write_row('A1', data[0])
+worksheet2.write_row('A2', data[1])
+worksheet2.write_row('A3', data[2])
+worksheet2.write_row('A4', data[3])
+worksheet2.write_row('A5', data[4])
+worksheet2.write_row('A6', data[5])
+worksheet2.write_row('A7', data[6])
+
+workbook.close()
diff --git a/examples/tab_colors.py b/examples/tab_colors.py
new file mode 100644
index 0000000..26d9828
--- /dev/null
+++ b/examples/tab_colors.py
@@ -0,0 +1,25 @@
+#######################################################################
+#
+# Example of how to set Excel worksheet tab colors using Python
+# and the XlsxWriter module.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('tab_colors.xlsx')
+
+# Set up some worksheets.
+worksheet1 = workbook.add_worksheet()
+worksheet2 = workbook.add_worksheet()
+worksheet3 = workbook.add_worksheet()
+worksheet4 = workbook.add_worksheet()
+
+# Set tab colors
+worksheet1.set_tab_color('red')
+worksheet2.set_tab_color('green')
+worksheet3.set_tab_color('#FF9900') # Orange
+
+# worksheet4 will have the default color.
+
+workbook.close()
diff --git a/examples/tables.py b/examples/tables.py
new file mode 100644
index 0000000..d3515c1
--- /dev/null
+++ b/examples/tables.py
@@ -0,0 +1,340 @@
+###############################################################################
+#
+# Example of how to add tables to an XlsxWriter worksheet.
+#
+# Tables in Excel are used to group rows and columns of data into a single
+# structure that can be referenced in a formula or formatted collectively.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('tables.xlsx')
+worksheet1 = workbook.add_worksheet()
+worksheet2 = workbook.add_worksheet()
+worksheet3 = workbook.add_worksheet()
+worksheet4 = workbook.add_worksheet()
+worksheet5 = workbook.add_worksheet()
+worksheet6 = workbook.add_worksheet()
+worksheet7 = workbook.add_worksheet()
+worksheet8 = workbook.add_worksheet()
+worksheet9 = workbook.add_worksheet()
+worksheet10 = workbook.add_worksheet()
+worksheet11 = workbook.add_worksheet()
+worksheet12 = workbook.add_worksheet()
+
+currency_format = workbook.add_format({'num_format': '$#,##0'})
+
+# Some sample data for the table.
+data = [
+ ['Apples', 10000, 5000, 8000, 6000],
+ ['Pears', 2000, 3000, 4000, 5000],
+ ['Bananas', 6000, 6000, 6500, 6000],
+ ['Oranges', 500, 300, 200, 700],
+
+]
+
+
+###############################################################################
+#
+# Example 1.
+#
+caption = 'Default table with no data.'
+
+# Set the columns widths.
+worksheet1.set_column('B:G', 12)
+
+# Write the caption.
+worksheet1.write('B1', caption)
+
+# Add a table to the worksheet.
+worksheet1.add_table('B3:F7')
+
+
+###############################################################################
+#
+# Example 2.
+#
+caption = 'Default table with data.'
+
+# Set the columns widths.
+worksheet2.set_column('B:G', 12)
+
+# Write the caption.
+worksheet2.write('B1', caption)
+
+# Add a table to the worksheet.
+worksheet2.add_table('B3:F7', {'data': data})
+
+
+###############################################################################
+#
+# Example 3.
+#
+caption = 'Table without default autofilter.'
+
+# Set the columns widths.
+worksheet3.set_column('B:G', 12)
+
+# Write the caption.
+worksheet3.write('B1', caption)
+
+# Add a table to the worksheet.
+worksheet3.add_table('B3:F7', {'autofilter': 0})
+
+# Table data can also be written separately, as an array or individual cells.
+worksheet3.write_row('B4', data[0])
+worksheet3.write_row('B5', data[1])
+worksheet3.write_row('B6', data[2])
+worksheet3.write_row('B7', data[3])
+
+
+###############################################################################
+#
+# Example 4.
+#
+caption = 'Table without default header row.'
+
+# Set the columns widths.
+worksheet4.set_column('B:G', 12)
+
+# Write the caption.
+worksheet4.write('B1', caption)
+
+# Add a table to the worksheet.
+worksheet4.add_table('B4:F7', {'header_row': 0})
+
+# Table data can also be written separately, as an array or individual cells.
+worksheet4.write_row('B4', data[0])
+worksheet4.write_row('B5', data[1])
+worksheet4.write_row('B6', data[2])
+worksheet4.write_row('B7', data[3])
+
+
+###############################################################################
+#
+# Example 5.
+#
+caption = 'Default table with "First Column" and "Last Column" options.'
+
+# Set the columns widths.
+worksheet5.set_column('B:G', 12)
+
+# Write the caption.
+worksheet5.write('B1', caption)
+
+# Add a table to the worksheet.
+worksheet5.add_table('B3:F7', {'first_column': 1, 'last_column': 1})
+
+# Table data can also be written separately, as an array or individual cells.
+worksheet5.write_row('B4', data[0])
+worksheet5.write_row('B5', data[1])
+worksheet5.write_row('B6', data[2])
+worksheet5.write_row('B7', data[3])
+
+
+###############################################################################
+#
+# Example 6.
+#
+caption = 'Table with banded columns but without default banded rows.'
+
+# Set the columns widths.
+worksheet6.set_column('B:G', 12)
+
+# Write the caption.
+worksheet6.write('B1', caption)
+
+# Add a table to the worksheet.
+worksheet6.add_table('B3:F7', {'banded_rows': 0, 'banded_columns': 1})
+
+# Table data can also be written separately, as an array or individual cells.
+worksheet6.write_row('B4', data[0])
+worksheet6.write_row('B5', data[1])
+worksheet6.write_row('B6', data[2])
+worksheet6.write_row('B7', data[3])
+
+
+###############################################################################
+#
+# Example 7.
+#
+caption = 'Table with user defined column headers'
+
+# Set the columns widths.
+worksheet7.set_column('B:G', 12)
+
+# Write the caption.
+worksheet7.write('B1', caption)
+
+# Add a table to the worksheet.
+worksheet7.add_table('B3:F7', {'data': data,
+ 'columns': [{'header': 'Product'},
+ {'header': 'Quarter 1'},
+ {'header': 'Quarter 2'},
+ {'header': 'Quarter 3'},
+ {'header': 'Quarter 4'},
+ ]})
+
+
+###############################################################################
+#
+# Example 8.
+#
+caption = 'Table with user defined column headers'
+
+# Set the columns widths.
+worksheet8.set_column('B:G', 12)
+
+# Write the caption.
+worksheet8.write('B1', caption)
+
+# Formula to use in the table.
+formula = '=SUM(Table8[@[Quarter 1]:[Quarter 4]])'
+
+# Add a table to the worksheet.
+worksheet8.add_table('B3:G7', {'data': data,
+ 'columns': [{'header': 'Product'},
+ {'header': 'Quarter 1'},
+ {'header': 'Quarter 2'},
+ {'header': 'Quarter 3'},
+ {'header': 'Quarter 4'},
+ {'header': 'Year',
+ 'formula': formula},
+ ]})
+
+
+###############################################################################
+#
+# Example 9.
+#
+caption = 'Table with totals row (but no caption or totals).'
+
+# Set the columns widths.
+worksheet9.set_column('B:G', 12)
+
+# Write the caption.
+worksheet9.write('B1', caption)
+
+# Formula to use in the table.
+formula = '=SUM(Table9[@[Quarter 1]:[Quarter 4]])'
+
+# Add a table to the worksheet.
+worksheet9.add_table('B3:G8', {'data': data,
+ 'total_row': 1,
+ 'columns': [{'header': 'Product'},
+ {'header': 'Quarter 1'},
+ {'header': 'Quarter 2'},
+ {'header': 'Quarter 3'},
+ {'header': 'Quarter 4'},
+ {'header': 'Year',
+ 'formula': formula
+ },
+ ]})
+
+
+###############################################################################
+#
+# Example 10.
+#
+caption = 'Table with totals row with user captions and functions.'
+
+# Set the columns widths.
+worksheet10.set_column('B:G', 12)
+
+# Write the caption.
+worksheet10.write('B1', caption)
+
+# Options to use in the table.
+options = {'data': data,
+ 'total_row': 1,
+ 'columns': [{'header': 'Product', 'total_string': 'Totals'},
+ {'header': 'Quarter 1', 'total_function': 'sum'},
+ {'header': 'Quarter 2', 'total_function': 'sum'},
+ {'header': 'Quarter 3', 'total_function': 'sum'},
+ {'header': 'Quarter 4', 'total_function': 'sum'},
+ {'header': 'Year',
+ 'formula': '=SUM(Table10[@[Quarter 1]:[Quarter 4]])',
+ 'total_function': 'sum'
+ },
+ ]}
+
+# Add a table to the worksheet.
+worksheet10.add_table('B3:G8', options)
+
+
+###############################################################################
+#
+# Example 11.
+#
+caption = 'Table with alternative Excel style.'
+
+# Set the columns widths.
+worksheet11.set_column('B:G', 12)
+
+# Write the caption.
+worksheet11.write('B1', caption)
+
+# Options to use in the table.
+options = {'data': data,
+ 'style': 'Table Style Light 11',
+ 'total_row': 1,
+ 'columns': [{'header': 'Product', 'total_string': 'Totals'},
+ {'header': 'Quarter 1', 'total_function': 'sum'},
+ {'header': 'Quarter 2', 'total_function': 'sum'},
+ {'header': 'Quarter 3', 'total_function': 'sum'},
+ {'header': 'Quarter 4', 'total_function': 'sum'},
+ {'header': 'Year',
+ 'formula': '=SUM(Table11[@[Quarter 1]:[Quarter 4]])',
+ 'total_function': 'sum'
+ },
+ ]}
+
+
+# Add a table to the worksheet.
+worksheet11.add_table('B3:G8', options)
+
+
+###############################################################################
+#
+# Example 12.
+#
+caption = 'Table with column formats.'
+
+# Set the columns widths.
+worksheet12.set_column('B:G', 12)
+
+# Write the caption.
+worksheet12.write('B1', caption)
+
+# Options to use in the table.
+options = {'data': data,
+ 'total_row': 1,
+ 'columns': [{'header': 'Product', 'total_string': 'Totals'},
+ {'header': 'Quarter 1',
+ 'total_function': 'sum',
+ 'format': currency_format,
+ },
+ {'header': 'Quarter 2',
+ 'total_function': 'sum',
+ 'format': currency_format,
+ },
+ {'header': 'Quarter 3',
+ 'total_function': 'sum',
+ 'format': currency_format,
+ },
+ {'header': 'Quarter 4',
+ 'total_function': 'sum',
+ 'format': currency_format,
+ },
+ {'header': 'Year',
+ 'formula': '=SUM(Table12[@[Quarter 1]:[Quarter 4]])',
+ 'total_function': 'sum',
+ 'format': currency_format,
+ },
+ ]}
+
+# Add a table to the worksheet.
+worksheet12.add_table('B3:G8', options)
+
+workbook.close()
diff --git a/examples/text_indent.py b/examples/text_indent.py
new file mode 100644
index 0000000..b3273b5
--- /dev/null
+++ b/examples/text_indent.py
@@ -0,0 +1,22 @@
+##############################################################################
+#
+# A simple formatting example using XlsxWriter.
+#
+# This program demonstrates the indentation cell format.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('text_indent.xlsx')
+
+worksheet = workbook.add_worksheet()
+indent1 = workbook.add_format({'indent': 1})
+indent2 = workbook.add_format({'indent': 2})
+
+worksheet.set_column('A:A', 40)
+
+worksheet.write('A1', "This text is indented 1 level", indent1)
+worksheet.write('A2', "This text is indented 2 levels", indent2)
+
+workbook.close()
diff --git a/examples/textbox.py b/examples/textbox.py
new file mode 100644
index 0000000..0d7bea3
--- /dev/null
+++ b/examples/textbox.py
@@ -0,0 +1,177 @@
+#######################################################################
+#
+# An example of inserting textboxes into an Excel worksheet using
+# Python and XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('textbox.xlsx')
+worksheet = workbook.add_worksheet()
+row = 4
+col = 1
+
+# The examples below show different textbox options and formatting. In each
+# example the text describes the formatting.
+
+
+# Example
+text = 'A simple textbox with some text'
+worksheet.insert_textbox(row, col, text)
+row += 10
+
+# Example
+text = 'A textbox with changed dimensions'
+options = {
+ 'width': 256,
+ 'height': 100,
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+# Example
+text = 'A textbox with an offset in the cell'
+options = {
+ 'x_offset': 10,
+ 'y_offset': 10,
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+# Example
+text = 'A textbox with scaling'
+options = {
+ 'x_scale': 1.5,
+ 'y_scale': 0.8,
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+# Example
+text = 'A textbox with some long text that wraps around onto several lines'
+worksheet.insert_textbox(row, col, text)
+row += 10
+
+# Example
+text = 'A textbox\nwith some\nnewlines\n\nand paragraphs'
+worksheet.insert_textbox(row, col, text)
+row += 10
+
+# Example
+text = 'A textbox with a solid fill background'
+options = {
+ 'fill': {'color': 'red'},
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+# Example
+text = 'A textbox with a no fill background'
+options = {
+ 'fill': {'none': True},
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+# Example
+text = 'A textbox with a gradient fill background'
+options = {
+ 'gradient': {'colors': ['#DDEBCF',
+ '#9CB86E',
+ '#156B13']},
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+# Example
+text = 'A textbox with a user defined border line'
+options = {
+ 'border': {'color': 'red',
+ 'width': 3,
+ 'dash_type': 'round_dot'},
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+# Example
+text = 'A textbox with no border line'
+options = {
+ 'border': {'none': True},
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+# Example
+text = 'Default alignment: top - left'
+worksheet.insert_textbox(row, col, text)
+row += 10
+
+# Example
+text = 'Alignment: top - center'
+options = {
+ 'align': {'horizontal': 'center'},
+}
+worksheet.insert_textbox(row, col, text)
+row += 10
+
+# Example
+text = 'Alignment: top - center'
+options = {
+ 'align': {'horizontal': 'center'},
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+# Example
+text = 'Alignment: middle - center'
+options = {
+ 'align': {'vertical': 'middle',
+ 'horizontal': 'center'},
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+# Example
+text = 'Font properties: bold'
+options = {
+ 'font': {'bold': True},
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+# Example
+text = 'Font properties: various'
+options = {
+ 'font': {'bold': True},
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+# Example
+text = 'Font properties: various'
+options = {
+ 'font': {'bold': True,
+ 'italic': True,
+ 'underline': True,
+ 'name': 'Arial',
+ 'color': 'red',
+ 'size': 12}
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+# Example
+text = 'Some text in a textbox with formatting'
+options = {
+ 'font': {'color': 'white'},
+ 'align': {'vertical': 'middle',
+ 'horizontal': 'center'
+ },
+ 'gradient': {'colors': ['red', 'blue']},
+}
+worksheet.insert_textbox(row, col, text, options)
+row += 10
+
+
+workbook.close()
diff --git a/examples/tutorial1.py b/examples/tutorial1.py
new file mode 100644
index 0000000..6bfc196
--- /dev/null
+++ b/examples/tutorial1.py
@@ -0,0 +1,39 @@
+##############################################################################
+#
+# A simple program to write some data to an Excel file using the XlsxWriter
+# Python module.
+#
+# This program is shown, with explanations, in Tutorial 1 of the XlsxWriter
+# documentation.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+# Create a workbook and add a worksheet.
+workbook = xlsxwriter.Workbook('Expenses01.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Some data we want to write to the worksheet.
+expenses = (
+ ['Rent', 1000],
+ ['Gas', 100],
+ ['Food', 300],
+ ['Gym', 50],
+)
+
+# Start from the first cell. Rows and columns are zero indexed.
+row = 0
+col = 0
+
+# Iterate over the data and write it out row by row.
+for item, cost in (expenses):
+ worksheet.write(row, col, item)
+ worksheet.write(row, col + 1, cost)
+ row += 1
+
+# Write a total using a formula.
+worksheet.write(row, 0, 'Total')
+worksheet.write(row, 1, '=SUM(B1:B4)')
+
+workbook.close()
diff --git a/examples/tutorial2.py b/examples/tutorial2.py
new file mode 100644
index 0000000..d43836f
--- /dev/null
+++ b/examples/tutorial2.py
@@ -0,0 +1,49 @@
+##############################################################################
+#
+# A simple program to write some data to an Excel file using the XlsxWriter
+# Python module.
+#
+# This program is shown, with explanations, in Tutorial 2 of the XlsxWriter
+# documentation.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+# Create a workbook and add a worksheet.
+workbook = xlsxwriter.Workbook('Expenses02.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Add a bold format to use to highlight cells.
+bold = workbook.add_format({'bold': True})
+
+# Add a number format for cells with money.
+money = workbook.add_format({'num_format': '$#,##0'})
+
+# Write some data header.
+worksheet.write('A1', 'Item', bold)
+worksheet.write('B1', 'Cost', bold)
+
+# Some data we want to write to the worksheet.
+expenses = (
+ ['Rent', 1000],
+ ['Gas', 100],
+ ['Food', 300],
+ ['Gym', 50],
+)
+
+# Start from the first cell below the headers.
+row = 1
+col = 0
+
+# Iterate over the data and write it out row by row.
+for item, cost in (expenses):
+ worksheet.write(row, col, item)
+ worksheet.write(row, col + 1, cost, money)
+ row += 1
+
+# Write a total using a formula.
+worksheet.write(row, 0, 'Total', bold)
+worksheet.write(row, 1, '=SUM(B2:B5)', money)
+
+workbook.close()
diff --git a/examples/tutorial3.py b/examples/tutorial3.py
new file mode 100644
index 0000000..4105e46
--- /dev/null
+++ b/examples/tutorial3.py
@@ -0,0 +1,60 @@
+##############################################################################
+#
+# A simple program to write some data to an Excel file using the XlsxWriter
+# Python module.
+#
+# This program is shown, with explanations, in Tutorial 3 of the XlsxWriter
+# documentation.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from datetime import datetime
+import xlsxwriter
+
+# Create a workbook and add a worksheet.
+workbook = xlsxwriter.Workbook('Expenses03.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Add a bold format to use to highlight cells.
+bold = workbook.add_format({'bold': 1})
+
+# Add a number format for cells with money.
+money_format = workbook.add_format({'num_format': '$#,##0'})
+
+# Add an Excel date format.
+date_format = workbook.add_format({'num_format': 'mmmm d yyyy'})
+
+# Adjust the column width.
+worksheet.set_column(1, 1, 15)
+
+# Write some data headers.
+worksheet.write('A1', 'Item', bold)
+worksheet.write('B1', 'Date', bold)
+worksheet.write('C1', 'Cost', bold)
+
+# Some data we want to write to the worksheet.
+expenses = (
+ ['Rent', '2013-01-13', 1000],
+ ['Gas', '2013-01-14', 100],
+ ['Food', '2013-01-16', 300],
+ ['Gym', '2013-01-20', 50],
+)
+
+# Start from the first cell below the headers.
+row = 1
+col = 0
+
+for item, date_str, cost in (expenses):
+ # Convert the date string into a datetime object.
+ date = datetime.strptime(date_str, "%Y-%m-%d")
+
+ worksheet.write_string(row, col, item)
+ worksheet.write_datetime(row, col + 1, date, date_format)
+ worksheet.write_number(row, col + 2, cost, money_format)
+ row += 1
+
+# Write a total using a formula.
+worksheet.write(row, 0, 'Total', bold)
+worksheet.write(row, 2, '=SUM(C2:C5)', money_format)
+
+workbook.close()
diff --git a/examples/unicode_polish_utf8.py b/examples/unicode_polish_utf8.py
new file mode 100644
index 0000000..22e4e16
--- /dev/null
+++ b/examples/unicode_polish_utf8.py
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# A simple example of converting some Unicode text to an Excel file using
+# the XlsxWriter Python module.
+#
+# This example generates a spreadsheet with some Polish text from a file
+# with UTF8 encoded text.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import codecs
+import xlsxwriter
+
+# Open the input file with the correct encoding.
+textfile = codecs.open('unicode_polish_utf8.txt', 'r', 'utf-8')
+
+# Create an new Excel file and convert the text data.
+workbook = xlsxwriter.Workbook('unicode_polish_utf8.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Widen the first column to make the text clearer.
+worksheet.set_column('A:A', 50)
+
+# Start from the first cell.
+row = 0
+col = 0
+
+# Read the text file and write it to the worksheet.
+for line in textfile:
+ # Ignore the comments in the text file.
+ if line.startswith('#'):
+ continue
+
+ # Write any other lines to the worksheet.
+ worksheet.write(row, col, line.rstrip("\n"))
+ row += 1
+
+workbook.close()
diff --git a/examples/unicode_polish_utf8.txt b/examples/unicode_polish_utf8.txt
new file mode 100644
index 0000000..db53a80
--- /dev/null
+++ b/examples/unicode_polish_utf8.txt
@@ -0,0 +1,34 @@
+#
+# XlsxWriter Unicode examples.
+#
+# Sample encoded text borrowed from Sean Burke's Pod::Simple distro.
+#
+# The text is Polish encoded as UTF8
+#
+# See the unicode_polish_utf8.py example.
+#
+WŚRÓD NOCNEJ CISZY
+
+Wśród nocnej ciszy głos się rozchodzi:
+Wstańcie, pasterze, Bóg się nam rodzi!
+Czym prędzej się wybierajcie,
+Do Betlejem pospieszajcie
+Przywitać Pana.
+
+Poszli, znaleźli Dzieciątko w żłobie
+Z wszystkimi znaki danymi sobie.
+Jako Bogu cześć Mu dali,
+A witając zawołali
+Z wielkiej radości:
+
+Ach, witaj Zbawco z dawno żądany,
+Wiele tysięcy lat wyglądany
+Na Ciebie króle, prorocy
+Czekali, a Tyś tej nocy
+Nam się objawił.
+
+I my czekamy na Ciebie, Pana,
+A skoro przyjdziesz na głos kapłana,
+Padniemy na twarz przed Tobą,
+Wierząc, żeś jest pod osłoną
+Chleba i wina.
diff --git a/examples/unicode_python2.py b/examples/unicode_python2.py
new file mode 100644
index 0000000..1b29f0f
--- /dev/null
+++ b/examples/unicode_python2.py
@@ -0,0 +1,22 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# A simple Unicode spreadsheet in Python 2 using the XlsxWriter Python module.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# To write Unicode text in UTF-8 to a xlsxwriter file in Python 2:
+#
+# 1. Encode the file as UTF-8.
+# 2. Include the "coding" directive at the start of the file.
+# 3. Use u'' to indicate a Unicode string.
+
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('unicode_python2.xlsx')
+worksheet = workbook.add_worksheet()
+
+worksheet.write('B3', u'Это фраза на русском!')
+
+workbook.close()
diff --git a/examples/unicode_python3.py b/examples/unicode_python3.py
new file mode 100644
index 0000000..5c7e730
--- /dev/null
+++ b/examples/unicode_python3.py
@@ -0,0 +1,22 @@
+###############################################################################
+#
+#
+# A simple Unicode spreadsheet in Python 3 using the XlsxWriter Python module.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# To write Unicode text in UTF-8 to a xlsxwriter file in Python 3:
+#
+# 1. Encode the file as UTF-8.
+#
+#
+
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('unicode_python3.xlsx')
+worksheet = workbook.add_worksheet()
+
+worksheet.write('B3', 'Это фраза на русском!')
+
+workbook.close()
diff --git a/examples/unicode_shift_jis.py b/examples/unicode_shift_jis.py
new file mode 100644
index 0000000..f63e036
--- /dev/null
+++ b/examples/unicode_shift_jis.py
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# A simple example of converting some Unicode text to an Excel file using
+# the XlsxWriter Python module.
+#
+# This example generates a spreadsheet with some Japanese text from a file
+# with Shift-JIS encoded text.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import codecs
+import xlsxwriter
+
+# Open the input file with the correct encoding.
+textfile = codecs.open('unicode_shift_jis.txt', 'r', 'shift_jis')
+
+# Create an new Excel file and convert the text data.
+workbook = xlsxwriter.Workbook('unicode_shift_jis.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Widen the first column to make the text clearer.
+worksheet.set_column('A:A', 50)
+
+# Start from the first cell.
+row = 0
+col = 0
+
+# Read the text file and write it to the worksheet.
+for line in textfile:
+ # Ignore the comments in the text file.
+ if line.startswith('#'):
+ continue
+
+ # Write any other lines to the worksheet.
+ worksheet.write(row, col, line.rstrip("\n"))
+ row += 1
+
+workbook.close()
diff --git a/examples/unicode_shift_jis.txt b/examples/unicode_shift_jis.txt
new file mode 100644
index 0000000..60fc196
--- /dev/null
+++ b/examples/unicode_shift_jis.txt
@@ -0,0 +1,35 @@
+#
+# XlsxWriter Unicode examples.
+#
+# Sample encoded text borrowed from Sean Burke's Pod::Simple distro.
+#
+# The text is encoded as Japanese Shift-JIS.
+#
+# See the unicode_shift_jis.py example.
+#
+Some uninteresting product specs found on the Net
+
+�^��
+
+S2763
+
+����
+
+GZ4 �_�C�N���C�b�N�~���[�����v 12V 10W�~1
+
+���@
+
+���E295 ���E365 ���E76mm
+
+����
+
+8.0kg
+
+�ގ�
+
+�����@�A���~�A�A���}�C�g�d��@�K���X
+
+���i
+
+76,000�~�i�����v�E�g�����X���݁j
+
diff --git a/examples/vbaProject.bin b/examples/vbaProject.bin
new file mode 100644
index 0000000..3d4d499
Binary files /dev/null and b/examples/vbaProject.bin differ
diff --git a/examples/vba_extract.py b/examples/vba_extract.py
new file mode 100644
index 0000000..e314b2d
--- /dev/null
+++ b/examples/vba_extract.py
@@ -0,0 +1,68 @@
+#!python
+
+##############################################################################
+#
+# vba_extract - A simple utility to extract a vbaProject.bin binary from an
+# Excel 2007+ xlsm file for insertion into an XlsxWriter file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import sys
+import shutil
+from zipfile import ZipFile
+from zipfile import BadZipfile
+
+# The VBA project file we want to extract.
+vba_filename = 'vbaProject.bin'
+
+# Get the xlsm file name from the commandline.
+if len(sys.argv) > 1:
+ xlsm_file = sys.argv[1]
+else:
+ print("\nUtility to extract a vbaProject.bin binary from an Excel 2007+ "
+ "xlsm macro file for insertion into an XlsxWriter file."
+ "\n"
+ "See: https://xlsxwriter.readthedocs.io/working_with_macros.html\n"
+ "\n"
+ "Usage: vba_extract file.xlsm\n")
+ exit()
+
+try:
+ # Open the Excel xlsm file as a zip file.
+ xlsm_zip = ZipFile(xlsm_file, 'r')
+
+ # Read the xl/vbaProject.bin file.
+ vba_data = xlsm_zip.read('xl/' + vba_filename)
+
+ # Write the vba data to a local file.
+ vba_file = open(vba_filename, "wb")
+ vba_file.write(vba_data)
+ vba_file.close()
+
+except IOError:
+ # Use exc_info() for Python 2.5+ compatibility.
+ e = sys.exc_info()[1]
+ print("File error: %s" % str(e))
+ exit()
+
+except KeyError:
+ # Usually when there isn't a xl/vbaProject.bin member in the file.
+ e = sys.exc_info()[1]
+ print("File error: %s" % str(e))
+ print("File may not be an Excel xlsm macro file: '%s'" % xlsm_file)
+ exit()
+
+except BadZipfile:
+ # Usually if the file is an xls file and not an xlsm file.
+ e = sys.exc_info()[1]
+ print("File error: %s: '%s'" % (str(e), xlsm_file))
+ print("File may not be an Excel xlsm macro file.")
+ exit()
+
+except:
+ # Catch any other exceptions.
+ e = sys.exc_info()[1]
+ print("File error: %s" % str(e))
+ exit()
+
+print("Extracted: %s" % vba_filename)
diff --git a/examples/worksheet_protection.py b/examples/worksheet_protection.py
new file mode 100644
index 0000000..43f23c6
--- /dev/null
+++ b/examples/worksheet_protection.py
@@ -0,0 +1,32 @@
+########################################################################
+#
+# Example of cell locking and formula hiding in an Excel worksheet
+# using Python and the XlsxWriter module.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import xlsxwriter
+
+workbook = xlsxwriter.Workbook('protection.xlsx')
+worksheet = workbook.add_worksheet()
+
+# Create some cell formats with protection properties.
+unlocked = workbook.add_format({'locked': 0})
+hidden = workbook.add_format({'hidden': 1})
+
+# Format the columns to make the text more visible.
+worksheet.set_column('A:A', 40)
+
+# Turn worksheet protection on.
+worksheet.protect()
+
+# Write a locked, unlocked and hidden cell.
+worksheet.write('A1', 'Cell B1 is locked. It cannot be edited.')
+worksheet.write('A2', 'Cell B2 is unlocked. It can be edited.')
+worksheet.write('A3', "Cell B3 is hidden. The formula isn't visible.")
+
+worksheet.write_formula('B1', '=1+2') # Locked by default.
+worksheet.write_formula('B2', '=1+2', unlocked)
+worksheet.write_formula('B3', '=1+2', hidden)
+
+workbook.close()
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..8951ea8
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,3 @@
+[wheel]
+universal = 1
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..8f9b8ec
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,54 @@
+import sys
+import subprocess
+from warnings import warn
+
+try:
+ from setuptools import setup, Command
+except ImportError:
+ from distutils.core import setup, Command
+
+if sys.version_info < (2, 5, 0):
+ warn("The minimum Python version supported by XlsxWriter is 2.5.")
+ exit()
+
+
+class PyTest(Command):
+
+ user_options = []
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ errno = subprocess.call(['python', '-m', 'unittest', 'discover'])
+ raise SystemExit(errno)
+
+setup(
+ name='XlsxWriter',
+ version='0.9.6',
+ author='John McNamara',
+ author_email='jmcnamara at cpan.org',
+ url='https://github.com/jmcnamara/XlsxWriter',
+ packages=['xlsxwriter'],
+ scripts=['examples/vba_extract.py'],
+ cmdclass={'test': PyTest},
+ license='BSD',
+ description='A Python module for creating Excel XLSX files.',
+ long_description=open('README.rst').read(),
+ classifiers=[
+ 'Development Status :: 5 - Production/Stable',
+ 'License :: OSI Approved :: BSD License',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2.5',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3.1',
+ 'Programming Language :: Python :: 3.2',
+ 'Programming Language :: Python :: 3.3',
+ 'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
+ ],
+)
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..ba949bb
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,8 @@
+# Tox config file. See http://tox.testrun.org/.
+
+[tox]
+envlist = py25, py26, py27, py32, py33
+
+[testenv]
+commands = {envpython} -m pytest -q
+deps = pytest
diff --git a/xlsxwriter/__init__.py b/xlsxwriter/__init__.py
new file mode 100644
index 0000000..960dbab
--- /dev/null
+++ b/xlsxwriter/__init__.py
@@ -0,0 +1,3 @@
+__version__ = '0.9.6'
+__VERSION__ = __version__
+from .workbook import Workbook
diff --git a/xlsxwriter/app.py b/xlsxwriter/app.py
new file mode 100644
index 0000000..0a0dd82
--- /dev/null
+++ b/xlsxwriter/app.py
@@ -0,0 +1,196 @@
+###############################################################################
+#
+# App - A class for writing the Excel XLSX App file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Package imports.
+from . import xmlwriter
+
+
+class App(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX App file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(App, self).__init__()
+
+ self.part_names = []
+ self.heading_pairs = []
+ self.properties = {}
+
+ def _add_part_name(self, part_name):
+ # Add the name of a workbook Part such as 'Sheet1' or 'Print_Titles'.
+ self.part_names.append(part_name)
+
+ def _add_heading_pair(self, heading_pair):
+ # Add the name of a workbook Heading Pair such as 'Worksheets',
+ # 'Charts' or 'Named Ranges'.
+
+ # Ignore empty pairs such as chartsheets.
+ if not heading_pair[1]:
+ return
+
+ self.heading_pairs.append(('lpstr', heading_pair[0]))
+ self.heading_pairs.append(('i4', heading_pair[1]))
+
+ def _set_properties(self, properties):
+ # Set the document properties.
+ self.properties = properties
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ self._write_properties()
+ self._write_application()
+ self._write_doc_security()
+ self._write_scale_crop()
+ self._write_heading_pairs()
+ self._write_titles_of_parts()
+ self._write_manager()
+ self._write_company()
+ self._write_links_up_to_date()
+ self._write_shared_doc()
+ self._write_hyperlink_base()
+ self._write_hyperlinks_changed()
+ self._write_app_version()
+
+ self._xml_end_tag('Properties')
+
+ # Close the file.
+ self._xml_close()
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_properties(self):
+ # Write the <Properties> element.
+ schema = 'http://schemas.openxmlformats.org/officeDocument/2006/'
+ xmlns = schema + 'extended-properties'
+ xmlns_vt = schema + 'docPropsVTypes'
+
+ attributes = [
+ ('xmlns', xmlns),
+ ('xmlns:vt', xmlns_vt),
+ ]
+
+ self._xml_start_tag('Properties', attributes)
+
+ def _write_application(self):
+ # Write the <Application> element.
+ self._xml_data_element('Application', 'Microsoft Excel')
+
+ def _write_doc_security(self):
+ # Write the <DocSecurity> element.
+ self._xml_data_element('DocSecurity', '0')
+
+ def _write_scale_crop(self):
+ # Write the <ScaleCrop> element.
+ self._xml_data_element('ScaleCrop', 'false')
+
+ def _write_heading_pairs(self):
+ # Write the <HeadingPairs> element.
+ self._xml_start_tag('HeadingPairs')
+ self._write_vt_vector('variant', self.heading_pairs)
+ self._xml_end_tag('HeadingPairs')
+
+ def _write_titles_of_parts(self):
+ # Write the <TitlesOfParts> element.
+ parts_data = []
+
+ self._xml_start_tag('TitlesOfParts')
+
+ for part_name in self.part_names:
+ parts_data.append(('lpstr', part_name))
+
+ self._write_vt_vector('lpstr', parts_data)
+
+ self._xml_end_tag('TitlesOfParts')
+
+ def _write_vt_vector(self, base_type, vector_data):
+ # Write the <vt:vector> element.
+ attributes = [
+ ('size', len(vector_data)),
+ ('baseType', base_type),
+ ]
+
+ self._xml_start_tag('vt:vector', attributes)
+
+ for vt_data in vector_data:
+ if base_type == 'variant':
+ self._xml_start_tag('vt:variant')
+
+ self._write_vt_data(vt_data)
+
+ if base_type == 'variant':
+ self._xml_end_tag('vt:variant')
+
+ self._xml_end_tag('vt:vector')
+
+ def _write_vt_data(self, vt_data):
+ # Write the <vt:*> elements such as <vt:lpstr> and <vt:if>.
+ self._xml_data_element("vt:%s" % vt_data[0], vt_data[1])
+
+ def _write_company(self):
+ company = self.properties.get('company', '')
+
+ self._xml_data_element('Company', company)
+
+ def _write_manager(self):
+ # Write the <Manager> element.
+ if 'manager' not in self.properties:
+ return
+
+ self._xml_data_element('Manager', self.properties['manager'])
+
+ def _write_links_up_to_date(self):
+ # Write the <LinksUpToDate> element.
+ self._xml_data_element('LinksUpToDate', 'false')
+
+ def _write_shared_doc(self):
+ # Write the <SharedDoc> element.
+ self._xml_data_element('SharedDoc', 'false')
+
+ def _write_hyperlink_base(self):
+ # Write the <HyperlinkBase> element.
+ hyperlink_base = self.properties.get('hyperlink_base')
+
+ if hyperlink_base is None:
+ return
+
+ self._xml_data_element('HyperlinkBase', hyperlink_base)
+
+ def _write_hyperlinks_changed(self):
+ # Write the <HyperlinksChanged> element.
+ self._xml_data_element('HyperlinksChanged', 'false')
+
+ def _write_app_version(self):
+ # Write the <AppVersion> element.
+ self._xml_data_element('AppVersion', '12.0000')
diff --git a/xlsxwriter/chart.py b/xlsxwriter/chart.py
new file mode 100644
index 0000000..975374c
--- /dev/null
+++ b/xlsxwriter/chart.py
@@ -0,0 +1,3972 @@
+###############################################################################
+#
+# Chart - A class for writing the Excel XLSX Worksheet file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import re
+import copy
+from warnings import warn
+
+from .shape import Shape
+from . import xmlwriter
+from .utility import get_rgb_color
+from .utility import xl_rowcol_to_cell
+from .utility import xl_range_formula
+from .utility import supported_datetime
+from .utility import datetime_to_excel_datetime
+from .utility import quote_sheetname
+
+
+class Chart(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX Chart file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self, options=None):
+ """
+ Constructor.
+
+ """
+
+ super(Chart, self).__init__()
+
+ self.subtype = None
+ self.sheet_type = 0x0200
+ self.orientation = 0x0
+ self.series = []
+ self.embedded = 0
+ self.id = -1
+ self.series_index = 0
+ self.style_id = 2
+ self.axis_ids = []
+ self.axis2_ids = []
+ self.cat_has_num_fmt = 0
+ self.requires_category = False
+ self.legend_position = 'right'
+ self.legend_delete_series = None
+ self.legend_font = None
+ self.legend_layout = None
+ self.cat_axis_position = 'b'
+ self.val_axis_position = 'l'
+ self.formula_ids = {}
+ self.formula_data = []
+ self.horiz_cat_axis = 0
+ self.horiz_val_axis = 1
+ self.protection = 0
+ self.chartarea = {}
+ self.plotarea = {}
+ self.x_axis = {}
+ self.y_axis = {}
+ self.y2_axis = {}
+ self.x2_axis = {}
+ self.chart_name = ''
+ self.show_blanks = 'gap'
+ self.show_hidden = 0
+ self.show_crosses = 1
+ self.width = 480
+ self.height = 288
+ self.x_scale = 1
+ self.y_scale = 1
+ self.x_offset = 0
+ self.y_offset = 0
+ self.table = None
+ self.cross_between = 'between'
+ self.default_marker = None
+ self.series_gap_1 = None
+ self.series_gap_2 = None
+ self.series_overlap_1 = None
+ self.series_overlap_2 = None
+ self.drop_lines = None
+ self.hi_low_lines = None
+ self.up_down_bars = None
+ self.smooth_allowed = False
+ self.title_font = None
+ self.title_name = None
+ self.title_formula = None
+ self.title_data_id = None
+ self.title_layout = None
+ self.title_overlay = None
+ self.title_none = False
+ self.date_category = False
+ self.date_1904 = False
+ self.remove_timezone = False
+ self.label_positions = {}
+ self.label_position_default = ''
+ self.already_inserted = False
+ self.combined = None
+ self.is_secondary = False
+ self._set_default_properties()
+
+ def add_series(self, options):
+ """
+ Add a data series to a chart.
+
+ Args:
+ options: A dictionary of chart series options.
+
+ Returns:
+ Nothing.
+
+ """
+ # Add a series and it's properties to a chart.
+
+ # Check that the required input has been specified.
+ if 'values' not in options:
+ warn("Must specify 'values' in add_series()")
+ return
+
+ if self.requires_category and 'categories' not in options:
+ warn("Must specify 'categories' in add_series() "
+ "for this chart type")
+ return
+
+ if len(self.series) == 255:
+ warn("The maximum number of series that can be added to an "
+ "Excel Chart is 255")
+ return
+
+ # Convert list into a formula string.
+ values = self._list_to_formula(options.get('values'))
+ categories = self._list_to_formula(options.get('categories'))
+
+ # Switch name and name_formula parameters if required.
+ name, name_formula = self._process_names(options.get('name'),
+ options.get('name_formula'))
+
+ # Get an id for the data equivalent to the range formula.
+ cat_id = self._get_data_id(categories, options.get('categories_data'))
+ val_id = self._get_data_id(values, options.get('values_data'))
+ name_id = self._get_data_id(name_formula, options.get('name_data'))
+
+ # Set the line properties for the series.
+ line = Shape._get_line_properties(options.get('line'))
+
+ # Allow 'border' as a synonym for 'line' in bar/column style charts.
+ if options.get('border'):
+ line = Shape._get_line_properties(options['border'])
+
+ # Set the fill properties for the series.
+ fill = Shape._get_fill_properties(options.get('fill'))
+
+ # Set the pattern fill properties for the series.
+ pattern = Shape._get_pattern_properties(options.get('pattern'))
+
+ # Set the gradient fill properties for the series.
+ gradient = Shape._get_gradient_properties(options.get('gradient'))
+
+ # Pattern fill overrides solid fill.
+ if pattern:
+ self.fill = None
+
+ # Gradient fill overrides the solid and pattern fill.
+ if gradient:
+ pattern = None
+ fill = None
+
+ # Set the marker properties for the series.
+ marker = self._get_marker_properties(options.get('marker'))
+
+ # Set the trendline properties for the series.
+ trendline = self._get_trendline_properties(options.get('trendline'))
+
+ # Set the line smooth property for the series.
+ smooth = options.get('smooth')
+
+ # Set the error bars properties for the series.
+ y_error_bars = self._get_error_bars_props(options.get('y_error_bars'))
+ x_error_bars = self._get_error_bars_props(options.get('x_error_bars'))
+
+ error_bars = {'x_error_bars': x_error_bars,
+ 'y_error_bars': y_error_bars}
+
+ # Set the point properties for the series.
+ points = self._get_points_properties(options.get('points'))
+
+ # Set the labels properties for the series.
+ labels = self._get_labels_properties(options.get('data_labels'))
+
+ # Set the "invert if negative" fill property.
+ invert_if_neg = options.get('invert_if_negative', False)
+
+ # Set the secondary axis properties.
+ x2_axis = options.get('x2_axis')
+ y2_axis = options.get('y2_axis')
+
+ # Store secondary status for combined charts.
+ if x2_axis or y2_axis:
+ self.is_secondary = True
+
+ # Set the gap for Bar/Column charts.
+ if options.get('gap') is not None:
+ if y2_axis:
+ self.series_gap_2 = options['gap']
+ else:
+ self.series_gap_1 = options['gap']
+
+ # Set the overlap for Bar/Column charts.
+ if options.get('overlap'):
+ if y2_axis:
+ self.series_overlap_2 = options['overlap']
+ else:
+ self.series_overlap_1 = options['overlap']
+
+ # Add the user supplied data to the internal structures.
+ series = {
+ 'values': values,
+ 'categories': categories,
+ 'name': name,
+ 'name_formula': name_formula,
+ 'name_id': name_id,
+ 'val_data_id': val_id,
+ 'cat_data_id': cat_id,
+ 'line': line,
+ 'fill': fill,
+ 'pattern': pattern,
+ 'gradient': gradient,
+ 'marker': marker,
+ 'trendline': trendline,
+ 'labels': labels,
+ 'invert_if_neg': invert_if_neg,
+ 'x2_axis': x2_axis,
+ 'y2_axis': y2_axis,
+ 'points': points,
+ 'error_bars': error_bars,
+ 'smooth': smooth
+ }
+
+ self.series.append(series)
+
+ def set_x_axis(self, options):
+ """
+ Set the chart X axis options.
+
+ Args:
+ options: A dictionary of axis options.
+
+ Returns:
+ Nothing.
+
+ """
+ axis = self._convert_axis_args(self.x_axis, options)
+
+ self.x_axis = axis
+
+ def set_y_axis(self, options):
+ """
+ Set the chart Y axis options.
+
+ Args:
+ options: A dictionary of axis options.
+
+ Returns:
+ Nothing.
+
+ """
+ axis = self._convert_axis_args(self.y_axis, options)
+
+ self.y_axis = axis
+
+ def set_x2_axis(self, options):
+ """
+ Set the chart secondary X axis options.
+
+ Args:
+ options: A dictionary of axis options.
+
+ Returns:
+ Nothing.
+
+ """
+ axis = self._convert_axis_args(self.x2_axis, options)
+
+ self.x2_axis = axis
+
+ def set_y2_axis(self, options):
+ """
+ Set the chart secondary Y axis options.
+
+ Args:
+ options: A dictionary of axis options.
+
+ Returns:
+ Nothing.
+
+ """
+ axis = self._convert_axis_args(self.y2_axis, options)
+
+ self.y2_axis = axis
+
+ def set_title(self, options):
+ """
+ Set the chart title options.
+
+ Args:
+ options: A dictionary of chart title options.
+
+ Returns:
+ Nothing.
+
+ """
+ name, name_formula = self._process_names(options.get('name'),
+ options.get('name_formula'))
+
+ data_id = self._get_data_id(name_formula, options.get('data'))
+
+ self.title_name = name
+ self.title_formula = name_formula
+ self.title_data_id = data_id
+
+ # Set the font properties if present.
+ self.title_font = self._convert_font_args(options.get('name_font'))
+
+ # Set the axis name layout.
+ self.title_layout = self._get_layout_properties(options.get('layout'),
+ True)
+ # Set the title overlay option.
+ self.title_overlay = options.get('overlay')
+
+ # Set the automatic title option.
+ self.title_none = options.get('none')
+
+ def set_legend(self, options):
+ """
+ Set the chart legend options.
+
+ Args:
+ options: A dictionary of chart legend options.
+
+ Returns:
+ Nothing.
+ """
+ self.legend_position = options.get('position', 'right')
+ self.legend_delete_series = options.get('delete_series')
+ self.legend_font = self._convert_font_args(options.get('font'))
+ self.legend_layout = self._get_layout_properties(options.get('layout'),
+ False)
+ # Turn off the legend.
+ if options.get('none'):
+ self.legend_position = 'none'
+
+ def set_plotarea(self, options):
+ """
+ Set the chart plot area options.
+
+ Args:
+ options: A dictionary of chart plot area options.
+
+ Returns:
+ Nothing.
+ """
+ # Convert the user defined properties to internal properties.
+ self.plotarea = self._get_area_properties(options)
+
+ def set_chartarea(self, options):
+ """
+ Set the chart area options.
+
+ Args:
+ options: A dictionary of chart area options.
+
+ Returns:
+ Nothing.
+ """
+ # Convert the user defined properties to internal properties.
+ self.chartarea = self._get_area_properties(options)
+
+ def set_style(self, style_id):
+ """
+ Set the chart style type.
+
+ Args:
+ style_id: An int representing the chart style.
+
+ Returns:
+ Nothing.
+ """
+ # Set one of the 48 built-in Excel chart styles. The default is 2.
+ if style_id is None:
+ style_id = 2
+
+ if style_id < 0 or style_id > 48:
+ style_id = 2
+
+ self.style_id = style_id
+
+ def show_blanks_as(self, option):
+ """
+ Set the option for displaying blank data in a chart.
+
+ Args:
+ option: A string representing the display option.
+
+ Returns:
+ Nothing.
+ """
+ if not option:
+ return
+
+ valid_options = {
+ 'gap': 1,
+ 'zero': 1,
+ 'span': 1,
+ }
+
+ if option not in valid_options:
+ warn("Unknown show_blanks_as() option '%s'" % option)
+ return
+
+ self.show_blanks = option
+
+ def show_hidden_data(self):
+ """
+ Display data on charts from hidden rows or columns.
+
+ Args:
+ option: A string representing the display option.
+
+ Returns:
+ Nothing.
+ """
+ self.show_hidden = 1
+
+ def set_size(self, options):
+ """
+ Set size or scale of the chart.
+
+ Args:
+ options: A dictionary of chart size options.
+
+ Returns:
+ Nothing.
+ """
+
+ # Set dimensions or scale for the chart.
+ self.width = options.get('width', self.width)
+ self.height = options.get('height', self.height)
+ self.x_scale = options.get('x_scale', 1)
+ self.y_scale = options.get('y_scale', 1)
+ self.x_offset = options.get('x_offset', 0)
+ self.y_offset = options.get('y_offset', 0)
+
+ def set_table(self, options=None):
+ """
+ Set properties for an axis data table.
+
+ Args:
+ options: A dictionary of axis table options.
+
+ Returns:
+ Nothing.
+
+ """
+ if options is None:
+ options = {}
+
+ table = {}
+
+ table['horizontal'] = options.get('horizontal', 1)
+ table['vertical'] = options.get('vertical', 1)
+ table['outline'] = options.get('outline', 1)
+ table['show_keys'] = options.get('show_keys', 0)
+ table['font'] = self._convert_font_args(options.get('font'))
+
+ self.table = table
+
+ def set_up_down_bars(self, options=None):
+ """
+ Set properties for the chart up-down bars.
+
+ Args:
+ options: A dictionary of options.
+
+ Returns:
+ Nothing.
+
+ """
+ if options is None:
+ options = {}
+
+ # Defaults.
+ up_line = None
+ up_fill = None
+ down_line = None
+ down_fill = None
+
+ # Set properties for 'up' bar.
+ if options.get('up'):
+ if 'border' in options['up']:
+ # Map border to line.
+ up_line = Shape._get_line_properties(options['up']['border'])
+
+ if 'line' in options['up']:
+ up_line = Shape._get_line_properties(options['up']['line'])
+
+ if 'fill' in options['up']:
+ up_fill = Shape._get_line_properties(options['up']['fill'])
+
+ # Set properties for 'down' bar.
+ if options.get('down'):
+ if 'border' in options['down']:
+ # Map border to line.
+ down_line = \
+ Shape._get_line_properties(options['down']['border'])
+
+ if 'line' in options['down']:
+ down_line = Shape._get_line_properties(options['down']['line'])
+
+ if 'fill' in options['down']:
+ down_fill = Shape._get_line_properties(options['down']['fill'])
+
+ self.up_down_bars = {'up': {'line': up_line,
+ 'fill': up_fill,
+ },
+ 'down': {'line': down_line,
+ 'fill': down_fill,
+ },
+ }
+
+ def set_drop_lines(self, options=None):
+ """
+ Set properties for the chart drop lines.
+
+ Args:
+ options: A dictionary of options.
+
+ Returns:
+ Nothing.
+
+ """
+ if options is None:
+ options = {}
+
+ line = Shape._get_line_properties(options.get('line'))
+ fill = Shape._get_fill_properties(options.get('fill'))
+
+ # Set the pattern fill properties for the series.
+ pattern = Shape._get_pattern_properties(options.get('pattern'))
+
+ # Set the gradient fill properties for the series.
+ gradient = Shape._get_gradient_properties(options.get('gradient'))
+
+ # Pattern fill overrides solid fill.
+ if pattern:
+ self.fill = None
+
+ # Gradient fill overrides the solid and pattern fill.
+ if gradient:
+ pattern = None
+ fill = None
+
+ self.drop_lines = {'line': line,
+ 'fill': fill,
+ 'pattern': pattern,
+ 'gradient': gradient}
+
+ def set_high_low_lines(self, options=None):
+ """
+ Set properties for the chart high-low lines.
+
+ Args:
+ options: A dictionary of options.
+
+ Returns:
+ Nothing.
+
+ """
+ if options is None:
+ options = {}
+
+ line = Shape._get_line_properties(options.get('line'))
+ fill = Shape._get_fill_properties(options.get('fill'))
+
+ # Set the pattern fill properties for the series.
+ pattern = Shape._get_pattern_properties(options.get('pattern'))
+
+ # Set the gradient fill properties for the series.
+ gradient = Shape._get_gradient_properties(options.get('gradient'))
+
+ # Pattern fill overrides solid fill.
+ if pattern:
+ self.fill = None
+
+ # Gradient fill overrides the solid and pattern fill.
+ if gradient:
+ pattern = None
+ fill = None
+
+ self.hi_low_lines = {'line': line,
+ 'fill': fill,
+ 'pattern': pattern,
+ 'gradient': gradient}
+
+ def combine(self, chart=None):
+ """
+ Create a combination chart with a secondary chart.
+
+ Args:
+ chart: The secondary chart to combine with the primary chart.
+
+ Returns:
+ Nothing.
+
+ """
+ if chart is None:
+ return
+
+ self.combined = chart
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ # Write the c:chartSpace element.
+ self._write_chart_space()
+
+ # Write the c:lang element.
+ self._write_lang()
+
+ # Write the c:style element.
+ self._write_style()
+
+ # Write the c:protection element.
+ self._write_protection()
+
+ # Write the c:chart element.
+ self._write_chart()
+
+ # Write the c:spPr element for the chartarea formatting.
+ self._write_sp_pr(self.chartarea)
+
+ # Write the c:printSettings element.
+ if self.embedded:
+ self._write_print_settings()
+
+ # Close the worksheet tag.
+ self._xml_end_tag('c:chartSpace')
+ # Close the file.
+ self._xml_close()
+
+ def _convert_axis_args(self, axis, user_options):
+ # Convert user defined axis values into private hash values.
+ options = axis['defaults'].copy()
+ options.update(user_options)
+
+ name, name_formula = self._process_names(options.get('name'),
+ options.get('name_formula'))
+
+ data_id = self._get_data_id(name_formula, options.get('data'))
+
+ axis = {
+ 'defaults': axis['defaults'],
+ 'name': name,
+ 'formula': name_formula,
+ 'data_id': data_id,
+ 'reverse': options.get('reverse'),
+ 'min': options.get('min'),
+ 'max': options.get('max'),
+ 'minor_unit': options.get('minor_unit'),
+ 'major_unit': options.get('major_unit'),
+ 'minor_unit_type': options.get('minor_unit_type'),
+ 'major_unit_type': options.get('major_unit_type'),
+ 'display_units': options.get('display_units'),
+ 'log_base': options.get('log_base'),
+ 'crossing': options.get('crossing'),
+ 'position_axis': options.get('position_axis'),
+ 'position': options.get('position'),
+ 'label_position': options.get('label_position'),
+ 'num_format': options.get('num_format'),
+ 'num_format_linked': options.get('num_format_linked'),
+ 'interval_unit': options.get('interval_unit'),
+ 'interval_tick': options.get('interval_tick'),
+ 'text_axis': False,
+ }
+
+ if 'visible' in options:
+ axis['visible'] = options.get('visible')
+ else:
+ axis['visible'] = 1
+
+ # Convert the display units.
+ axis['display_units'] = self._get_display_units(axis['display_units'])
+ axis['display_units_visible'] = \
+ options.get('display_units_visible', True)
+
+ # Map major_gridlines properties.
+ if (options.get('major_gridlines')
+ and options['major_gridlines']['visible']):
+ axis['major_gridlines'] = \
+ self._get_gridline_properties(options['major_gridlines'])
+
+ # Map minor_gridlines properties.
+ if (options.get('minor_gridlines')
+ and options['minor_gridlines']['visible']):
+ axis['minor_gridlines'] = \
+ self._get_gridline_properties(options['minor_gridlines'])
+
+ # Only use the first letter of bottom, top, left or right.
+ if axis.get('position'):
+ axis['position'] = axis['position'].lower()[0]
+
+ # Set the position for a category axis on or between the tick marks.
+ if axis.get('position_axis'):
+ if axis['position_axis'] == 'on_tick':
+ axis['position_axis'] = 'midCat'
+ elif axis['position_axis'] == 'between':
+ # Doesn't need to be modified.
+ pass
+ else:
+ # Otherwise use the default value.
+ axis['position_axis'] = None
+
+ # Set the category axis as a date axis.
+ if options.get('date_axis'):
+ self.date_category = True
+
+ # Set the category axis as a text axis.
+ if options.get('text_axis'):
+ self.date_category = False
+ axis['text_axis'] = True
+
+ # Convert datetime args if required.
+ if axis.get('min') and supported_datetime(axis['min']):
+ axis['min'] = datetime_to_excel_datetime(axis['min'],
+ self.date_1904,
+ self.remove_timezone)
+ if axis.get('max') and supported_datetime(axis['max']):
+ axis['max'] = datetime_to_excel_datetime(axis['max'],
+ self.date_1904,
+ self.remove_timezone)
+ if axis.get('crossing') and supported_datetime(axis['crossing']):
+ axis['crossing'] = datetime_to_excel_datetime(axis['crossing'],
+ self.date_1904,
+ self.remove_timezone)
+
+ # Set the font properties if present.
+ axis['num_font'] = self._convert_font_args(options.get('num_font'))
+ axis['name_font'] = self._convert_font_args(options.get('name_font'))
+
+ # Set the axis name layout.
+ axis['name_layout'] = \
+ self._get_layout_properties(options.get('name_layout'), True)
+
+ # Set the line properties for the axis.
+ axis['line'] = Shape._get_line_properties(options.get('line'))
+
+ # Set the fill properties for the axis.
+ axis['fill'] = Shape._get_fill_properties(options.get('fill'))
+
+ # Set the pattern fill properties for the series.
+ axis['pattern'] = Shape._get_pattern_properties(options.get('pattern'))
+
+ # Set the gradient fill properties for the series.
+ axis['gradient'] = \
+ Shape._get_gradient_properties(options.get('gradient'))
+
+ # Pattern fill overrides solid fill.
+ if axis.get('pattern'):
+ axis['fill'] = None
+
+ # Gradient fill overrides the solid and pattern fill.
+ if axis.get('gradient'):
+ axis['pattern'] = None
+ axis['fill'] = None
+
+ # Set the tick marker types.
+ axis['minor_tick_mark'] = \
+ self._get_tick_type(options.get('minor_tick_mark'))
+ axis['major_tick_mark'] = \
+ self._get_tick_type(options.get('major_tick_mark'))
+
+ return axis
+
+ def _convert_font_args(self, options):
+ # Convert user defined font values into private dict values.
+ if not options:
+ return
+
+ font = {
+ 'name': options.get('name'),
+ 'color': options.get('color'),
+ 'size': options.get('size'),
+ 'bold': options.get('bold'),
+ 'italic': options.get('italic'),
+ 'underline': options.get('underline'),
+ 'pitch_family': options.get('pitch_family'),
+ 'charset': options.get('charset'),
+ 'baseline': options.get('baseline', 0),
+ 'rotation': options.get('rotation'),
+ }
+
+ # Convert font size units.
+ if font['size']:
+ font['size'] = int(font['size'] * 100)
+
+ # Convert rotation into 60,000ths of a degree.
+ if font['rotation']:
+ font['rotation'] = 60000 * int(font['rotation'])
+
+ return font
+
+ def _list_to_formula(self, data):
+ # Convert and list of row col values to a range formula.
+
+ # If it isn't an array ref it is probably a formula already.
+ if type(data) is not list:
+ return data
+
+ formula = xl_range_formula(*data)
+
+ return formula
+
+ def _process_names(self, name, name_formula):
+ # Switch name and name_formula parameters if required.
+
+ if name is not None:
+ if isinstance(name, list):
+ # Convert an list of values into a name formula.
+ cell = xl_rowcol_to_cell(name[1], name[2], True, True)
+ name_formula = quote_sheetname(name[0]) + '!' + cell
+ name = ''
+ elif re.match(r'^=?[^!]+!\$?[A-Z]+\$?[0-9]+', name):
+ # Name looks like a formula, use it to set name_formula.
+ name_formula = name
+ name = ''
+
+ return name, name_formula
+
+ def _get_data_type(self, data):
+ # Find the overall type of the data associated with a series.
+
+ # Check for no data in the series.
+ if data is None or len(data) == 0:
+ return 'none'
+
+ if isinstance(data[0], list):
+ return 'multi_str'
+
+ # Determine if data is numeric or strings.
+ for token in data:
+ if token is None:
+ continue
+
+ try:
+ float(token)
+ except ValueError:
+ # Not a number. Assume entire data series is string data.
+ return 'str'
+
+ # The series data was all numeric.
+ return 'num'
+
+ def _get_data_id(self, formula, data):
+ # Assign an id to a each unique series formula or title/axis formula.
+ # Repeated formulas such as for categories get the same id. If the
+ # series or title has user specified data associated with it then
+ # that is also stored. This data is used to populate cached Excel
+ # data when creating a chart. If there is no user defined data then
+ # it will be populated by the parent Workbook._add_chart_data().
+
+ # Ignore series without a range formula.
+ if not formula:
+ return
+
+ # Strip the leading '=' from the formula.
+ if formula.startswith('='):
+ formula = formula.lstrip('=')
+
+ # Store the data id in a hash keyed by the formula and store the data
+ # in a separate array with the same id.
+ if formula not in self.formula_ids:
+ # Haven't seen this formula before.
+ formula_id = len(self.formula_data)
+
+ self.formula_data.append(data)
+ self.formula_ids[formula] = formula_id
+ else:
+ # Formula already seen. Return existing id.
+ formula_id = self.formula_ids[formula]
+
+ # Store user defined data if it isn't already there.
+ if self.formula_data[formula_id] is None:
+ self.formula_data[formula_id] = data
+
+ return formula_id
+
+ def _get_marker_properties(self, marker):
+ # Convert user marker properties to the structure required internally.
+
+ if not marker:
+ return
+
+ # Copy the user defined properties since they will be modified.
+ marker = copy.deepcopy(marker)
+
+ types = {
+ 'automatic': 'automatic',
+ 'none': 'none',
+ 'square': 'square',
+ 'diamond': 'diamond',
+ 'triangle': 'triangle',
+ 'x': 'x',
+ 'star': 'star',
+ 'dot': 'dot',
+ 'short_dash': 'dot',
+ 'dash': 'dash',
+ 'long_dash': 'dash',
+ 'circle': 'circle',
+ 'plus': 'plus',
+ 'picture': 'picture',
+ }
+
+ # Check for valid types.
+ marker_type = marker.get('type')
+
+ if marker_type is not None:
+ if marker_type == 'automatic':
+ marker['automatic'] = 1
+
+ if marker_type in types:
+ marker['type'] = types[marker_type]
+ else:
+ warn("Unknown marker type '%s" % marker_type)
+ return
+
+ # Set the line properties for the marker.
+ line = Shape._get_line_properties(marker.get('line'))
+
+ # Allow 'border' as a synonym for 'line'.
+ if 'border' in marker:
+ line = Shape._get_line_properties(marker['border'])
+
+ # Set the fill properties for the marker.
+ fill = Shape._get_fill_properties(marker.get('fill'))
+
+ # Set the pattern fill properties for the series.
+ pattern = Shape._get_pattern_properties(marker.get('pattern'))
+
+ # Set the gradient fill properties for the series.
+ gradient = Shape._get_gradient_properties(marker.get('gradient'))
+
+ # Pattern fill overrides solid fill.
+ if pattern:
+ self.fill = None
+
+ # Gradient fill overrides the solid and pattern fill.
+ if gradient:
+ pattern = None
+ fill = None
+
+ marker['line'] = line
+ marker['fill'] = fill
+ marker['pattern'] = pattern
+ marker['gradient'] = gradient
+
+ return marker
+
+ def _get_trendline_properties(self, trendline):
+ # Convert user trendline properties to structure required internally.
+
+ if not trendline:
+ return
+
+ # Copy the user defined properties since they will be modified.
+ trendline = copy.deepcopy(trendline)
+
+ types = {
+ 'exponential': 'exp',
+ 'linear': 'linear',
+ 'log': 'log',
+ 'moving_average': 'movingAvg',
+ 'polynomial': 'poly',
+ 'power': 'power',
+ }
+
+ # Check the trendline type.
+ trend_type = trendline.get('type')
+
+ if trend_type in types:
+ trendline['type'] = types[trend_type]
+ else:
+ warn("Unknown trendline type '%s'" % trend_type)
+ return
+
+ # Set the line properties for the trendline.
+ line = Shape._get_line_properties(trendline.get('line'))
+
+ # Allow 'border' as a synonym for 'line'.
+ if 'border' in trendline:
+ line = Shape._get_line_properties(trendline['border'])
+
+ # Set the fill properties for the trendline.
+ fill = Shape._get_fill_properties(trendline.get('fill'))
+
+ # Set the pattern fill properties for the series.
+ pattern = Shape._get_pattern_properties(trendline.get('pattern'))
+
+ # Set the gradient fill properties for the series.
+ gradient = Shape._get_gradient_properties(trendline.get('gradient'))
+
+ # Pattern fill overrides solid fill.
+ if pattern:
+ self.fill = None
+
+ # Gradient fill overrides the solid and pattern fill.
+ if gradient:
+ pattern = None
+ fill = None
+
+ trendline['line'] = line
+ trendline['fill'] = fill
+ trendline['pattern'] = pattern
+ trendline['gradient'] = gradient
+
+ return trendline
+
+ def _get_error_bars_props(self, options):
+ # Convert user error bars properties to structure required internally.
+ if not options:
+ return
+
+ # Default values.
+ error_bars = {
+ 'type': 'fixedVal',
+ 'value': 1,
+ 'endcap': 1,
+ 'direction': 'both'
+ }
+
+ types = {
+ 'fixed': 'fixedVal',
+ 'percentage': 'percentage',
+ 'standard_deviation': 'stdDev',
+ 'standard_error': 'stdErr',
+ 'custom': 'cust',
+ }
+
+ # Check the error bars type.
+ error_type = options['type']
+
+ if error_type in types:
+ error_bars['type'] = types[error_type]
+ else:
+ warn("Unknown error bars type '%s" % error_type)
+ return
+
+ # Set the value for error types that require it.
+ if 'value' in options:
+ error_bars['value'] = options['value']
+
+ # Set the end-cap style.
+ if 'end_style' in options:
+ error_bars['endcap'] = options['end_style']
+
+ # Set the error bar direction.
+ if 'direction' in options:
+ if options['direction'] == 'minus':
+ error_bars['direction'] = 'minus'
+ elif options['direction'] == 'plus':
+ error_bars['direction'] = 'plus'
+ else:
+ # Default to 'both'.
+ pass
+
+ # Set any custom values.
+ error_bars['plus_values'] = options.get('plus_values')
+ error_bars['minus_values'] = options.get('minus_values')
+ error_bars['plus_data'] = options.get('plus_data')
+ error_bars['minus_data'] = options.get('minus_data')
+
+ # Set the line properties for the error bars.
+ error_bars['line'] = Shape._get_line_properties(options.get('line'))
+ error_bars['fill'] = Shape._get_line_properties(options.get('fill'))
+
+ return error_bars
+
+ def _get_gridline_properties(self, options):
+ # Convert user gridline properties to structure required internally.
+
+ # Set the visible property for the gridline.
+ gridline = {'visible': options.get('visible')}
+
+ # Set the line properties for the gridline.
+ gridline['line'] = Shape._get_line_properties(options.get('line'))
+ gridline['fill'] = Shape._get_line_properties(options.get('fill'))
+
+ return gridline
+
+ def _get_labels_properties(self, labels):
+ # Convert user labels properties to the structure required internally.
+
+ if not labels:
+ return None
+
+ # Copy the user defined properties since they will be modified.
+ labels = copy.deepcopy(labels)
+
+ # Map user defined label positions to Excel positions.
+ position = labels.get('position')
+
+ if position:
+ if position in self.label_positions:
+ if position == self.label_position_default:
+ labels['position'] = None
+ else:
+ labels['position'] = self.label_positions[position]
+ else:
+ warn("Unsupported label position '%s' for this chart type"
+ % position)
+ return
+
+ # Map the user defined label separator to the Excel separator.
+ separator = labels.get('separator')
+ separators = {
+ ',': ', ',
+ ';': '; ',
+ '.': '. ',
+ "\n": "\n",
+ ' ': ' ',
+ }
+
+ if separator:
+ if separator in separators:
+ labels['separator'] = separators[separator]
+ else:
+ warn("Unsupported label separator")
+ return
+
+ # Set the font properties if present.
+ labels['font'] = self._convert_font_args(labels.get('font'))
+
+ return labels
+
+ def _get_area_properties(self, options):
+ # Convert user area properties to the structure required internally.
+ area = {}
+
+ # Set the line properties for the chartarea.
+ line = Shape._get_line_properties(options.get('line'))
+
+ # Allow 'border' as a synonym for 'line'.
+ if options.get('border'):
+ line = Shape._get_line_properties(options['border'])
+
+ # Set the fill properties for the chartarea.
+ fill = Shape._get_fill_properties(options.get('fill'))
+
+ # Set the pattern fill properties for the series.
+ pattern = Shape._get_pattern_properties(options.get('pattern'))
+
+ # Set the gradient fill properties for the series.
+ gradient = Shape._get_gradient_properties(options.get('gradient'))
+
+ # Pattern fill overrides solid fill.
+ if pattern:
+ self.fill = None
+
+ # Gradient fill overrides the solid and pattern fill.
+ if gradient:
+ pattern = None
+ fill = None
+
+ # Set the plotarea layout.
+ layout = self._get_layout_properties(options.get('layout'), False)
+
+ area['line'] = line
+ area['fill'] = fill
+ area['pattern'] = pattern
+ area['layout'] = layout
+ area['gradient'] = gradient
+
+ return area
+
+ def _get_layout_properties(self, args, is_text):
+ # Convert user defined layout properties to format used internally.
+ layout = {}
+
+ if not args:
+ return
+
+ if is_text:
+ properties = ('x', 'y')
+ else:
+ properties = ('x', 'y', 'width', 'height')
+
+ # Check for valid properties.
+ for key in args.keys():
+ if key not in properties:
+ warn("Property '%s' allowed not in layout options" % key)
+ return
+
+ # Set the layout properties.
+ for prop in properties:
+ if prop not in args.keys():
+ warn("Property '%s' must be specified in layout options"
+ % prop)
+ return
+
+ value = args[prop]
+
+ try:
+ float(value)
+ except ValueError:
+ warn("Property '%s' value '%s' must be numeric in layout" %
+ (prop, value))
+ return
+
+ if value < 0 or value > 1:
+ warn("Property '%s' value '%s' must be in range "
+ "0 < x <= 1 in layout options" % (prop, value))
+ return
+
+ # Convert to the format used by Excel for easier testing
+ layout[prop] = "%.17g" % value
+
+ return layout
+
+ def _get_points_properties(self, user_points):
+ # Convert user points properties to structure required internally.
+ points = []
+
+ if not user_points:
+ return
+
+ for user_point in user_points:
+ point = {}
+
+ if user_point is not None:
+
+ # Set the line properties for the point.
+ line = Shape._get_line_properties(user_point.get('line'))
+
+ # Allow 'border' as a synonym for 'line'.
+ if 'border' in user_point:
+ line = Shape._get_line_properties(user_point['border'])
+
+ # Set the fill properties for the chartarea.
+ fill = Shape._get_fill_properties(user_point.get('fill'))
+
+ # Set the pattern fill properties for the series.
+ pattern = \
+ Shape._get_pattern_properties(user_point.get('pattern'))
+
+ # Set the gradient fill properties for the series.
+ gradient = \
+ Shape._get_gradient_properties(user_point.get('gradient'))
+
+ # Pattern fill overrides solid fill.
+ if pattern:
+ self.fill = None
+
+ # Gradient fill overrides the solid and pattern fill.
+ if gradient:
+ pattern = None
+ fill = None
+
+ point['line'] = line
+ point['fill'] = fill
+ point['pattern'] = pattern
+ point['gradient'] = gradient
+
+ points.append(point)
+
+ return points
+
+ def _get_display_units(self, display_units):
+ # Convert user defined display units to internal units.
+ if not display_units:
+ return
+
+ types = {
+ 'hundreds': 'hundreds',
+ 'thousands': 'thousands',
+ 'ten_thousands': 'tenThousands',
+ 'hundred_thousands': 'hundredThousands',
+ 'millions': 'millions',
+ 'ten_millions': 'tenMillions',
+ 'hundred_millions': 'hundredMillions',
+ 'billions': 'billions',
+ 'trillions': 'trillions',
+ }
+
+ if display_units in types:
+ display_units = types[display_units]
+ else:
+ warn("Unknown display_units type '%s'" % display_units)
+ return
+
+ return display_units
+
+ def _get_tick_type(self, tick_type):
+ # Convert user defined display units to internal units.
+ if not tick_type:
+ return
+
+ types = {
+ 'outside': 'out',
+ 'inside': 'in',
+ 'none': 'none',
+ 'cross': 'cross',
+ }
+
+ if tick_type in types:
+ tick_type = types[tick_type]
+ else:
+ warn("Unknown tick_type '%s'" % tick_type)
+ return
+
+ return tick_type
+
+ def _get_primary_axes_series(self):
+ # Returns series which use the primary axes.
+ primary_axes_series = []
+
+ for series in self.series:
+ if not series['y2_axis']:
+ primary_axes_series.append(series)
+
+ return primary_axes_series
+
+ def _get_secondary_axes_series(self):
+ # Returns series which use the secondary axes.
+ secondary_axes_series = []
+
+ for series in self.series:
+ if series['y2_axis']:
+ secondary_axes_series.append(series)
+
+ return secondary_axes_series
+
+ def _add_axis_ids(self, args):
+ # Add unique ids for primary or secondary axes
+ chart_id = 5001 + int(self.id)
+ axis_count = 1 + len(self.axis2_ids) + len(self.axis_ids)
+
+ id1 = '%04d%04d' % (chart_id, axis_count)
+ id2 = '%04d%04d' % (chart_id, axis_count + 1)
+
+ if args['primary_axes']:
+ self.axis_ids.append(id1)
+ self.axis_ids.append(id2)
+
+ if not args['primary_axes']:
+ self.axis2_ids.append(id1)
+ self.axis2_ids.append(id2)
+
+ def _set_default_properties(self):
+ # Setup the default properties for a chart.
+
+ self.x_axis['defaults'] = {
+ 'num_format': 'General',
+ 'major_gridlines': {'visible': 0}
+ }
+
+ self.y_axis['defaults'] = {
+ 'num_format': 'General',
+ 'major_gridlines': {'visible': 1}
+ }
+
+ self.x2_axis['defaults'] = {
+ 'num_format': 'General',
+ 'label_position': 'none',
+ 'crossing': 'max',
+ 'visible': 0
+ }
+
+ self.y2_axis['defaults'] = {
+ 'num_format': 'General',
+ 'major_gridlines': {'visible': 0},
+ 'position': 'right',
+ 'visible': 1
+ }
+
+ self.set_x_axis({})
+ self.set_y_axis({})
+
+ self.set_x2_axis({})
+ self.set_y2_axis({})
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_chart_space(self):
+ # Write the <c:chartSpace> element.
+ schema = 'http://schemas.openxmlformats.org/'
+ xmlns_c = schema + 'drawingml/2006/chart'
+ xmlns_a = schema + 'drawingml/2006/main'
+ xmlns_r = schema + 'officeDocument/2006/relationships'
+
+ attributes = [
+ ('xmlns:c', xmlns_c),
+ ('xmlns:a', xmlns_a),
+ ('xmlns:r', xmlns_r),
+ ]
+
+ self._xml_start_tag('c:chartSpace', attributes)
+
+ def _write_lang(self):
+ # Write the <c:lang> element.
+ val = 'en-US'
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:lang', attributes)
+
+ def _write_style(self):
+ # Write the <c:style> element.
+ style_id = self.style_id
+
+ # Don't write an element for the default style, 2.
+ if style_id == 2:
+ return
+
+ attributes = [('val', style_id)]
+
+ self._xml_empty_tag('c:style', attributes)
+
+ def _write_chart(self):
+ # Write the <c:chart> element.
+ self._xml_start_tag('c:chart')
+
+ if self.title_none:
+ # Turn off the title.
+ self._write_c_auto_title_deleted()
+ else:
+ # Write the chart title elements.
+ if self.title_formula is not None:
+ self._write_title_formula(self.title_formula,
+ self.title_data_id,
+ None,
+ self.title_font,
+ self.title_layout,
+ self.title_overlay)
+ elif self.title_name is not None:
+ self._write_title_rich(self.title_name,
+ None,
+ self.title_font,
+ self.title_layout,
+ self.title_overlay)
+
+ # Write the c:plotArea element.
+ self._write_plot_area()
+
+ # Write the c:legend element.
+ self._write_legend()
+
+ # Write the c:plotVisOnly element.
+ self._write_plot_vis_only()
+
+ # Write the c:dispBlanksAs element.
+ self._write_disp_blanks_as()
+
+ self._xml_end_tag('c:chart')
+
+ def _write_disp_blanks_as(self):
+ # Write the <c:dispBlanksAs> element.
+ val = self.show_blanks
+
+ # Ignore the default value.
+ if val == 'gap':
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:dispBlanksAs', attributes)
+
+ def _write_plot_area(self):
+ # Write the <c:plotArea> element.
+ self._xml_start_tag('c:plotArea')
+
+ # Write the c:layout element.
+ self._write_layout(self.plotarea.get('layout'), 'plot')
+
+ # Write subclass chart type elements for primary and secondary axes.
+ self._write_chart_type({'primary_axes': True})
+ self._write_chart_type({'primary_axes': False})
+
+ # Configure a combined chart if present.
+ second_chart = self.combined
+ if second_chart:
+ # Secondary axis has unique id otherwise use same as primary.
+ if second_chart.is_secondary:
+ second_chart.id = 1000 + self.id
+ else:
+ second_chart.id = self.id
+
+ # Share the same filehandle for writing.
+ second_chart.fh = self.fh
+
+ # Share series index with primary chart.
+ second_chart.series_index = self.series_index
+
+ # Write the subclass chart type elements for combined chart.
+ second_chart._write_chart_type({'primary_axes': True})
+ second_chart._write_chart_type({'primary_axes': False})
+
+ # Write the category and value elements for the primary axes.
+ args = {'x_axis': self.x_axis,
+ 'y_axis': self.y_axis,
+ 'axis_ids': self.axis_ids}
+
+ if self.date_category:
+ self._write_date_axis(args)
+ else:
+ self._write_cat_axis(args)
+
+ self._write_val_axis(args)
+
+ # Write the category and value elements for the secondary axes.
+ args = {'x_axis': self.x2_axis,
+ 'y_axis': self.y2_axis,
+ 'axis_ids': self.axis2_ids}
+
+ self._write_val_axis(args)
+
+ # Write the secondary axis for the secondary chart.
+ if second_chart and second_chart.is_secondary:
+ args = {'x_axis': second_chart.x2_axis,
+ 'y_axis': second_chart.y2_axis,
+ 'axis_ids': second_chart.axis2_ids}
+
+ second_chart._write_val_axis(args)
+
+ if self.date_category:
+ self._write_date_axis(args)
+ else:
+ self._write_cat_axis(args)
+
+ # Write the c:dTable element.
+ self._write_d_table()
+
+ # Write the c:spPr element for the plotarea formatting.
+ self._write_sp_pr(self.plotarea)
+
+ self._xml_end_tag('c:plotArea')
+
+ def _write_layout(self, layout, layout_type):
+ # Write the <c:layout> element.
+
+ if not layout:
+ # Automatic layout.
+ self._xml_empty_tag('c:layout')
+ else:
+ # User defined manual layout.
+ self._xml_start_tag('c:layout')
+ self._write_manual_layout(layout, layout_type)
+ self._xml_end_tag('c:layout')
+
+ def _write_manual_layout(self, layout, layout_type):
+ # Write the <c:manualLayout> element.
+ self._xml_start_tag('c:manualLayout')
+
+ # Plotarea has a layoutTarget element.
+ if layout_type == 'plot':
+ self._xml_empty_tag('c:layoutTarget', [('val', 'inner')])
+
+ # Set the x, y positions.
+ self._xml_empty_tag('c:xMode', [('val', 'edge')])
+ self._xml_empty_tag('c:yMode', [('val', 'edge')])
+ self._xml_empty_tag('c:x', [('val', layout['x'])])
+ self._xml_empty_tag('c:y', [('val', layout['y'])])
+
+ # For plotarea and legend set the width and height.
+ if layout_type != 'text':
+ self._xml_empty_tag('c:w', [('val', layout['width'])])
+ self._xml_empty_tag('c:h', [('val', layout['height'])])
+
+ self._xml_end_tag('c:manualLayout')
+
+ def _write_chart_type(self, options):
+ # Write the chart type element. This method should be overridden
+ # by the subclasses.
+ return
+
+ def _write_grouping(self, val):
+ # Write the <c:grouping> element.
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:grouping', attributes)
+
+ def _write_series(self, series):
+ # Write the series elements.
+ self._write_ser(series)
+
+ def _write_ser(self, series):
+ # Write the <c:ser> element.
+ index = self.series_index
+ self.series_index += 1
+
+ self._xml_start_tag('c:ser')
+
+ # Write the c:idx element.
+ self._write_idx(index)
+
+ # Write the c:order element.
+ self._write_order(index)
+
+ # Write the series name.
+ self._write_series_name(series)
+
+ # Write the c:spPr element.
+ self._write_sp_pr(series)
+
+ # Write the c:marker element.
+ self._write_marker(series['marker'])
+
+ # Write the c:invertIfNegative element.
+ self._write_c_invert_if_negative(series['invert_if_neg'])
+
+ # Write the c:dPt element.
+ self._write_d_pt(series['points'])
+
+ # Write the c:dLbls element.
+ self._write_d_lbls(series['labels'])
+
+ # Write the c:trendline element.
+ self._write_trendline(series['trendline'])
+
+ # Write the c:errBars element.
+ self._write_error_bars(series['error_bars'])
+
+ # Write the c:cat element.
+ self._write_cat(series)
+
+ # Write the c:val element.
+ self._write_val(series)
+
+ # Write the c:smooth element.
+ if self.smooth_allowed:
+ self._write_c_smooth(series['smooth'])
+
+ self._xml_end_tag('c:ser')
+
+ def _write_idx(self, val):
+ # Write the <c:idx> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:idx', attributes)
+
+ def _write_order(self, val):
+ # Write the <c:order> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:order', attributes)
+
+ def _write_series_name(self, series):
+ # Write the series name.
+
+ if series['name_formula'] is not None:
+ self._write_tx_formula(series['name_formula'], series['name_id'])
+ elif series['name'] is not None:
+ self._write_tx_value(series['name'])
+
+ def _write_c_smooth(self, smooth):
+ # Write the <c:smooth> element.
+
+ if smooth:
+ self._xml_empty_tag('c:smooth', [('val', '1')])
+
+ def _write_cat(self, series):
+ # Write the <c:cat> element.
+ formula = series['categories']
+ data_id = series['cat_data_id']
+ data = None
+
+ if data_id is not None:
+ data = self.formula_data[data_id]
+
+ # Ignore <c:cat> elements for charts without category values.
+ if not formula:
+ return
+
+ self._xml_start_tag('c:cat')
+
+ # Check the type of cached data.
+ cat_type = self._get_data_type(data)
+
+ if cat_type == 'str':
+ self.cat_has_num_fmt = 0
+ # Write the c:numRef element.
+ self._write_str_ref(formula, data, cat_type)
+
+ elif cat_type == 'multi_str':
+ self.cat_has_num_fmt = 0
+ # Write the c:numRef element.
+ self._write_multi_lvl_str_ref(formula, data)
+
+ else:
+ self.cat_has_num_fmt = 1
+ # Write the c:numRef element.
+ self._write_num_ref(formula, data, cat_type)
+
+ self._xml_end_tag('c:cat')
+
+ def _write_val(self, series):
+ # Write the <c:val> element.
+ formula = series['values']
+ data_id = series['val_data_id']
+ data = self.formula_data[data_id]
+
+ self._xml_start_tag('c:val')
+
+ # Unlike Cat axes data should only be numeric.
+ # Write the c:numRef element.
+ self._write_num_ref(formula, data, 'num')
+
+ self._xml_end_tag('c:val')
+
+ def _write_num_ref(self, formula, data, ref_type):
+ # Write the <c:numRef> element.
+ self._xml_start_tag('c:numRef')
+
+ # Write the c:f element.
+ self._write_series_formula(formula)
+
+ if ref_type == 'num':
+ # Write the c:numCache element.
+ self._write_num_cache(data)
+ elif ref_type == 'str':
+ # Write the c:strCache element.
+ self._write_str_cache(data)
+
+ self._xml_end_tag('c:numRef')
+
+ def _write_str_ref(self, formula, data, ref_type):
+ # Write the <c:strRef> element.
+
+ self._xml_start_tag('c:strRef')
+
+ # Write the c:f element.
+ self._write_series_formula(formula)
+
+ if ref_type == 'num':
+ # Write the c:numCache element.
+ self._write_num_cache(data)
+ elif ref_type == 'str':
+ # Write the c:strCache element.
+ self._write_str_cache(data)
+
+ self._xml_end_tag('c:strRef')
+
+ def _write_multi_lvl_str_ref(self, formula, data):
+ # Write the <c:multiLvlStrRef> element.
+
+ if not data:
+ return
+
+ self._xml_start_tag('c:multiLvlStrRef')
+
+ # Write the c:f element.
+ self._write_series_formula(formula)
+
+ self._xml_start_tag('c:multiLvlStrCache')
+
+ # Write the c:ptCount element.
+ count = len(data[-1])
+ self._write_pt_count(count)
+
+ for cat_data in reversed(data):
+
+ self._xml_start_tag('c:lvl')
+
+ for i, point in enumerate(cat_data):
+ # Write the c:pt element.
+ self._write_pt(i, cat_data[i])
+
+ self._xml_end_tag('c:lvl')
+
+ self._xml_end_tag('c:multiLvlStrCache')
+ self._xml_end_tag('c:multiLvlStrRef')
+
+ def _write_series_formula(self, formula):
+ # Write the <c:f> element.
+
+ # Strip the leading '=' from the formula.
+ if formula.startswith('='):
+ formula = formula.lstrip('=')
+
+ self._xml_data_element('c:f', formula)
+
+ def _write_axis_ids(self, args):
+ # Write the <c:axId> elements for the primary or secondary axes.
+
+ # Generate the axis ids.
+ self._add_axis_ids(args)
+
+ if args['primary_axes']:
+ # Write the axis ids for the primary axes.
+ self._write_axis_id(self.axis_ids[0])
+ self._write_axis_id(self.axis_ids[1])
+ else:
+ # Write the axis ids for the secondary axes.
+ self._write_axis_id(self.axis2_ids[0])
+ self._write_axis_id(self.axis2_ids[1])
+
+ def _write_axis_id(self, val):
+ # Write the <c:axId> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:axId', attributes)
+
+ def _write_cat_axis(self, args):
+ # Write the <c:catAx> element. Usually the X axis.
+ x_axis = args['x_axis']
+ y_axis = args['y_axis']
+ axis_ids = args['axis_ids']
+
+ # If there are no axis_ids then we don't need to write this element.
+ if axis_ids is None or not len(axis_ids):
+ return
+
+ position = self.cat_axis_position
+ horiz = self.horiz_cat_axis
+
+ # Overwrite the default axis position with a user supplied value.
+ if x_axis.get('position'):
+ position = x_axis['position']
+
+ self._xml_start_tag('c:catAx')
+
+ self._write_axis_id(axis_ids[0])
+
+ # Write the c:scaling element.
+ self._write_scaling(x_axis.get('reverse'),
+ None,
+ None,
+ None)
+
+ if not x_axis.get('visible'):
+ self._write_delete(1)
+
+ # Write the c:axPos element.
+ self._write_axis_pos(position, y_axis.get('reverse'))
+
+ # Write the c:majorGridlines element.
+ self._write_major_gridlines(x_axis.get('major_gridlines'))
+
+ # Write the c:minorGridlines element.
+ self._write_minor_gridlines(x_axis.get('minor_gridlines'))
+
+ # Write the axis title elements.
+ if x_axis['formula'] is not None:
+ self._write_title_formula(x_axis['formula'],
+ x_axis['data_id'],
+ horiz,
+ x_axis['name_font'],
+ x_axis['name_layout'])
+ elif x_axis['name'] is not None:
+ self._write_title_rich(x_axis['name'],
+ horiz,
+ x_axis['name_font'],
+ x_axis['name_layout'])
+
+ # Write the c:numFmt element.
+ self._write_cat_number_format(x_axis)
+
+ # Write the c:majorTickMark element.
+ self._write_major_tick_mark(x_axis.get('major_tick_mark'))
+
+ # Write the c:minorTickMark element.
+ self._write_minor_tick_mark(x_axis.get('minor_tick_mark'))
+
+ # Write the c:tickLblPos element.
+ self._write_tick_label_pos(x_axis.get('label_position'))
+
+ # Write the c:spPr element for the axis line.
+ self._write_sp_pr(x_axis)
+
+ # Write the axis font elements.
+ self._write_axis_font(x_axis.get('num_font'))
+
+ # Write the c:crossAx element.
+ self._write_cross_axis(axis_ids[1])
+
+ if self.show_crosses or x_axis.get('visible'):
+
+ # Note, the category crossing comes from the value axis.
+ if (y_axis.get('crossing') is None
+ or y_axis.get('crossing') == 'max'):
+
+ # Write the c:crosses element.
+ self._write_crosses(y_axis.get('crossing'))
+ else:
+
+ # Write the c:crossesAt element.
+ self._write_c_crosses_at(y_axis.get('crossing'))
+
+ # Write the c:auto element.
+ if not x_axis.get('text_axis'):
+ self._write_auto(1)
+
+ # Write the c:labelAlign element.
+ self._write_label_align('ctr')
+
+ # Write the c:labelOffset element.
+ self._write_label_offset(100)
+
+ # Write the c:tickLblSkip element.
+ self._write_c_tick_lbl_skip(x_axis.get('interval_unit'))
+
+ # Write the c:tickMarkSkip element.
+ self._write_c_tick_mark_skip(x_axis.get('interval_tick'))
+
+ self._xml_end_tag('c:catAx')
+
+ def _write_val_axis(self, args):
+ # Write the <c:valAx> element. Usually the Y axis.
+ x_axis = args['x_axis']
+ y_axis = args['y_axis']
+ axis_ids = args['axis_ids']
+ position = args.get('position', self.val_axis_position)
+ horiz = self.horiz_val_axis
+
+ # If there are no axis_ids then we don't need to write this element.
+ if axis_ids is None or not len(axis_ids):
+ return
+
+ # Overwrite the default axis position with a user supplied value.
+ position = y_axis.get('position') or position
+
+ self._xml_start_tag('c:valAx')
+
+ self._write_axis_id(axis_ids[1])
+
+ # Write the c:scaling element.
+ self._write_scaling(y_axis.get('reverse'),
+ y_axis.get('min'),
+ y_axis.get('max'),
+ y_axis.get('log_base'))
+
+ if not y_axis.get('visible'):
+ self._write_delete(1)
+
+ # Write the c:axPos element.
+ self._write_axis_pos(position, x_axis.get('reverse'))
+
+ # Write the c:majorGridlines element.
+ self._write_major_gridlines(y_axis.get('major_gridlines'))
+
+ # Write the c:minorGridlines element.
+ self._write_minor_gridlines(y_axis.get('minor_gridlines'))
+
+ # Write the axis title elements.
+ if y_axis['formula'] is not None:
+ self._write_title_formula(y_axis['formula'],
+ y_axis['data_id'],
+ horiz,
+ y_axis['name_font'],
+ y_axis['name_layout'])
+ elif y_axis['name'] is not None:
+ self._write_title_rich(y_axis['name'],
+ horiz,
+ y_axis.get('name_font'),
+ y_axis.get('name_layout'))
+
+ # Write the c:numberFormat element.
+ self._write_number_format(y_axis)
+
+ # Write the c:majorTickMark element.
+ self._write_major_tick_mark(y_axis.get('major_tick_mark'))
+
+ # Write the c:minorTickMark element.
+ self._write_minor_tick_mark(y_axis.get('minor_tick_mark'))
+
+ # Write the c:tickLblPos element.
+ self._write_tick_label_pos(y_axis.get('label_position'))
+
+ # Write the c:spPr element for the axis line.
+ self._write_sp_pr(y_axis)
+
+ # Write the axis font elements.
+ self._write_axis_font(y_axis.get('num_font'))
+
+ # Write the c:crossAx element.
+ self._write_cross_axis(axis_ids[0])
+
+ # Note, the category crossing comes from the value axis.
+ if x_axis.get('crossing') is None or x_axis['crossing'] == 'max':
+
+ # Write the c:crosses element.
+ self._write_crosses(x_axis.get('crossing'))
+ else:
+
+ # Write the c:crossesAt element.
+ self._write_c_crosses_at(x_axis.get('crossing'))
+
+ # Write the c:crossBetween element.
+ self._write_cross_between(x_axis.get('position_axis'))
+
+ # Write the c:majorUnit element.
+ self._write_c_major_unit(y_axis.get('major_unit'))
+
+ # Write the c:minorUnit element.
+ self._write_c_minor_unit(y_axis.get('minor_unit'))
+
+ # Write the c:dispUnits element.
+ self._write_disp_units(y_axis.get('display_units'),
+ y_axis.get('display_units_visible'))
+
+ self._xml_end_tag('c:valAx')
+
+ def _write_cat_val_axis(self, args):
+ # Write the <c:valAx> element. This is for the second valAx
+ # in scatter plots. Usually the X axis.
+ x_axis = args['x_axis']
+ y_axis = args['y_axis']
+ axis_ids = args['axis_ids']
+ position = args['position'] or self.val_axis_position
+ horiz = self.horiz_val_axis
+
+ # If there are no axis_ids then we don't need to write this element.
+ if axis_ids is None or not len(axis_ids):
+ return
+
+ # Overwrite the default axis position with a user supplied value.
+ position = x_axis.get('position') or position
+
+ self._xml_start_tag('c:valAx')
+
+ self._write_axis_id(axis_ids[0])
+
+ # Write the c:scaling element.
+ self._write_scaling(x_axis.get('reverse'),
+ x_axis.get('min'),
+ x_axis.get('max'),
+ x_axis.get('log_base'))
+
+ if not x_axis.get('visible'):
+ self._write_delete(1)
+
+ # Write the c:axPos element.
+ self._write_axis_pos(position, y_axis.get('reverse'))
+
+ # Write the c:majorGridlines element.
+ self._write_major_gridlines(x_axis.get('major_gridlines'))
+
+ # Write the c:minorGridlines element.
+ self._write_minor_gridlines(x_axis.get('minor_gridlines'))
+
+ # Write the axis title elements.
+ if x_axis['formula'] is not None:
+ self._write_title_formula(x_axis['formula'],
+ x_axis['data_id'],
+ horiz,
+ x_axis['name_font'],
+ x_axis['name_layout'])
+ elif x_axis['name'] is not None:
+ self._write_title_rich(x_axis['name'],
+ horiz,
+ x_axis['name_font'],
+ x_axis['name_layout'])
+
+ # Write the c:numberFormat element.
+ self._write_number_format(x_axis)
+
+ # Write the c:majorTickMark element.
+ self._write_major_tick_mark(x_axis.get('major_tick_mark'))
+
+ # Write the c:minorTickMark element.
+ self._write_minor_tick_mark(x_axis.get('minor_tick_mark'))
+
+ # Write the c:tickLblPos element.
+ self._write_tick_label_pos(x_axis.get('label_position'))
+
+ # Write the c:spPr element for the axis line.
+ self._write_sp_pr(x_axis)
+
+ # Write the axis font elements.
+ self._write_axis_font(x_axis.get('num_font'))
+
+ # Write the c:crossAx element.
+ self._write_cross_axis(axis_ids[1])
+
+ # Note, the category crossing comes from the value axis.
+ if y_axis.get('crossing') is None or y_axis['crossing'] == 'max':
+
+ # Write the c:crosses element.
+ self._write_crosses(y_axis.get('crossing'))
+ else:
+
+ # Write the c:crossesAt element.
+ self._write_c_crosses_at(y_axis.get('crossing'))
+
+ # Write the c:crossBetween element.
+ self._write_cross_between(y_axis.get('position_axis'))
+
+ # Write the c:majorUnit element.
+ self._write_c_major_unit(x_axis.get('major_unit'))
+
+ # Write the c:minorUnit element.
+ self._write_c_minor_unit(x_axis.get('minor_unit'))
+
+ # Write the c:dispUnits element.
+ self._write_disp_units(x_axis.get('display_units'),
+ x_axis.get('display_units_visible'))
+
+ self._xml_end_tag('c:valAx')
+
+ def _write_date_axis(self, args):
+ # Write the <c:dateAx> element. Usually the X axis.
+ x_axis = args['x_axis']
+ y_axis = args['y_axis']
+ axis_ids = args['axis_ids']
+
+ # If there are no axis_ids then we don't need to write this element.
+ if axis_ids is None or not len(axis_ids):
+ return
+
+ position = self.cat_axis_position
+
+ # Overwrite the default axis position with a user supplied value.
+ position = x_axis.get('position') or position
+
+ self._xml_start_tag('c:dateAx')
+
+ self._write_axis_id(axis_ids[0])
+
+ # Write the c:scaling element.
+ self._write_scaling(x_axis.get('reverse'),
+ x_axis.get('min'),
+ x_axis.get('max'),
+ x_axis.get('log_base'))
+
+ if not x_axis.get('visible'):
+ self._write_delete(1)
+
+ # Write the c:axPos element.
+ self._write_axis_pos(position, y_axis.get('reverse'))
+
+ # Write the c:majorGridlines element.
+ self._write_major_gridlines(x_axis.get('major_gridlines'))
+
+ # Write the c:minorGridlines element.
+ self._write_minor_gridlines(x_axis.get('minor_gridlines'))
+
+ # Write the axis title elements.
+ if x_axis['formula'] is not None:
+ self._write_title_formula(x_axis['formula'],
+ x_axis['data_id'],
+ None,
+ x_axis['name_font'],
+ x_axis['name_layout'])
+ elif x_axis['name'] is not None:
+ self._write_title_rich(x_axis['name'],
+ None,
+ x_axis['name_font'],
+ x_axis['name_layout'])
+
+ # Write the c:numFmt element.
+ self._write_number_format(x_axis)
+
+ # Write the c:majorTickMark element.
+ self._write_major_tick_mark(x_axis.get('major_tick_mark'))
+
+ # Write the c:minorTickMark element.
+ self._write_minor_tick_mark(x_axis.get('minor_tick_mark'))
+
+ # Write the c:tickLblPos element.
+ self._write_tick_label_pos(x_axis.get('label_position'))
+
+ # Write the c:spPr element for the axis line.
+ self._write_sp_pr(x_axis)
+
+ # Write the axis font elements.
+ self._write_axis_font(x_axis.get('num_font'))
+
+ # Write the c:crossAx element.
+ self._write_cross_axis(axis_ids[1])
+
+ if self.show_crosses or x_axis.get('visible'):
+
+ # Note, the category crossing comes from the value axis.
+ if (y_axis.get('crossing') is None
+ or y_axis.get('crossing') == 'max'):
+
+ # Write the c:crosses element.
+ self._write_crosses(y_axis.get('crossing'))
+ else:
+
+ # Write the c:crossesAt element.
+ self._write_c_crosses_at(y_axis.get('crossing'))
+
+ # Write the c:auto element.
+ self._write_auto(1)
+
+ # Write the c:labelOffset element.
+ self._write_label_offset(100)
+
+ # Write the c:tickLblSkip element.
+ self._write_c_tick_lbl_skip(x_axis.get('interval_unit'))
+
+ # Write the c:tickMarkSkip element.
+ self._write_c_tick_mark_skip(x_axis.get('interval_tick'))
+
+ # Write the c:majorUnit element.
+ self._write_c_major_unit(x_axis.get('major_unit'))
+
+ # Write the c:majorTimeUnit element.
+ if x_axis.get('major_unit'):
+ self._write_c_major_time_unit(x_axis['major_unit_type'])
+
+ # Write the c:minorUnit element.
+ self._write_c_minor_unit(x_axis.get('minor_unit'))
+
+ # Write the c:minorTimeUnit element.
+ if x_axis.get('minor_unit'):
+ self._write_c_minor_time_unit(x_axis['minor_unit_type'])
+
+ self._xml_end_tag('c:dateAx')
+
+ def _write_scaling(self, reverse, min_val, max_val, log_base):
+ # Write the <c:scaling> element.
+
+ self._xml_start_tag('c:scaling')
+
+ # Write the c:logBase element.
+ self._write_c_log_base(log_base)
+
+ # Write the c:orientation element.
+ self._write_orientation(reverse)
+
+ # Write the c:max element.
+ self._write_c_max(max_val)
+
+ # Write the c:min element.
+ self._write_c_min(min_val)
+
+ self._xml_end_tag('c:scaling')
+
+ def _write_c_log_base(self, val):
+ # Write the <c:logBase> element.
+
+ if not val:
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:logBase', attributes)
+
+ def _write_orientation(self, reverse):
+ # Write the <c:orientation> element.
+ val = 'minMax'
+
+ if reverse:
+ val = 'maxMin'
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:orientation', attributes)
+
+ def _write_c_max(self, max_val):
+ # Write the <c:max_val> element.
+
+ if max_val is None:
+ return
+
+ attributes = [('val', max_val)]
+
+ self._xml_empty_tag('c:max', attributes)
+
+ def _write_c_min(self, min_val):
+ # Write the <c:min_val> element.
+
+ if min_val is None:
+ return
+
+ attributes = [('val', min_val)]
+
+ self._xml_empty_tag('c:min', attributes)
+
+ def _write_axis_pos(self, val, reverse):
+ # Write the <c:axPos> element.
+
+ if reverse:
+ if val == 'l':
+ val = 'r'
+ if val == 'b':
+ val = 't'
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:axPos', attributes)
+
+ def _write_number_format(self, axis):
+ # Write the <c:numberFormat> element. Note: It is assumed that if
+ # a user defined number format is supplied (i.e., non-default) then
+ # the sourceLinked attribute is 0.
+ # The user can override this if required.
+ format_code = axis.get('num_format')
+ source_linked = 1
+
+ # Check if a user defined number format has been set.
+ if (format_code is not None
+ and format_code != axis['defaults']['num_format']):
+ source_linked = 0
+
+ # User override of sourceLinked.
+ if axis.get('num_format_linked'):
+ source_linked = 1
+
+ attributes = [
+ ('formatCode', format_code),
+ ('sourceLinked', source_linked),
+ ]
+
+ self._xml_empty_tag('c:numFmt', attributes)
+
+ def _write_cat_number_format(self, axis):
+ # Write the <c:numFmt> element. Special case handler for category
+ # axes which don't always have a number format.
+ format_code = axis.get('num_format')
+ source_linked = 1
+ default_format = 1
+
+ # Check if a user defined number format has been set.
+ if (format_code is not None
+ and format_code != axis['defaults']['num_format']):
+ source_linked = 0
+ default_format = 0
+
+ # User override of sourceLinked.
+ if axis.get('num_format_linked'):
+ source_linked = 1
+
+ # Skip if cat doesn't have a num format (unless it is non-default).
+ if not self.cat_has_num_fmt and default_format:
+ return
+
+ attributes = [
+ ('formatCode', format_code),
+ ('sourceLinked', source_linked),
+ ]
+
+ self._xml_empty_tag('c:numFmt', attributes)
+
+ def _write_data_label_number_format(self, format_code):
+ # Write the <c:numberFormat> element for data labels.
+ source_linked = 0
+
+ attributes = [
+ ('formatCode', format_code),
+ ('sourceLinked', source_linked),
+ ]
+
+ self._xml_empty_tag('c:numFmt', attributes)
+
+ def _write_major_tick_mark(self, val):
+ # Write the <c:majorTickMark> element.
+
+ if not val:
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:majorTickMark', attributes)
+
+ def _write_minor_tick_mark(self, val):
+ # Write the <c:minorTickMark> element.
+
+ if not val:
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:minorTickMark', attributes)
+
+ def _write_tick_label_pos(self, val=None):
+ # Write the <c:tickLblPos> element.
+ if val is None or val == 'next_to':
+ val = 'nextTo'
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:tickLblPos', attributes)
+
+ def _write_cross_axis(self, val):
+ # Write the <c:crossAx> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:crossAx', attributes)
+
+ def _write_crosses(self, val=None):
+ # Write the <c:crosses> element.
+ if val is None:
+ val = 'autoZero'
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:crosses', attributes)
+
+ def _write_c_crosses_at(self, val):
+ # Write the <c:crossesAt> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:crossesAt', attributes)
+
+ def _write_auto(self, val):
+ # Write the <c:auto> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:auto', attributes)
+
+ def _write_label_align(self, val):
+ # Write the <c:labelAlign> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:lblAlgn', attributes)
+
+ def _write_label_offset(self, val):
+ # Write the <c:labelOffset> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:lblOffset', attributes)
+
+ def _write_c_tick_lbl_skip(self, val):
+ # Write the <c:tickLblSkip> element.
+ if val is None:
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:tickLblSkip', attributes)
+
+ def _write_c_tick_mark_skip(self, val):
+ # Write the <c:tickMarkSkip> element.
+ if val is None:
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:tickMarkSkip', attributes)
+
+ def _write_major_gridlines(self, gridlines):
+ # Write the <c:majorGridlines> element.
+
+ if not gridlines:
+ return
+
+ if not gridlines['visible']:
+ return
+
+ if gridlines['line']['defined']:
+ self._xml_start_tag('c:majorGridlines')
+
+ # Write the c:spPr element.
+ self._write_sp_pr(gridlines)
+
+ self._xml_end_tag('c:majorGridlines')
+ else:
+ self._xml_empty_tag('c:majorGridlines')
+
+ def _write_minor_gridlines(self, gridlines):
+ # Write the <c:minorGridlines> element.
+
+ if not gridlines:
+ return
+
+ if not gridlines['visible']:
+ return
+
+ if gridlines['line']['defined']:
+ self._xml_start_tag('c:minorGridlines')
+
+ # Write the c:spPr element.
+ self._write_sp_pr(gridlines)
+
+ self._xml_end_tag('c:minorGridlines')
+ else:
+ self._xml_empty_tag('c:minorGridlines')
+
+ def _write_cross_between(self, val):
+ # Write the <c:crossBetween> element.
+ if val is None:
+ val = self.cross_between
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:crossBetween', attributes)
+
+ def _write_c_major_unit(self, val):
+ # Write the <c:majorUnit> element.
+
+ if not val:
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:majorUnit', attributes)
+
+ def _write_c_minor_unit(self, val):
+ # Write the <c:minorUnit> element.
+
+ if not val:
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:minorUnit', attributes)
+
+ def _write_c_major_time_unit(self, val=None):
+ # Write the <c:majorTimeUnit> element.
+ if val is None:
+ val = 'days'
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:majorTimeUnit', attributes)
+
+ def _write_c_minor_time_unit(self, val=None):
+ # Write the <c:minorTimeUnit> element.
+ if val is None:
+ val = 'days'
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:minorTimeUnit', attributes)
+
+ def _write_legend(self):
+ # Write the <c:legend> element.
+ position = self.legend_position
+ font = self.legend_font
+ delete_series = []
+ overlay = 0
+
+ if (self.legend_delete_series is not None
+ and type(self.legend_delete_series) is list):
+ delete_series = self.legend_delete_series
+
+ if position.startswith('overlay_'):
+ position = position.replace('overlay_', '')
+ overlay = 1
+
+ allowed = {
+ 'right': 'r',
+ 'left': 'l',
+ 'top': 't',
+ 'bottom': 'b',
+ }
+
+ if position == 'none':
+ return
+
+ if position not in allowed:
+ return
+
+ position = allowed[position]
+
+ self._xml_start_tag('c:legend')
+
+ # Write the c:legendPos element.
+ self._write_legend_pos(position)
+
+ # Remove series labels from the legend.
+ for index in delete_series:
+ # Write the c:legendEntry element.
+ self._write_legend_entry(index)
+
+ # Write the c:layout element.
+ self._write_layout(self.legend_layout, 'legend')
+
+ if font:
+ self._write_tx_pr(None, font)
+
+ # Write the c:overlay element.
+ if overlay:
+ self._write_overlay()
+
+ self._xml_end_tag('c:legend')
+
+ def _write_legend_pos(self, val):
+ # Write the <c:legendPos> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:legendPos', attributes)
+
+ def _write_legend_entry(self, index):
+ # Write the <c:legendEntry> element.
+
+ self._xml_start_tag('c:legendEntry')
+
+ # Write the c:idx element.
+ self._write_idx(index)
+
+ # Write the c:delete element.
+ self._write_delete(1)
+
+ self._xml_end_tag('c:legendEntry')
+
+ def _write_overlay(self):
+ # Write the <c:overlay> element.
+ val = 1
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:overlay', attributes)
+
+ def _write_plot_vis_only(self):
+ # Write the <c:plotVisOnly> element.
+ val = 1
+
+ # Ignore this element if we are plotting hidden data.
+ if self.show_hidden:
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:plotVisOnly', attributes)
+
+ def _write_print_settings(self):
+ # Write the <c:printSettings> element.
+ self._xml_start_tag('c:printSettings')
+
+ # Write the c:headerFooter element.
+ self._write_header_footer()
+
+ # Write the c:pageMargins element.
+ self._write_page_margins()
+
+ # Write the c:pageSetup element.
+ self._write_page_setup()
+
+ self._xml_end_tag('c:printSettings')
+
+ def _write_header_footer(self):
+ # Write the <c:headerFooter> element.
+ self._xml_empty_tag('c:headerFooter')
+
+ def _write_page_margins(self):
+ # Write the <c:pageMargins> element.
+ b = 0.75
+ l = 0.7
+ r = 0.7
+ t = 0.75
+ header = 0.3
+ footer = 0.3
+
+ attributes = [
+ ('b', b),
+ ('l', l),
+ ('r', r),
+ ('t', t),
+ ('header', header),
+ ('footer', footer),
+ ]
+
+ self._xml_empty_tag('c:pageMargins', attributes)
+
+ def _write_page_setup(self):
+ # Write the <c:pageSetup> element.
+ self._xml_empty_tag('c:pageSetup')
+
+ def _write_c_auto_title_deleted(self):
+ # Write the <c:autoTitleDeleted> element.
+ self._xml_empty_tag('c:autoTitleDeleted', [('val', 1)])
+
+ def _write_title_rich(self, title, horiz, font, layout, overlay=False):
+ # Write the <c:title> element for a rich string.
+
+ self._xml_start_tag('c:title')
+
+ # Write the c:tx element.
+ self._write_tx_rich(title, horiz, font)
+
+ # Write the c:layout element.
+ self._write_layout(layout, 'text')
+
+ # Write the c:overlay element.
+ if overlay:
+ self._write_overlay()
+
+ self._xml_end_tag('c:title')
+
+ def _write_title_formula(self, title, data_id, horiz, font, layout,
+ overlay=False):
+ # Write the <c:title> element for a rich string.
+
+ self._xml_start_tag('c:title')
+
+ # Write the c:tx element.
+ self._write_tx_formula(title, data_id)
+
+ # Write the c:layout element.
+ self._write_layout(layout, 'text')
+
+ # Write the c:overlay element.
+ if overlay:
+ self._write_overlay()
+
+ # Write the c:txPr element.
+ self._write_tx_pr(horiz, font)
+
+ self._xml_end_tag('c:title')
+
+ def _write_tx_rich(self, title, horiz, font):
+ # Write the <c:tx> element.
+
+ self._xml_start_tag('c:tx')
+
+ # Write the c:rich element.
+ self._write_rich(title, horiz, font)
+
+ self._xml_end_tag('c:tx')
+
+ def _write_tx_value(self, title):
+ # Write the <c:tx> element with a value such as for series names.
+
+ self._xml_start_tag('c:tx')
+
+ # Write the c:v element.
+ self._write_v(title)
+
+ self._xml_end_tag('c:tx')
+
+ def _write_tx_formula(self, title, data_id):
+ # Write the <c:tx> element.
+ data = None
+
+ if data_id is not None:
+ data = self.formula_data[data_id]
+
+ self._xml_start_tag('c:tx')
+
+ # Write the c:strRef element.
+ self._write_str_ref(title, data, 'str')
+
+ self._xml_end_tag('c:tx')
+
+ def _write_rich(self, title, horiz, font):
+ # Write the <c:rich> element.
+
+ if font and font.get('rotation'):
+ rotation = font['rotation']
+ else:
+ rotation = None
+
+ self._xml_start_tag('c:rich')
+
+ # Write the a:bodyPr element.
+ self._write_a_body_pr(rotation, horiz)
+
+ # Write the a:lstStyle element.
+ self._write_a_lst_style()
+
+ # Write the a:p element.
+ self._write_a_p_rich(title, font)
+
+ self._xml_end_tag('c:rich')
+
+ def _write_a_body_pr(self, rotation, horiz):
+ # Write the <a:bodyPr> element.
+ attributes = []
+
+ if rotation is None and horiz:
+ rotation = -5400000
+
+ if rotation is not None:
+ attributes.append(('rot', rotation))
+
+ if horiz:
+ attributes.append(('vert', 'horz'))
+
+ self._xml_empty_tag('a:bodyPr', attributes)
+
+ def _write_a_lst_style(self):
+ # Write the <a:lstStyle> element.
+ self._xml_empty_tag('a:lstStyle')
+
+ def _write_a_p_rich(self, title, font):
+ # Write the <a:p> element for rich string titles.
+
+ self._xml_start_tag('a:p')
+
+ # Write the a:pPr element.
+ self._write_a_p_pr_rich(font)
+
+ # Write the a:r element.
+ self._write_a_r(title, font)
+
+ self._xml_end_tag('a:p')
+
+ def _write_a_p_formula(self, font):
+ # Write the <a:p> element for formula titles.
+
+ self._xml_start_tag('a:p')
+
+ # Write the a:pPr element.
+ self._write_a_p_pr_formula(font)
+
+ # Write the a:endParaRPr element.
+ self._write_a_end_para_rpr()
+
+ self._xml_end_tag('a:p')
+
+ def _write_a_p_pr_rich(self, font):
+ # Write the <a:pPr> element for rich string titles.
+
+ self._xml_start_tag('a:pPr')
+
+ # Write the a:defRPr element.
+ self._write_a_def_rpr(font)
+
+ self._xml_end_tag('a:pPr')
+
+ def _write_a_p_pr_formula(self, font):
+ # Write the <a:pPr> element for formula titles.
+
+ self._xml_start_tag('a:pPr')
+
+ # Write the a:defRPr element.
+ self._write_a_def_rpr(font)
+
+ self._xml_end_tag('a:pPr')
+
+ def _write_a_def_rpr(self, font):
+ # Write the <a:defRPr> element.
+ has_color = 0
+
+ style_attributes = Shape._get_font_style_attributes(font)
+ latin_attributes = Shape._get_font_latin_attributes(font)
+
+ if font and font.get('color') is not None:
+ has_color = 1
+
+ if latin_attributes or has_color:
+ self._xml_start_tag('a:defRPr', style_attributes)
+
+ if has_color:
+ self._write_a_solid_fill({'color': font['color']})
+
+ if latin_attributes:
+ self._write_a_latin(latin_attributes)
+
+ self._xml_end_tag('a:defRPr')
+ else:
+ self._xml_empty_tag('a:defRPr', style_attributes)
+
+ def _write_a_end_para_rpr(self):
+ # Write the <a:endParaRPr> element.
+ lang = 'en-US'
+
+ attributes = [('lang', lang)]
+
+ self._xml_empty_tag('a:endParaRPr', attributes)
+
+ def _write_a_r(self, title, font):
+ # Write the <a:r> element.
+
+ self._xml_start_tag('a:r')
+
+ # Write the a:rPr element.
+ self._write_a_r_pr(font)
+
+ # Write the a:t element.
+ self._write_a_t(title)
+
+ self._xml_end_tag('a:r')
+
+ def _write_a_r_pr(self, font):
+ # Write the <a:rPr> element.
+ has_color = 0
+ lang = 'en-US'
+
+ style_attributes = Shape._get_font_style_attributes(font)
+ latin_attributes = Shape._get_font_latin_attributes(font)
+
+ if font and font['color'] is not None:
+ has_color = 1
+
+ # Add the lang type to the attributes.
+ style_attributes.insert(0, ('lang', lang))
+
+ if latin_attributes or has_color:
+ self._xml_start_tag('a:rPr', style_attributes)
+
+ if has_color:
+ self._write_a_solid_fill({'color': font['color']})
+
+ if latin_attributes:
+ self._write_a_latin(latin_attributes)
+
+ self._xml_end_tag('a:rPr')
+ else:
+ self._xml_empty_tag('a:rPr', style_attributes)
+
+ def _write_a_t(self, title):
+ # Write the <a:t> element.
+
+ self._xml_data_element('a:t', title)
+
+ def _write_tx_pr(self, horiz, font):
+ # Write the <c:txPr> element.
+
+ if font and font.get('rotation'):
+ rotation = font['rotation']
+ else:
+ rotation = None
+
+ self._xml_start_tag('c:txPr')
+
+ # Write the a:bodyPr element.
+ self._write_a_body_pr(rotation, horiz)
+
+ # Write the a:lstStyle element.
+ self._write_a_lst_style()
+
+ # Write the a:p element.
+ self._write_a_p_formula(font)
+
+ self._xml_end_tag('c:txPr')
+
+ def _write_marker(self, marker):
+ # Write the <c:marker> element.
+ if marker is None:
+ marker = self.default_marker
+
+ if not marker:
+ return
+ if 'automatic' in marker:
+ return
+
+ self._xml_start_tag('c:marker')
+
+ # Write the c:symbol element.
+ self._write_symbol(marker['type'])
+
+ # Write the c:size element.
+ if marker.get('size'):
+ self._write_marker_size(marker['size'])
+
+ # Write the c:spPr element.
+ self._write_sp_pr(marker)
+
+ self._xml_end_tag('c:marker')
+
+ def _write_marker_value(self):
+ # Write the <c:marker> element without a sub-element.
+ style = self.default_marker
+
+ if not style:
+ return
+
+ attributes = [('val', 1)]
+
+ self._xml_empty_tag('c:marker', attributes)
+
+ def _write_marker_size(self, val):
+ # Write the <c:size> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:size', attributes)
+
+ def _write_symbol(self, val):
+ # Write the <c:symbol> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:symbol', attributes)
+
+ def _write_sp_pr(self, series):
+ # Write the <c:spPr> element.
+
+ has_fill = False
+ has_line = False
+ has_pattern = series.get('pattern')
+ has_gradient = series.get('gradient')
+
+ if series.get('fill') and series['fill']['defined']:
+ has_fill = True
+
+ if series.get('line') and series['line']['defined']:
+ has_line = True
+
+ if (not has_fill and not has_line and not has_pattern
+ and not has_gradient):
+ return
+
+ self._xml_start_tag('c:spPr')
+
+ # Write the fill elements for solid charts such as pie and bar.
+ if series.get('fill') and series['fill']['defined']:
+ if 'none' in series['fill']:
+ # Write the a:noFill element.
+ self._write_a_no_fill()
+ else:
+ # Write the a:solidFill element.
+ self._write_a_solid_fill(series['fill'])
+
+ if series.get('pattern'):
+ # Write the a:gradFill element.
+ self._write_a_patt_fill(series['pattern'])
+
+ if series.get('gradient'):
+ # Write the a:gradFill element.
+ self._write_a_grad_fill(series['gradient'])
+
+ # Write the a:ln element.
+ if series.get('line') and series['line']['defined']:
+ self._write_a_ln(series['line'])
+
+ self._xml_end_tag('c:spPr')
+
+ def _write_a_ln(self, line):
+ # Write the <a:ln> element.
+ attributes = []
+
+ # Add the line width as an attribute.
+ width = line.get('width')
+
+ if width:
+ # Round width to nearest 0.25, like Excel.
+ width = int((width + 0.125) * 4) / 4.0
+
+ # Convert to internal units.
+ width = int(0.5 + (12700 * width))
+
+ attributes = [('w', width)]
+
+ self._xml_start_tag('a:ln', attributes)
+
+ # Write the line fill.
+ if 'none' in line:
+ # Write the a:noFill element.
+ self._write_a_no_fill()
+ elif 'color' in line:
+ # Write the a:solidFill element.
+ self._write_a_solid_fill(line)
+
+ # Write the line/dash type.
+ line_type = line.get('dash_type')
+ if line_type:
+ # Write the a:prstDash element.
+ self._write_a_prst_dash(line_type)
+
+ self._xml_end_tag('a:ln')
+
+ def _write_a_no_fill(self):
+ # Write the <a:noFill> element.
+ self._xml_empty_tag('a:noFill')
+
+ def _write_a_solid_fill(self, fill):
+ # Write the <a:solidFill> element.
+
+ self._xml_start_tag('a:solidFill')
+
+ if 'color' in fill:
+ color = get_rgb_color(fill['color'])
+ transparency = fill.get('transparency')
+ # Write the a:srgbClr element.
+ self._write_a_srgb_clr(color, transparency)
+
+ self._xml_end_tag('a:solidFill')
+
+ def _write_a_srgb_clr(self, val, transparency=None):
+ # Write the <a:srgbClr> element.
+ attributes = [('val', val)]
+
+ if transparency:
+ self._xml_start_tag('a:srgbClr', attributes)
+
+ # Write the a:alpha element.
+ self._write_a_alpha(transparency)
+
+ self._xml_end_tag('a:srgbClr')
+ else:
+ self._xml_empty_tag('a:srgbClr', attributes)
+
+ def _write_a_alpha(self, val):
+ # Write the <a:alpha> element.
+
+ val = int((100 - int(val)) * 1000)
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('a:alpha', attributes)
+
+ def _write_a_prst_dash(self, val):
+ # Write the <a:prstDash> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('a:prstDash', attributes)
+
+ def _write_trendline(self, trendline):
+ # Write the <c:trendline> element.
+
+ if not trendline:
+ return
+
+ self._xml_start_tag('c:trendline')
+
+ # Write the c:name element.
+ self._write_name(trendline.get('name'))
+
+ # Write the c:spPr element.
+ self._write_sp_pr(trendline)
+
+ # Write the c:trendlineType element.
+ self._write_trendline_type(trendline['type'])
+
+ # Write the c:order element for polynomial trendlines.
+ if trendline['type'] == 'poly':
+ self._write_trendline_order(trendline.get('order'))
+
+ # Write the c:period element for moving average trendlines.
+ if trendline['type'] == 'movingAvg':
+ self._write_period(trendline.get('period'))
+
+ # Write the c:forward element.
+ self._write_forward(trendline.get('forward'))
+
+ # Write the c:backward element.
+ self._write_backward(trendline.get('backward'))
+
+ if 'intercept' in trendline:
+ # Write the c:intercept element.
+ self._write_c_intercept(trendline['intercept'])
+
+ if trendline.get('display_r_squared'):
+ # Write the c:dispRSqr element.
+ self._write_c_disp_rsqr()
+
+ if trendline.get('display_equation'):
+ # Write the c:dispEq element.
+ self._write_c_disp_eq()
+
+ # Write the c:trendlineLbl element.
+ self._write_c_trendline_lbl()
+
+ self._xml_end_tag('c:trendline')
+
+ def _write_trendline_type(self, val):
+ # Write the <c:trendlineType> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:trendlineType', attributes)
+
+ def _write_name(self, data):
+ # Write the <c:name> element.
+
+ if data is None:
+ return
+
+ self._xml_data_element('c:name', data)
+
+ def _write_trendline_order(self, val):
+ # Write the <c:order> element.
+ # val = _[0] is not None ? _[0]: 2
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:order', attributes)
+
+ def _write_period(self, val):
+ # Write the <c:period> element.
+ # val = _[0] is not None ? _[0]: 2
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:period', attributes)
+
+ def _write_forward(self, val):
+ # Write the <c:forward> element.
+
+ if not val:
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:forward', attributes)
+
+ def _write_backward(self, val):
+ # Write the <c:backward> element.
+
+ if not val:
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:backward', attributes)
+
+ def _write_c_intercept(self, val):
+ # Write the <c:intercept> element.
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:intercept', attributes)
+
+ def _write_c_disp_eq(self):
+ # Write the <c:dispEq> element.
+ attributes = [('val', 1)]
+
+ self._xml_empty_tag('c:dispEq', attributes)
+
+ def _write_c_disp_rsqr(self):
+ # Write the <c:dispRSqr> element.
+ attributes = [('val', 1)]
+
+ self._xml_empty_tag('c:dispRSqr', attributes)
+
+ def _write_c_trendline_lbl(self):
+ # Write the <c:trendlineLbl> element.
+ self._xml_start_tag('c:trendlineLbl')
+
+ # Write the c:layout element.
+ self._write_layout(None, None)
+
+ # Write the c:numFmt element.
+ self._write_trendline_num_fmt()
+
+ self._xml_end_tag('c:trendlineLbl')
+
+ def _write_trendline_num_fmt(self):
+ # Write the <c:numFmt> element.
+ attributes = [
+ ('formatCode', 'General'),
+ ('sourceLinked', 0),
+ ]
+
+ self._xml_empty_tag('c:numFmt', attributes)
+
+ def _write_hi_low_lines(self):
+ # Write the <c:hiLowLines> element.
+ hi_low_lines = self.hi_low_lines
+
+ if hi_low_lines is None:
+ return
+
+ if 'line' in hi_low_lines and hi_low_lines['line']['defined']:
+
+ self._xml_start_tag('c:hiLowLines')
+
+ # Write the c:spPr element.
+ self._write_sp_pr(hi_low_lines)
+
+ self._xml_end_tag('c:hiLowLines')
+ else:
+ self._xml_empty_tag('c:hiLowLines')
+
+ def _write_drop_lines(self):
+ # Write the <c:dropLines> element.
+ drop_lines = self.drop_lines
+
+ if drop_lines is None:
+ return
+
+ if drop_lines['line']['defined']:
+
+ self._xml_start_tag('c:dropLines')
+
+ # Write the c:spPr element.
+ self._write_sp_pr(drop_lines)
+
+ self._xml_end_tag('c:dropLines')
+ else:
+ self._xml_empty_tag('c:dropLines')
+
+ def _write_overlap(self, val):
+ # Write the <c:overlap> element.
+
+ if val is None:
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:overlap', attributes)
+
+ def _write_num_cache(self, data):
+ # Write the <c:numCache> element.
+ if data:
+ count = len(data)
+ else:
+ count = 0
+
+ self._xml_start_tag('c:numCache')
+
+ # Write the c:formatCode element.
+ self._write_format_code('General')
+
+ # Write the c:ptCount element.
+ self._write_pt_count(count)
+
+ for i in range(count):
+ token = data[i]
+
+ if token is None:
+ continue
+
+ try:
+ float(token)
+ except ValueError:
+ # Write non-numeric data as 0.
+ token = 0
+
+ # Write the c:pt element.
+ self._write_pt(i, token)
+
+ self._xml_end_tag('c:numCache')
+
+ def _write_str_cache(self, data):
+ # Write the <c:strCache> element.
+ count = len(data)
+
+ self._xml_start_tag('c:strCache')
+
+ # Write the c:ptCount element.
+ self._write_pt_count(count)
+
+ for i in range(count):
+ # Write the c:pt element.
+ self._write_pt(i, data[i])
+
+ self._xml_end_tag('c:strCache')
+
+ def _write_format_code(self, data):
+ # Write the <c:formatCode> element.
+
+ self._xml_data_element('c:formatCode', data)
+
+ def _write_pt_count(self, val):
+ # Write the <c:ptCount> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:ptCount', attributes)
+
+ def _write_pt(self, idx, value):
+ # Write the <c:pt> element.
+
+ if value is None:
+ return
+
+ attributes = [('idx', idx)]
+
+ self._xml_start_tag('c:pt', attributes)
+
+ # Write the c:v element.
+ self._write_v(value)
+
+ self._xml_end_tag('c:pt')
+
+ def _write_v(self, data):
+ # Write the <c:v> element.
+
+ self._xml_data_element('c:v', data)
+
+ def _write_protection(self):
+ # Write the <c:protection> element.
+ if not self.protection:
+ return
+
+ self._xml_empty_tag('c:protection')
+
+ def _write_d_pt(self, points):
+ # Write the <c:dPt> elements.
+ index = -1
+
+ if not points:
+ return
+
+ for point in points:
+ index += 1
+ if not point:
+ continue
+
+ self._write_d_pt_point(index, point)
+
+ def _write_d_pt_point(self, index, point):
+ # Write an individual <c:dPt> element.
+
+ self._xml_start_tag('c:dPt')
+
+ # Write the c:idx element.
+ self._write_idx(index)
+
+ # Write the c:spPr element.
+ self._write_sp_pr(point)
+
+ self._xml_end_tag('c:dPt')
+
+ def _write_d_lbls(self, labels):
+ # Write the <c:dLbls> element.
+
+ if not labels:
+ return
+
+ self._xml_start_tag('c:dLbls')
+
+ # Write the c:numFmt element.
+ if labels.get('num_format'):
+ self._write_data_label_number_format(labels['num_format'])
+
+ # Write the data label font elements.
+ if labels.get('font'):
+ self._write_axis_font(labels['font'])
+
+ # Write the c:dLblPos element.
+ if labels.get('position'):
+ self._write_d_lbl_pos(labels['position'])
+
+ # Write the c:showLegendKey element.
+ if labels.get('legend_key'):
+ self._write_show_legend_key()
+
+ # Write the c:showVal element.
+ if labels.get('value'):
+ self._write_show_val()
+
+ # Write the c:showCatName element.
+ if labels.get('category'):
+ self._write_show_cat_name()
+
+ # Write the c:showSerName element.
+ if labels.get('series_name'):
+ self._write_show_ser_name()
+
+ # Write the c:showPercent element.
+ if labels.get('percentage'):
+ self._write_show_percent()
+
+ # Write the c:separator element.
+ if labels.get('separator'):
+ self._write_separator(labels['separator'])
+
+ # Write the c:showLeaderLines element.
+ if labels.get('leader_lines'):
+ self._write_show_leader_lines()
+
+ self._xml_end_tag('c:dLbls')
+
+ def _write_show_legend_key(self):
+ # Write the <c:showLegendKey> element.
+ val = '1'
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:showLegendKey', attributes)
+
+ def _write_show_val(self):
+ # Write the <c:showVal> element.
+ val = 1
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:showVal', attributes)
+
+ def _write_show_cat_name(self):
+ # Write the <c:showCatName> element.
+ val = 1
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:showCatName', attributes)
+
+ def _write_show_ser_name(self):
+ # Write the <c:showSerName> element.
+ val = 1
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:showSerName', attributes)
+
+ def _write_show_percent(self):
+ # Write the <c:showPercent> element.
+ val = 1
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:showPercent', attributes)
+
+ def _write_separator(self, data):
+ # Write the <c:separator> element.
+ self._xml_data_element('c:separator', data)
+
+ def _write_show_leader_lines(self):
+ # Write the <c:showLeaderLines> element.
+ val = 1
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:showLeaderLines', attributes)
+
+ def _write_d_lbl_pos(self, val):
+ # Write the <c:dLblPos> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:dLblPos', attributes)
+
+ def _write_delete(self, val):
+ # Write the <c:delete> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:delete', attributes)
+
+ def _write_c_invert_if_negative(self, invert):
+ # Write the <c:invertIfNegative> element.
+ val = 1
+
+ if not invert:
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:invertIfNegative', attributes)
+
+ def _write_axis_font(self, font):
+ # Write the axis font elements.
+
+ if not font:
+ return
+
+ self._xml_start_tag('c:txPr')
+ self._write_a_body_pr(font.get('rotation'), None)
+ self._write_a_lst_style()
+ self._xml_start_tag('a:p')
+
+ self._write_a_p_pr_rich(font)
+
+ self._write_a_end_para_rpr()
+ self._xml_end_tag('a:p')
+ self._xml_end_tag('c:txPr')
+
+ def _write_a_latin(self, attributes):
+ # Write the <a:latin> element.
+ self._xml_empty_tag('a:latin', attributes)
+
+ def _write_d_table(self):
+ # Write the <c:dTable> element.
+ table = self.table
+
+ if not table:
+ return
+
+ self._xml_start_tag('c:dTable')
+
+ if table['horizontal']:
+ # Write the c:showHorzBorder element.
+ self._write_show_horz_border()
+
+ if table['vertical']:
+ # Write the c:showVertBorder element.
+ self._write_show_vert_border()
+
+ if table['outline']:
+ # Write the c:showOutline element.
+ self._write_show_outline()
+
+ if table['show_keys']:
+ # Write the c:showKeys element.
+ self._write_show_keys()
+
+ if table['font']:
+ # Write the table font.
+ self._write_tx_pr(None, table['font'])
+
+ self._xml_end_tag('c:dTable')
+
+ def _write_show_horz_border(self):
+ # Write the <c:showHorzBorder> element.
+ attributes = [('val', 1)]
+
+ self._xml_empty_tag('c:showHorzBorder', attributes)
+
+ def _write_show_vert_border(self):
+ # Write the <c:showVertBorder> element.
+ attributes = [('val', 1)]
+
+ self._xml_empty_tag('c:showVertBorder', attributes)
+
+ def _write_show_outline(self):
+ # Write the <c:showOutline> element.
+ attributes = [('val', 1)]
+
+ self._xml_empty_tag('c:showOutline', attributes)
+
+ def _write_show_keys(self):
+ # Write the <c:showKeys> element.
+ attributes = [('val', 1)]
+
+ self._xml_empty_tag('c:showKeys', attributes)
+
+ def _write_error_bars(self, error_bars):
+ # Write the X and Y error bars.
+
+ if not error_bars:
+ return
+
+ if error_bars['x_error_bars']:
+ self._write_err_bars('x', error_bars['x_error_bars'])
+
+ if error_bars['y_error_bars']:
+ self._write_err_bars('y', error_bars['y_error_bars'])
+
+ def _write_err_bars(self, direction, error_bars):
+ # Write the <c:errBars> element.
+
+ if not error_bars:
+ return
+
+ self._xml_start_tag('c:errBars')
+
+ # Write the c:errDir element.
+ self._write_err_dir(direction)
+
+ # Write the c:errBarType element.
+ self._write_err_bar_type(error_bars['direction'])
+
+ # Write the c:errValType element.
+ self._write_err_val_type(error_bars['type'])
+
+ if not error_bars['endcap']:
+ # Write the c:noEndCap element.
+ self._write_no_end_cap()
+
+ if error_bars['type'] == 'stdErr':
+ # Don't need to write a c:errValType tag.
+ pass
+ elif error_bars['type'] == 'cust':
+ # Write the custom error tags.
+ self._write_custom_error(error_bars)
+ else:
+ # Write the c:val element.
+ self._write_error_val(error_bars['value'])
+
+ # Write the c:spPr element.
+ self._write_sp_pr(error_bars)
+
+ self._xml_end_tag('c:errBars')
+
+ def _write_err_dir(self, val):
+ # Write the <c:errDir> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:errDir', attributes)
+
+ def _write_err_bar_type(self, val):
+ # Write the <c:errBarType> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:errBarType', attributes)
+
+ def _write_err_val_type(self, val):
+ # Write the <c:errValType> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:errValType', attributes)
+
+ def _write_no_end_cap(self):
+ # Write the <c:noEndCap> element.
+ attributes = [('val', 1)]
+
+ self._xml_empty_tag('c:noEndCap', attributes)
+
+ def _write_error_val(self, val):
+ # Write the <c:val> element for error bars.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:val', attributes)
+
+ def _write_custom_error(self, error_bars):
+ # Write the custom error bars tags.
+
+ if error_bars['plus_values']:
+ # Write the c:plus element.
+ self._xml_start_tag('c:plus')
+
+ if isinstance(error_bars['plus_values'], list):
+ self._write_num_lit(error_bars['plus_values'])
+ else:
+ self._write_num_ref(error_bars['plus_values'],
+ error_bars['plus_data'],
+ 'num')
+ self._xml_end_tag('c:plus')
+
+ if error_bars['minus_values']:
+ # Write the c:minus element.
+ self._xml_start_tag('c:minus')
+
+ if isinstance(error_bars['minus_values'], list):
+ self._write_num_lit(error_bars['minus_values'])
+ else:
+ self._write_num_ref(error_bars['minus_values'],
+ error_bars['minus_data'],
+ 'num')
+ self._xml_end_tag('c:minus')
+
+ def _write_num_lit(self, data):
+ # Write the <c:numLit> element for literal number list elements.
+ count = len(data)
+
+ # Write the c:numLit element.
+ self._xml_start_tag('c:numLit')
+
+ # Write the c:formatCode element.
+ self._write_format_code('General')
+
+ # Write the c:ptCount element.
+ self._write_pt_count(count)
+
+ for i in range(count):
+ token = data[i]
+
+ if token is None:
+ continue
+
+ try:
+ float(token)
+ except ValueError:
+ # Write non-numeric data as 0.
+ token = 0
+
+ # Write the c:pt element.
+ self._write_pt(i, token)
+
+ self._xml_end_tag('c:numLit')
+
+ def _write_up_down_bars(self):
+ # Write the <c:upDownBars> element.
+ up_down_bars = self.up_down_bars
+
+ if up_down_bars is None:
+ return
+
+ self._xml_start_tag('c:upDownBars')
+
+ # Write the c:gapWidth element.
+ self._write_gap_width(150)
+
+ # Write the c:upBars element.
+ self._write_up_bars(up_down_bars.get('up'))
+
+ # Write the c:downBars element.
+ self._write_down_bars(up_down_bars.get('down'))
+
+ self._xml_end_tag('c:upDownBars')
+
+ def _write_gap_width(self, val):
+ # Write the <c:gapWidth> element.
+
+ if val is None:
+ return
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:gapWidth', attributes)
+
+ def _write_up_bars(self, bar_format):
+ # Write the <c:upBars> element.
+
+ if bar_format['line'] and bar_format['line']['defined']:
+ self._xml_start_tag('c:upBars')
+
+ # Write the c:spPr element.
+ self._write_sp_pr(bar_format)
+
+ self._xml_end_tag('c:upBars')
+ else:
+ self._xml_empty_tag('c:upBars')
+
+ def _write_down_bars(self, bar_format):
+ # Write the <c:downBars> element.
+
+ if bar_format['line'] and bar_format['line']['defined']:
+ self._xml_start_tag('c:downBars')
+
+ # Write the c:spPr element.
+ self._write_sp_pr(bar_format)
+
+ self._xml_end_tag('c:downBars')
+ else:
+ self._xml_empty_tag('c:downBars')
+
+ def _write_disp_units(self, units, display):
+ # Write the <c:dispUnits> element.
+
+ if not units:
+ return
+
+ attributes = [('val', units)]
+
+ self._xml_start_tag('c:dispUnits')
+ self._xml_empty_tag('c:builtInUnit', attributes)
+
+ if display:
+ self._xml_start_tag('c:dispUnitsLbl')
+ self._xml_empty_tag('c:layout')
+ self._xml_end_tag('c:dispUnitsLbl')
+
+ self._xml_end_tag('c:dispUnits')
+
+ def _write_a_grad_fill(self, gradient):
+ # Write the <a:gradFill> element.
+
+ attributes = [('flip', 'none'), ('rotWithShape', '1')]
+
+ if gradient['type'] == 'linear':
+ attributes = []
+
+ self._xml_start_tag('a:gradFill', attributes)
+
+ # Write the a:gsLst element.
+ self._write_a_gs_lst(gradient)
+
+ if gradient['type'] == 'linear':
+ # Write the a:lin element.
+ self._write_a_lin(gradient['angle'])
+ else:
+ # Write the a:path element.
+ self._write_a_path(gradient['type'])
+
+ # Write the a:tileRect element.
+ self._write_a_tile_rect(gradient['type'])
+
+ self._xml_end_tag('a:gradFill')
+
+ def _write_a_gs_lst(self, gradient):
+ # Write the <a:gsLst> element.
+ positions = gradient['positions']
+ colors = gradient['colors']
+
+ self._xml_start_tag('a:gsLst')
+
+ for i in range(len(colors)):
+ pos = int(positions[i] * 1000)
+ attributes = [('pos', pos)]
+ self._xml_start_tag('a:gs', attributes)
+
+ # Write the a:srgbClr element.
+ # TODO: Wait for a feature request to support transparency.
+ color = get_rgb_color(colors[i])
+ self._write_a_srgb_clr(color)
+
+ self._xml_end_tag('a:gs')
+
+ self._xml_end_tag('a:gsLst')
+
+ def _write_a_lin(self, angle):
+ # Write the <a:lin> element.
+
+ angle = int(60000 * angle)
+
+ attributes = [
+ ('ang', angle),
+ ('scaled', '0'),
+ ]
+
+ self._xml_empty_tag('a:lin', attributes)
+
+ def _write_a_path(self, gradient_type):
+ # Write the <a:path> element.
+
+ attributes = [('path', gradient_type)]
+
+ self._xml_start_tag('a:path', attributes)
+
+ # Write the a:fillToRect element.
+ self._write_a_fill_to_rect(gradient_type)
+
+ self._xml_end_tag('a:path')
+
+ def _write_a_fill_to_rect(self, gradient_type):
+ # Write the <a:fillToRect> element.
+
+ if gradient_type == 'shape':
+ attributes = [
+ ('l', '50000'),
+ ('t', '50000'),
+ ('r', '50000'),
+ ('b', '50000'),
+ ]
+ else:
+ attributes = [
+ ('l', '100000'),
+ ('t', '100000'),
+ ]
+
+ self._xml_empty_tag('a:fillToRect', attributes)
+
+ def _write_a_tile_rect(self, gradient_type):
+ # Write the <a:tileRect> element.
+
+ if gradient_type == 'shape':
+ attributes = []
+ else:
+ attributes = [
+ ('r', '-100000'),
+ ('b', '-100000'),
+ ]
+
+ self._xml_empty_tag('a:tileRect', attributes)
+
+ def _write_a_patt_fill(self, pattern):
+ # Write the <a:pattFill> element.
+
+ attributes = [('prst', pattern['pattern'])]
+
+ self._xml_start_tag('a:pattFill', attributes)
+
+ # Write the a:fgClr element.
+ self._write_a_fg_clr(pattern['fg_color'])
+
+ # Write the a:bgClr element.
+ self._write_a_bg_clr(pattern['bg_color'])
+
+ self._xml_end_tag('a:pattFill')
+
+ def _write_a_fg_clr(self, color):
+ # Write the <a:fgClr> element.
+
+ color = get_rgb_color(color)
+
+ self._xml_start_tag('a:fgClr')
+
+ # Write the a:srgbClr element.
+ self._write_a_srgb_clr(color)
+
+ self._xml_end_tag('a:fgClr')
+
+ def _write_a_bg_clr(self, color):
+ # Write the <a:bgClr> element.
+
+ color = get_rgb_color(color)
+
+ self._xml_start_tag('a:bgClr')
+
+ # Write the a:srgbClr element.
+ self._write_a_srgb_clr(color)
+
+ self._xml_end_tag('a:bgClr')
diff --git a/xlsxwriter/chart_area.py b/xlsxwriter/chart_area.py
new file mode 100644
index 0000000..19c6541
--- /dev/null
+++ b/xlsxwriter/chart_area.py
@@ -0,0 +1,103 @@
+###############################################################################
+#
+# ChartArea - A class for writing the Excel XLSX Area charts.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from . import chart
+
+
+class ChartArea(chart.Chart):
+ """
+ A class for writing the Excel XLSX Area charts.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self, options=None):
+ """
+ Constructor.
+
+ """
+ super(ChartArea, self).__init__()
+
+ if options is None:
+ options = {}
+
+ self.subtype = options.get('subtype')
+
+ if not self.subtype:
+ self.subtype = 'standard'
+
+ self.cross_between = 'midCat'
+ self.show_crosses = 0
+
+ # Override and reset the default axis values.
+ if self.subtype == 'percent_stacked':
+ self.y_axis['defaults']['num_format'] = '0%'
+
+ # Set the available data label positions for this chart type.
+ self.label_position_default = 'center'
+ self.label_positions = {'center': 'ctr'}
+
+ self.set_y_axis({})
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _write_chart_type(self, args):
+ # Override the virtual superclass method with a chart specific method.
+ # Write the c:areaChart element.
+ self._write_area_chart(args)
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+ #
+ def _write_area_chart(self, args):
+ # Write the <c:areaChart> element.
+
+ if args['primary_axes']:
+ series = self._get_primary_axes_series()
+ else:
+ series = self._get_secondary_axes_series()
+
+ if not len(series):
+ return
+
+ subtype = self.subtype
+
+ if subtype == 'percent_stacked':
+ subtype = 'percentStacked'
+
+ self._xml_start_tag('c:areaChart')
+
+ # Write the c:grouping element.
+ self._write_grouping(subtype)
+
+ # Write the series elements.
+ for data in series:
+ self._write_ser(data)
+
+ # Write the c:dropLines element.
+ self._write_drop_lines()
+
+ # Write the c:marker element.
+ self._write_marker_value()
+
+ # Write the c:axId elements
+ self._write_axis_ids(args)
+
+ self._xml_end_tag('c:areaChart')
diff --git a/xlsxwriter/chart_bar.py b/xlsxwriter/chart_bar.py
new file mode 100644
index 0000000..8f5ae20
--- /dev/null
+++ b/xlsxwriter/chart_bar.py
@@ -0,0 +1,175 @@
+###############################################################################
+#
+# ChartBar - A class for writing the Excel XLSX Bar charts.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from . import chart
+from warnings import warn
+
+
+class ChartBar(chart.Chart):
+ """
+ A class for writing the Excel XLSX Bar charts.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self, options=None):
+ """
+ Constructor.
+
+ """
+ super(ChartBar, self).__init__()
+
+ if options is None:
+ options = {}
+
+ self.subtype = options.get('subtype')
+
+ if not self.subtype:
+ self.subtype = 'clustered'
+
+ self.cat_axis_position = 'l'
+ self.val_axis_position = 'b'
+ self.horiz_val_axis = 0
+ self.horiz_cat_axis = 1
+ self.show_crosses = 0
+
+ # Override and reset the default axis values.
+ self.x_axis['defaults']['major_gridlines'] = {'visible': 1}
+ self.y_axis['defaults']['major_gridlines'] = {'visible': 0}
+
+ if self.subtype == 'percent_stacked':
+ self.x_axis['defaults']['num_format'] = '0%'
+
+ # Set the available data label positions for this chart type.
+ self.label_position_default = 'outside_end'
+ self.label_positions = {
+ 'center': 'ctr',
+ 'inside_base': 'inBase',
+ 'inside_end': 'inEnd',
+ 'outside_end': 'outEnd'}
+
+ self.set_x_axis({})
+ self.set_y_axis({})
+
+ def combine(self, chart=None):
+ """
+ Create a combination chart with a secondary chart.
+
+ Note: Override parent method to add an extra check that is required
+ for Bar charts to ensure that their combined chart is on a secondary
+ axis.
+
+ Args:
+ chart: The secondary chart to combine with the primary chart.
+
+ Returns:
+ Nothing.
+
+ """
+ if chart is None:
+ return
+
+ if not chart.is_secondary:
+ warn('Charts combined with Bar charts must be on a secondary axis')
+
+ self.combined = chart
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _write_chart_type(self, args):
+ # Override the virtual superclass method with a chart specific method.
+ if args['primary_axes']:
+ # Reverse X and Y axes for Bar charts.
+ tmp = self.y_axis
+ self.y_axis = self.x_axis
+ self.x_axis = tmp
+
+ if self.y2_axis['position'] == 'r':
+ self.y2_axis['position'] = 't'
+
+ # Write the c:barChart element.
+ self._write_bar_chart(args)
+
+ def _write_bar_chart(self, args):
+ # Write the <c:barChart> element.
+
+ if args['primary_axes']:
+ series = self._get_primary_axes_series()
+ else:
+ series = self._get_secondary_axes_series()
+
+ if not len(series):
+ return
+
+ subtype = self.subtype
+ if subtype == 'percent_stacked':
+ subtype = 'percentStacked'
+
+ # Set a default overlap for stacked charts.
+ if 'stacked' in self.subtype:
+ if self.series_overlap_1 is None:
+ self.series_overlap_1 = 100
+
+ self._xml_start_tag('c:barChart')
+
+ # Write the c:barDir element.
+ self._write_bar_dir()
+
+ # Write the c:grouping element.
+ self._write_grouping(subtype)
+
+ # Write the c:ser elements.
+ for data in series:
+ self._write_ser(data)
+
+ # Write the c:marker element.
+ self._write_marker_value()
+
+ # Write the c:gapWidth element.
+ if args['primary_axes']:
+ self._write_gap_width(self.series_gap_1)
+ else:
+ self._write_gap_width(self.series_gap_2)
+
+ # Write the c:overlap element.
+ if args['primary_axes']:
+ self._write_overlap(self.series_overlap_1)
+ else:
+ self._write_overlap(self.series_overlap_2)
+
+ # Write the c:axId elements
+ self._write_axis_ids(args)
+
+ self._xml_end_tag('c:barChart')
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_bar_dir(self):
+ # Write the <c:barDir> element.
+ val = 'bar'
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:barDir', attributes)
+
+ def _write_err_dir(self, val):
+ # Overridden from Chart class since it is not used in Bar charts.
+ pass
diff --git a/xlsxwriter/chart_column.py b/xlsxwriter/chart_column.py
new file mode 100644
index 0000000..181ea55
--- /dev/null
+++ b/xlsxwriter/chart_column.py
@@ -0,0 +1,134 @@
+###############################################################################
+#
+# ChartColumn - A class for writing the Excel XLSX Column charts.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from . import chart
+
+
+class ChartColumn(chart.Chart):
+ """
+ A class for writing the Excel XLSX Column charts.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self, options=None):
+ """
+ Constructor.
+
+ """
+ super(ChartColumn, self).__init__()
+
+ if options is None:
+ options = {}
+
+ self.subtype = options.get('subtype')
+
+ if not self.subtype:
+ self.subtype = 'clustered'
+
+ self.horiz_val_axis = 0
+
+ if self.subtype == 'percent_stacked':
+ self.y_axis['defaults']['num_format'] = '0%'
+
+ # Set the available data label positions for this chart type.
+ self.label_position_default = 'outside_end'
+ self.label_positions = {
+ 'center': 'ctr',
+ 'inside_base': 'inBase',
+ 'inside_end': 'inEnd',
+ 'outside_end': 'outEnd'}
+
+ self.set_y_axis({})
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _write_chart_type(self, args):
+ # Override the virtual superclass method with a chart specific method.
+
+ # Write the c:barChart element.
+ self._write_bar_chart(args)
+
+ def _write_bar_chart(self, args):
+ # Write the <c:barChart> element.
+
+ if args['primary_axes']:
+ series = self._get_primary_axes_series()
+ else:
+ series = self._get_secondary_axes_series()
+
+ if not len(series):
+ return
+
+ subtype = self.subtype
+ if subtype == 'percent_stacked':
+ subtype = 'percentStacked'
+
+ # Set a default overlap for stacked charts.
+ if 'stacked' in self.subtype:
+ if self.series_overlap_1 is None:
+ self.series_overlap_1 = 100
+
+ self._xml_start_tag('c:barChart')
+
+ # Write the c:barDir element.
+ self._write_bar_dir()
+
+ # Write the c:grouping element.
+ self._write_grouping(subtype)
+
+ # Write the c:ser elements.
+ for data in series:
+ self._write_ser(data)
+
+ # Write the c:marker element.
+ self._write_marker_value()
+
+ # Write the c:gapWidth element.
+ if args['primary_axes']:
+ self._write_gap_width(self.series_gap_1)
+ else:
+ self._write_gap_width(self.series_gap_2)
+
+ # Write the c:overlap element.
+ if args['primary_axes']:
+ self._write_overlap(self.series_overlap_1)
+ else:
+ self._write_overlap(self.series_overlap_2)
+
+ # Write the c:axId elements
+ self._write_axis_ids(args)
+
+ self._xml_end_tag('c:barChart')
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_bar_dir(self):
+ # Write the <c:barDir> element.
+ val = 'col'
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:barDir', attributes)
+
+ def _write_err_dir(self, val):
+ # Overridden from Chart class since it is not used in Column charts.
+ pass
diff --git a/xlsxwriter/chart_doughnut.py b/xlsxwriter/chart_doughnut.py
new file mode 100644
index 0000000..4240087
--- /dev/null
+++ b/xlsxwriter/chart_doughnut.py
@@ -0,0 +1,102 @@
+###############################################################################
+#
+# ChartDoughnut - A class for writing the Excel XLSX Doughnut charts.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from warnings import warn
+from . import chart_pie
+
+
+class ChartDoughnut(chart_pie.ChartPie):
+ """
+ A class for writing the Excel XLSX Doughnut charts.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self, options=None):
+ """
+ Constructor.
+
+ """
+ super(ChartDoughnut, self).__init__()
+
+ if options is None:
+ options = {}
+
+ self.vary_data_color = 1
+ self.rotation = 0
+ self.hole_size = 50
+
+ def set_hole_size(self, size):
+ """
+ Set the Doughnut chart hole size.
+
+ Args:
+ size: 10 <= size <= 90.
+
+ Returns:
+ Nothing.
+
+ """
+ if size is None:
+ return
+
+ # Ensure the size is in Excel's range.
+ if size < 10 or size > 90:
+ warn("Chart hole size %d outside Excel range: 10 <= size <= 90"
+ % size)
+ return
+
+ self.hole_size = int(size)
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _write_chart_type(self, args):
+ # Override the virtual superclass method with a chart specific method.
+ # Write the c:doughnutChart element.
+ self._write_doughnut_chart(args)
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_doughnut_chart(self, args):
+ # Write the <c:doughnutChart> element. Over-ridden method to remove
+ # axis_id code since Doughnut charts don't require val and cat axes.
+ self._xml_start_tag('c:doughnutChart')
+
+ # Write the c:varyColors element.
+ self._write_vary_colors()
+
+ # Write the series elements.
+ for data in self.series:
+ self._write_ser(data)
+
+ # Write the c:firstSliceAng element.
+ self._write_first_slice_ang()
+
+ # Write the c:holeSize element.
+ self._write_c_hole_size()
+
+ self._xml_end_tag('c:doughnutChart')
+
+ def _write_c_hole_size(self):
+ # Write the <c:holeSize> element.
+ attributes = [('val', self.hole_size)]
+
+ self._xml_empty_tag('c:holeSize', attributes)
diff --git a/xlsxwriter/chart_line.py b/xlsxwriter/chart_line.py
new file mode 100644
index 0000000..473ea7c
--- /dev/null
+++ b/xlsxwriter/chart_line.py
@@ -0,0 +1,119 @@
+###############################################################################
+#
+# ChartLine - A class for writing the Excel XLSX Line charts.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from . import chart
+
+
+class ChartLine(chart.Chart):
+ """
+ A class for writing the Excel XLSX Line charts.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self, options=None):
+ """
+ Constructor.
+
+ """
+ super(ChartLine, self).__init__()
+
+ if options is None:
+ options = {}
+
+ self.default_marker = {'type': 'none'}
+ self.smooth_allowed = True
+
+ # Set the available data label positions for this chart type.
+ self.label_position_default = 'right'
+ self.label_positions = {
+ 'center': 'ctr',
+ 'right': 'r',
+ 'left': 'l',
+ 'above': 't',
+ 'below': 'b',
+ # For backward compatibility.
+ 'top': 't',
+ 'bottom': 'b'}
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _write_chart_type(self, args):
+ # Override the virtual superclass method with a chart specific method.
+ # Write the c:lineChart element.
+ self._write_line_chart(args)
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_line_chart(self, args):
+ # Write the <c:lineChart> element.
+
+ if args['primary_axes']:
+ series = self._get_primary_axes_series()
+ else:
+ series = self._get_secondary_axes_series()
+
+ if not len(series):
+ return
+
+ self._xml_start_tag('c:lineChart')
+
+ # Write the c:grouping element.
+ self._write_grouping('standard')
+
+ # Write the series elements.
+ for data in series:
+ self._write_ser(data)
+
+ # Write the c:dropLines element.
+ self._write_drop_lines()
+
+ # Write the c:hiLowLines element.
+ self._write_hi_low_lines()
+
+ # Write the c:upDownBars element.
+ self._write_up_down_bars()
+
+ # Write the c:marker element.
+ self._write_marker_value()
+
+ # Write the c:axId elements
+ self._write_axis_ids(args)
+
+ self._xml_end_tag('c:lineChart')
+
+ def _write_d_pt_point(self, index, point):
+ # Write an individual <c:dPt> element. Override the parent method to
+ # add markers.
+
+ self._xml_start_tag('c:dPt')
+
+ # Write the c:idx element.
+ self._write_idx(index)
+
+ self._xml_start_tag('c:marker')
+
+ # Write the c:spPr element.
+ self._write_sp_pr(point)
+
+ self._xml_end_tag('c:marker')
+
+ self._xml_end_tag('c:dPt')
diff --git a/xlsxwriter/chart_pie.py b/xlsxwriter/chart_pie.py
new file mode 100644
index 0000000..fdcfcb0
--- /dev/null
+++ b/xlsxwriter/chart_pie.py
@@ -0,0 +1,227 @@
+###############################################################################
+#
+# ChartPie - A class for writing the Excel XLSX Pie charts.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from warnings import warn
+from . import chart
+
+
+class ChartPie(chart.Chart):
+ """
+ A class for writing the Excel XLSX Pie charts.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self, options=None):
+ """
+ Constructor.
+
+ """
+ super(ChartPie, self).__init__()
+
+ if options is None:
+ options = {}
+
+ self.vary_data_color = 1
+ self.rotation = 0
+
+ # Set the available data label positions for this chart type.
+ self.label_position_default = 'best_fit'
+ self.label_positions = {
+ 'center': 'ctr',
+ 'inside_end': 'inEnd',
+ 'outside_end': 'outEnd',
+ 'best_fit': 'bestFit'}
+
+ def set_rotation(self, rotation):
+ """
+ Set the Pie/Doughnut chart rotation: the angle of the first slice.
+
+ Args:
+ rotation: First segment angle: 0 <= rotation <= 360.
+
+ Returns:
+ Nothing.
+
+ """
+ if rotation is None:
+ return
+
+ # Ensure the rotation is in Excel's range.
+ if rotation < 0 or rotation > 360:
+ warn("Chart rotation %d outside Excel range: 0 <= rotation <= 360"
+ % rotation)
+ return
+
+ self.rotation = int(rotation)
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _write_chart_type(self, args):
+ # Override the virtual superclass method with a chart specific method.
+ # Write the c:pieChart element.
+ self._write_pie_chart(args)
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_pie_chart(self, args):
+ # Write the <c:pieChart> element. Over-ridden method to remove
+ # axis_id code since Pie charts don't require val and cat axes.
+ self._xml_start_tag('c:pieChart')
+
+ # Write the c:varyColors element.
+ self._write_vary_colors()
+
+ # Write the series elements.
+ for data in self.series:
+ self._write_ser(data)
+
+ # Write the c:firstSliceAng element.
+ self._write_first_slice_ang()
+
+ self._xml_end_tag('c:pieChart')
+
+ def _write_plot_area(self):
+ # Over-ridden method to remove the cat_axis() and val_axis() code
+ # since Pie charts don't require those axes.
+ #
+ # Write the <c:plotArea> element.
+
+ self._xml_start_tag('c:plotArea')
+
+ # Write the c:layout element.
+ self._write_layout(self.plotarea.get('layout'), 'plot')
+
+ # Write the subclass chart type element.
+ self._write_chart_type(None)
+
+ self._xml_end_tag('c:plotArea')
+
+ def _write_legend(self):
+ # Over-ridden method to add <c:txPr> to legend.
+ # Write the <c:legend> element.
+
+ position = self.legend_position
+ font = self.legend_font
+ delete_series = []
+ overlay = 0
+
+ if (self.legend_delete_series is not None
+ and type(self.legend_delete_series) is list):
+ delete_series = self.legend_delete_series
+
+ if position.startswith('overlay_'):
+ position = position.replace('overlay_', '')
+ overlay = 1
+
+ allowed = {
+ 'right': 'r',
+ 'left': 'l',
+ 'top': 't',
+ 'bottom': 'b',
+ }
+
+ if position == 'none':
+ return
+
+ if position not in allowed:
+ return
+
+ position = allowed[position]
+
+ self._xml_start_tag('c:legend')
+
+ # Write the c:legendPos element.
+ self._write_legend_pos(position)
+
+ # Remove series labels from the legend.
+ for index in delete_series:
+ # Write the c:legendEntry element.
+ self._write_legend_entry(index)
+
+ # Write the c:layout element.
+ self._write_layout(self.legend_layout, 'legend')
+
+ # Write the c:overlay element.
+ if overlay:
+ self._write_overlay()
+
+ # Write the c:txPr element. Over-ridden.
+ self._write_tx_pr_legend(None, font)
+
+ self._xml_end_tag('c:legend')
+
+ def _write_tx_pr_legend(self, horiz, font):
+ # Write the <c:txPr> element for legends.
+
+ if font and font.get('rotation'):
+ rotation = font['rotation']
+ else:
+ rotation = None
+
+ self._xml_start_tag('c:txPr')
+
+ # Write the a:bodyPr element.
+ self._write_a_body_pr(rotation, horiz)
+
+ # Write the a:lstStyle element.
+ self._write_a_lst_style()
+
+ # Write the a:p element.
+ self._write_a_p_legend(font)
+
+ self._xml_end_tag('c:txPr')
+
+ def _write_a_p_legend(self, font):
+ # Write the <a:p> element for legends.
+
+ self._xml_start_tag('a:p')
+
+ # Write the a:pPr element.
+ self._write_a_p_pr_legend(font)
+
+ # Write the a:endParaRPr element.
+ self._write_a_end_para_rpr()
+
+ self._xml_end_tag('a:p')
+
+ def _write_a_p_pr_legend(self, font):
+ # Write the <a:pPr> element for legends.
+ attributes = [('rtl', 0)]
+
+ self._xml_start_tag('a:pPr', attributes)
+
+ # Write the a:defRPr element.
+ self._write_a_def_rpr(font)
+
+ self._xml_end_tag('a:pPr')
+
+ def _write_vary_colors(self):
+ # Write the <c:varyColors> element.
+ attributes = [('val', 1)]
+
+ self._xml_empty_tag('c:varyColors', attributes)
+
+ def _write_first_slice_ang(self):
+ # Write the <c:firstSliceAng> element.
+ attributes = [('val', self.rotation)]
+
+ self._xml_empty_tag('c:firstSliceAng', attributes)
diff --git a/xlsxwriter/chart_radar.py b/xlsxwriter/chart_radar.py
new file mode 100644
index 0000000..581e2b8
--- /dev/null
+++ b/xlsxwriter/chart_radar.py
@@ -0,0 +1,101 @@
+###############################################################################
+#
+# ChartRadar - A class for writing the Excel XLSX Radar charts.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from . import chart
+
+
+class ChartRadar(chart.Chart):
+ """
+ A class for writing the Excel XLSX Radar charts.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self, options=None):
+ """
+ Constructor.
+
+ """
+ super(ChartRadar, self).__init__()
+
+ if options is None:
+ options = {}
+
+ self.subtype = options.get('subtype')
+
+ if not self.subtype:
+ self.subtype = 'marker'
+ self.default_marker = {'type': 'none'}
+
+ # Override and reset the default axis values.
+ self.x_axis['defaults']['major_gridlines'] = {'visible': 1}
+ self.set_x_axis({})
+
+ # Set the available data label positions for this chart type.
+ self.label_position_default = 'center'
+ self.label_positions = {'center': 'ctr'}
+
+ # Hardcode major_tick_mark for now until there is an accessor.
+ self.y_axis['major_tick_mark'] = 'cross'
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _write_chart_type(self, args):
+ # Write the c:radarChart element.
+ self._write_radar_chart(args)
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_radar_chart(self, args):
+ # Write the <c:radarChart> element.
+
+ if args['primary_axes']:
+ series = self._get_primary_axes_series()
+ else:
+ series = self._get_secondary_axes_series()
+
+ if not len(series):
+ return
+
+ self._xml_start_tag('c:radarChart')
+
+ # Write the c:radarStyle element.
+ self._write_radar_style()
+
+ # Write the series elements.
+ for data in series:
+ self._write_ser(data)
+
+ # Write the c:axId elements
+ self._write_axis_ids(args)
+
+ self._xml_end_tag('c:radarChart')
+
+ def _write_radar_style(self):
+ # Write the <c:radarStyle> element.
+ val = 'marker'
+
+ if self.subtype == 'filled':
+ val = 'filled'
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:radarStyle', attributes)
diff --git a/xlsxwriter/chart_scatter.py b/xlsxwriter/chart_scatter.py
new file mode 100644
index 0000000..81eda8b
--- /dev/null
+++ b/xlsxwriter/chart_scatter.py
@@ -0,0 +1,328 @@
+###############################################################################
+#
+# ChartScatter - A class for writing the Excel XLSX Scatter charts.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from . import chart
+from warnings import warn
+
+
+class ChartScatter(chart.Chart):
+ """
+ A class for writing the Excel XLSX Scatter charts.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self, options=None):
+ """
+ Constructor.
+
+ """
+ super(ChartScatter, self).__init__()
+
+ if options is None:
+ options = {}
+
+ self.subtype = options.get('subtype')
+
+ if not self.subtype:
+ self.subtype = 'marker_only'
+
+ self.cross_between = 'midCat'
+ self.horiz_val_axis = 0
+ self.val_axis_position = 'b'
+ self.smooth_allowed = True
+ self.requires_category = True
+
+ # Set the available data label positions for this chart type.
+ self.label_position_default = 'right'
+ self.label_positions = {
+ 'center': 'ctr',
+ 'right': 'r',
+ 'left': 'l',
+ 'above': 't',
+ 'below': 'b',
+ # For backward compatibility.
+ 'top': 't',
+ 'bottom': 'b'}
+
+ def combine(self, chart=None):
+ """
+ Create a combination chart with a secondary chart.
+
+ Note: Override parent method to add a warning.
+
+ Args:
+ chart: The secondary chart to combine with the primary chart.
+
+ Returns:
+ Nothing.
+
+ """
+ if chart is None:
+ return
+
+ warn('Combined chart not currently supported with scatter chart '
+ 'as the primary chart')
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _write_chart_type(self, args):
+ # Override the virtual superclass method with a chart specific method.
+ # Write the c:scatterChart element.
+ self._write_scatter_chart(args)
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_scatter_chart(self, args):
+ # Write the <c:scatterChart> element.
+
+ if args['primary_axes']:
+ series = self._get_primary_axes_series()
+ else:
+ series = self._get_secondary_axes_series()
+
+ if not len(series):
+ return
+
+ style = 'lineMarker'
+ subtype = self.subtype
+
+ # Set the user defined chart subtype.
+ if subtype == 'marker_only':
+ style = 'lineMarker'
+
+ if subtype == 'straight_with_markers':
+ style = 'lineMarker'
+
+ if subtype == 'straight':
+ style = 'lineMarker'
+
+ if subtype == 'smooth_with_markers':
+ style = 'smoothMarker'
+
+ if subtype == 'smooth':
+ style = 'smoothMarker'
+
+ # Add default formatting to the series data.
+ self._modify_series_formatting()
+
+ self._xml_start_tag('c:scatterChart')
+
+ # Write the c:scatterStyle element.
+ self._write_scatter_style(style)
+
+ # Write the series elements.
+ for data in series:
+ self._write_ser(data)
+
+ # Write the c:marker element.
+ self._write_marker_value()
+
+ # Write the c:axId elements
+ self._write_axis_ids(args)
+
+ self._xml_end_tag('c:scatterChart')
+
+ def _write_ser(self, series):
+ # Over-ridden to write c:xVal/c:yVal instead of c:cat/c:val elements.
+ # Write the <c:ser> element.
+
+ index = self.series_index
+ self.series_index += 1
+
+ self._xml_start_tag('c:ser')
+
+ # Write the c:idx element.
+ self._write_idx(index)
+
+ # Write the c:order element.
+ self._write_order(index)
+
+ # Write the series name.
+ self._write_series_name(series)
+
+ # Write the c:spPr element.
+ self._write_sp_pr(series)
+
+ # Write the c:marker element.
+ self._write_marker(series.get('marker'))
+
+ # Write the c:dPt element.
+ self._write_d_pt(series.get('points'))
+
+ # Write the c:dLbls element.
+ self._write_d_lbls(series.get('labels'))
+
+ # Write the c:trendline element.
+ self._write_trendline(series.get('trendline'))
+
+ # Write the c:errBars element.
+ self._write_error_bars(series.get('error_bars'))
+
+ # Write the c:xVal element.
+ self._write_x_val(series)
+
+ # Write the c:yVal element.
+ self._write_y_val(series)
+
+ # Write the c:smooth element.
+ if 'smooth' in self.subtype and series['smooth'] is None:
+ # Default is on for smooth scatter charts.
+ self._write_c_smooth(True)
+ else:
+ self._write_c_smooth(series['smooth'])
+
+ self._xml_end_tag('c:ser')
+
+ def _write_plot_area(self):
+ # Over-ridden to have 2 valAx elements for scatter charts instead
+ # of catAx/valAx.
+ #
+ # Write the <c:plotArea> element.
+ self._xml_start_tag('c:plotArea')
+
+ # Write the c:layout element.
+ self._write_layout(self.plotarea.get('layout'), 'plot')
+
+ # Write the subclass chart elements for primary and secondary axes.
+ self._write_chart_type({'primary_axes': 1})
+ self._write_chart_type({'primary_axes': 0})
+
+ # Write c:catAx and c:valAx elements for series using primary axes.
+ self._write_cat_val_axis({'x_axis': self.x_axis,
+ 'y_axis': self.y_axis,
+ 'axis_ids': self.axis_ids,
+ 'position': 'b',
+ })
+
+ tmp = self.horiz_val_axis
+ self.horiz_val_axis = 1
+
+ self._write_val_axis({'x_axis': self.x_axis,
+ 'y_axis': self.y_axis,
+ 'axis_ids': self.axis_ids,
+ 'position': 'l',
+ })
+
+ self.horiz_val_axis = tmp
+
+ # Write c:valAx and c:catAx elements for series using secondary axes
+ self._write_cat_val_axis({'x_axis': self.x2_axis,
+ 'y_axis': self.y2_axis,
+ 'axis_ids': self.axis2_ids,
+ 'position': 'b',
+ })
+ self.horiz_val_axis = 1
+ self._write_val_axis({'x_axis': self.x2_axis,
+ 'y_axis': self.y2_axis,
+ 'axis_ids': self.axis2_ids,
+ 'position': 'l',
+ })
+
+ # Write the c:spPr element for the plotarea formatting.
+ self._write_sp_pr(self.plotarea)
+
+ self._xml_end_tag('c:plotArea')
+
+ def _write_x_val(self, series):
+ # Write the <c:xVal> element.
+ formula = series.get('categories')
+ data_id = series.get('cat_data_id')
+ data = self.formula_data[data_id]
+
+ self._xml_start_tag('c:xVal')
+
+ # Check the type of cached data.
+ data_type = self._get_data_type(data)
+
+ # TODO. Can a scatter plot have non-numeric data.
+ if data_type == 'str':
+ # Write the c:numRef element.
+ self._write_str_ref(formula, data, data_type)
+ else:
+ # Write the c:numRef element.
+ self._write_num_ref(formula, data, data_type)
+
+ self._xml_end_tag('c:xVal')
+
+ def _write_y_val(self, series):
+ # Write the <c:yVal> element.
+ formula = series.get('values')
+ data_id = series.get('val_data_id')
+ data = self.formula_data[data_id]
+
+ self._xml_start_tag('c:yVal')
+
+ # Unlike Cat axes data should only be numeric.
+ # Write the c:numRef element.
+ self._write_num_ref(formula, data, 'num')
+
+ self._xml_end_tag('c:yVal')
+
+ def _write_scatter_style(self, val):
+ # Write the <c:scatterStyle> element.
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('c:scatterStyle', attributes)
+
+ def _modify_series_formatting(self):
+ # Add default formatting to the series data unless it has already been
+ # specified by the user.
+ subtype = self.subtype
+
+ # The default scatter style "markers only" requires a line type.
+ if subtype == 'marker_only':
+
+ # Go through each series and define default values.
+ for series in self.series:
+
+ # Set a line type unless there is already a user defined type.
+ if not series['line']['defined']:
+ series['line'] = {'width': 2.25,
+ 'none': 1,
+ 'defined': 1,
+ }
+
+ # Turn markers off for subtypes that don't have them.
+ if 'marker' not in subtype:
+ # Go through each series and define default values.
+ for series in self.series:
+ # Set a marker type unless there is a user defined type.
+ if not series.get('marker'):
+ series['marker'] = {'type': 'none', 'defined': 1}
+
+ def _write_d_pt_point(self, index, point):
+ # Write an individual <c:dPt> element. Override the parent method to
+ # add markers.
+
+ self._xml_start_tag('c:dPt')
+
+ # Write the c:idx element.
+ self._write_idx(index)
+
+ self._xml_start_tag('c:marker')
+
+ # Write the c:spPr element.
+ self._write_sp_pr(point)
+
+ self._xml_end_tag('c:marker')
+
+ self._xml_end_tag('c:dPt')
diff --git a/xlsxwriter/chart_stock.py b/xlsxwriter/chart_stock.py
new file mode 100644
index 0000000..70de3ac
--- /dev/null
+++ b/xlsxwriter/chart_stock.py
@@ -0,0 +1,130 @@
+###############################################################################
+#
+# ChartStock - A class for writing the Excel XLSX Stock charts.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from . import chart
+
+
+class ChartStock(chart.Chart):
+ """
+ A class for writing the Excel XLSX Stock charts.
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self, options=None):
+ """
+ Constructor.
+
+ """
+ super(ChartStock, self).__init__()
+
+ if options is None:
+ options = {}
+
+ self.show_crosses = 0
+ self.hi_low_lines = {}
+ self.date_category = True
+
+ # Override and reset the default axis values.
+ self.x_axis['defaults']['num_format'] = 'dd/mm/yyyy'
+ self.x2_axis['defaults']['num_format'] = 'dd/mm/yyyy'
+
+ # Set the available data label positions for this chart type.
+ self.label_position_default = 'right'
+ self.label_positions = {
+ 'center': 'ctr',
+ 'right': 'r',
+ 'left': 'l',
+ 'above': 't',
+ 'below': 'b',
+ # For backward compatibility.
+ 'top': 't',
+ 'bottom': 'b'}
+
+ self.set_x_axis({})
+ self.set_x2_axis({})
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _write_chart_type(self, args):
+ # Override the virtual superclass method with a chart specific method.
+ # Write the c:stockChart element.
+ self._write_stock_chart(args)
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_stock_chart(self, args):
+ # Write the <c:stockChart> element.
+ # Overridden to add hi_low_lines().
+
+ if args['primary_axes']:
+ series = self._get_primary_axes_series()
+ else:
+ series = self._get_secondary_axes_series()
+
+ if not len(series):
+ return
+
+ # Add default formatting to the series data.
+ self._modify_series_formatting()
+
+ self._xml_start_tag('c:stockChart')
+
+ # Write the series elements.
+ for data in series:
+ self._write_ser(data)
+
+ # Write the c:dropLines element.
+ self._write_drop_lines()
+
+ # Write the c:hiLowLines element.
+ if args.get('primary_axes'):
+ self._write_hi_low_lines()
+
+ # Write the c:upDownBars element.
+ self._write_up_down_bars()
+
+ # Write the c:marker element.
+ self._write_marker_value()
+
+ # Write the c:axId elements
+ self._write_axis_ids(args)
+
+ self._xml_end_tag('c:stockChart')
+
+ def _modify_series_formatting(self):
+ # Add default formatting to the series data.
+
+ index = 0
+
+ for series in self.series:
+ if index % 4 != 3:
+ if not series['line']['defined']:
+ series['line'] = {'width': 2.25,
+ 'none': 1,
+ 'defined': 1}
+
+ if series['marker'] is None:
+ if index % 4 == 2:
+ series['marker'] = {'type': 'dot', 'size': 3}
+ else:
+ series['marker'] = {'type': 'none'}
+
+ index += 1
diff --git a/xlsxwriter/chartsheet.py b/xlsxwriter/chartsheet.py
new file mode 100644
index 0000000..4939297
--- /dev/null
+++ b/xlsxwriter/chartsheet.py
@@ -0,0 +1,179 @@
+###############################################################################
+#
+# Chartsheet - A class for writing the Excel XLSX Worksheet file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from . import worksheet
+from .drawing import Drawing
+
+
+class Chartsheet(worksheet.Worksheet):
+ """
+ A class for writing the Excel XLSX Chartsheet file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(Chartsheet, self).__init__()
+
+ self.is_chartsheet = True
+ self.drawing = None
+ self.chart = None
+ self.charts = []
+ self.zoom_scale_normal = 0
+ self.orientation = 0
+ self.protection = False
+
+ def set_chart(self, chart):
+ """
+ Set the chart object for the chartsheet.
+ Args:
+ chart: Chart object.
+ Returns:
+ chart: A reference to the chart object.
+ """
+ chart.embedded = False
+ chart.protection = self.protection
+ self.chart = chart
+ self.charts.append([0, 0, chart, 0, 0, 1, 1])
+ return chart
+
+ def protect(self, password='', options=None):
+ """
+ Set the password and protection options of the worksheet.
+
+ Args:
+ password: An optional password string.
+ options: A dictionary of worksheet objects to protect.
+
+ Returns:
+ Nothing.
+
+ """
+ # Overridden from parent worksheet class.
+ if self.chart:
+ self.chart.protection = True
+ else:
+ self.protection = True
+
+ if not options:
+ options = {}
+
+ options = options.copy()
+
+ options['sheet'] = False
+ options['content'] = True
+ options['scenarios'] = True
+
+ # Call the parent method.
+ super(Chartsheet, self).protect(password, options)
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ # Write the root worksheet element.
+ self._write_chartsheet()
+
+ # Write the worksheet properties.
+ self._write_sheet_pr()
+
+ # Write the sheet view properties.
+ self._write_sheet_views()
+
+ # Write the sheetProtection element.
+ self._write_sheet_protection()
+
+ # Write the printOptions element.
+ self._write_print_options()
+
+ # Write the worksheet page_margins.
+ self._write_page_margins()
+
+ # Write the worksheet page setup.
+ self._write_page_setup()
+
+ # Write the headerFooter element.
+ self._write_header_footer()
+
+ # Write the drawing element.
+ self._write_drawings()
+
+ # Close the worksheet tag.
+ self._xml_end_tag('chartsheet')
+
+ # Close the file.
+ self._xml_close()
+
+ def _prepare_chart(self, index, chart_id, drawing_id):
+ # Set up chart/drawings.
+
+ self.chart.id = chart_id - 1
+
+ self.drawing = Drawing()
+ self.drawing.orientation = self.orientation
+
+ self.external_drawing_links.append(['/drawing',
+ '../drawings/drawing'
+ + str(drawing_id)
+ + '.xml'])
+
+ self.drawing_links.append(['/chart',
+ '../charts/chart'
+ + str(chart_id)
+ + '.xml'])
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_chartsheet(self):
+ # Write the <worksheet> element. This is the root element.
+
+ schema = 'http://schemas.openxmlformats.org/'
+ xmlns = schema + 'spreadsheetml/2006/main'
+ xmlns_r = schema + 'officeDocument/2006/relationships'
+
+ attributes = [
+ ('xmlns', xmlns),
+ ('xmlns:r', xmlns_r)]
+
+ self._xml_start_tag('chartsheet', attributes)
+
+ def _write_sheet_pr(self):
+ # Write the <sheetPr> element for Sheet level properties.
+ attributes = []
+
+ if self.filter_on:
+ attributes.append(('filterMode', 1))
+
+ if (self.fit_page or self.tab_color):
+ self._xml_start_tag('sheetPr', attributes)
+ self._write_tab_color()
+ self._write_page_set_up_pr()
+ self._xml_end_tag('sheetPr')
+ else:
+ self._xml_empty_tag('sheetPr', attributes)
diff --git a/xlsxwriter/comments.py b/xlsxwriter/comments.py
new file mode 100644
index 0000000..d948587
--- /dev/null
+++ b/xlsxwriter/comments.py
@@ -0,0 +1,205 @@
+###############################################################################
+#
+# Comments - A class for writing the Excel XLSX Worksheet file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import re
+
+from . import xmlwriter
+from .utility import xl_rowcol_to_cell
+
+
+class Comments(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX Comments file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(Comments, self).__init__()
+ self.author_ids = {}
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _assemble_xml_file(self, comments_data=[]):
+ # Assemble and write the XML file.
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ # Write the comments element.
+ self._write_comments()
+
+ # Write the authors element.
+ self._write_authors(comments_data)
+
+ # Write the commentList element.
+ self._write_comment_list(comments_data)
+
+ self._xml_end_tag('comments')
+
+ # Close the file.
+ self._xml_close()
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_comments(self):
+ # Write the <comments> element.
+ xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
+
+ attributes = [('xmlns', xmlns)]
+
+ self._xml_start_tag('comments', attributes)
+
+ def _write_authors(self, comment_data):
+ # Write the <authors> element.
+ author_count = 0
+
+ self._xml_start_tag('authors')
+
+ for comment in comment_data:
+ author = comment[3]
+
+ if author is not None and author not in self.author_ids:
+ # Store the author id.
+ self.author_ids[author] = author_count
+ author_count += 1
+
+ # Write the author element.
+ self._write_author(author)
+
+ self._xml_end_tag('authors')
+
+ def _write_author(self, data):
+ # Write the <author> element.
+ self._xml_data_element('author', data)
+
+ def _write_comment_list(self, comment_data):
+ # Write the <commentList> element.
+ self._xml_start_tag('commentList')
+
+ for comment in comment_data:
+ row = comment[0]
+ col = comment[1]
+ text = comment[2]
+ author = comment[3]
+
+ # Look up the author id.
+ author_id = None
+ if author is not None:
+ author_id = self.author_ids[author]
+
+ # Write the comment element.
+ self._write_comment(row, col, text, author_id)
+
+ self._xml_end_tag('commentList')
+
+ def _write_comment(self, row, col, text, author_id):
+ # Write the <comment> element.
+ ref = xl_rowcol_to_cell(row, col)
+
+ attributes = [('ref', ref)]
+
+ if author_id is not None:
+ attributes.append(('authorId', author_id))
+
+ self._xml_start_tag('comment', attributes)
+
+ # Write the text element.
+ self._write_text(text)
+
+ self._xml_end_tag('comment')
+
+ def _write_text(self, text):
+ # Write the <text> element.
+ self._xml_start_tag('text')
+
+ # Write the text r element.
+ self._write_text_r(text)
+
+ self._xml_end_tag('text')
+
+ def _write_text_r(self, text):
+ # Write the <r> element.
+ self._xml_start_tag('r')
+
+ # Write the rPr element.
+ self._write_r_pr()
+
+ # Write the text r element.
+ self._write_text_t(text)
+
+ self._xml_end_tag('r')
+
+ def _write_text_t(self, text):
+ # Write the text <t> element.
+ attributes = []
+
+ if re.search('^\s', text) or re.search('\s$', text):
+ attributes.append(('xml:space', 'preserve'))
+
+ self._xml_data_element('t', text, attributes)
+
+ def _write_r_pr(self):
+ # Write the <rPr> element.
+ self._xml_start_tag('rPr')
+
+ # Write the sz element.
+ self._write_sz()
+
+ # Write the color element.
+ self._write_color()
+
+ # Write the rFont element.
+ self._write_r_font()
+
+ # Write the family element.
+ self._write_family()
+
+ self._xml_end_tag('rPr')
+
+ def _write_sz(self):
+ # Write the <sz> element.
+ attributes = [('val', 8)]
+
+ self._xml_empty_tag('sz', attributes)
+
+ def _write_color(self):
+ # Write the <color> element.
+ attributes = [('indexed', 81)]
+
+ self._xml_empty_tag('color', attributes)
+
+ def _write_r_font(self):
+ # Write the <rFont> element.
+ attributes = [('val', 'Tahoma')]
+
+ self._xml_empty_tag('rFont', attributes)
+
+ def _write_family(self):
+ # Write the <family> element.
+ attributes = [('val', 2)]
+
+ self._xml_empty_tag('family', attributes)
diff --git a/xlsxwriter/compat_collections.py b/xlsxwriter/compat_collections.py
new file mode 100644
index 0000000..54db27d
--- /dev/null
+++ b/xlsxwriter/compat_collections.py
@@ -0,0 +1,196 @@
+"""
+From the GC3Pie project: https://code.google.com/p/gc3pie/
+
+A backport of the Python standard `collections` package, providing
+`namedtuple` and `defaultdict` also on Python 2.4 and 2.5.
+
+This package actually imports your Python `collections`, and adds
+its own versions of `namedtuple` and `defaultdict` only if they are
+missing.
+"""
+
+from collections import *
+import sys
+
+try:
+ defaultdict
+except NameError:
+ class defaultdict(dict):
+ """
+ A backport of `defaultdict` to Python 2.4
+ See http://docs.python.org/library/collections.html
+ """
+ def __new__(cls, default_factory=None):
+ return dict.__new__(cls)
+
+ def __init__(self, default_factory):
+ self.default_factory = default_factory
+
+ def __missing__(self, key):
+ try:
+ return self.default_factory()
+ except:
+ raise KeyError("Key '%s' not in dictionary" % key)
+
+ def __getitem__(self, key):
+ if not dict.__contains__(self, key):
+ dict.__setitem__(self, key, self.__missing__(key))
+ return dict.__getitem__(self, key)
+
+
+try:
+ namedtuple
+except NameError:
+ # Use Raymond Hettinger's original `namedtuple` package.
+ #
+ # Source originally taken from:
+ # http://code.activestate.com/recipes/500261-named-tuples/
+ from operator import itemgetter as _itemgetter
+ from keyword import iskeyword as _iskeyword
+ import sys as _sys
+
+ def namedtuple(typename, field_names, verbose=False, rename=False):
+ """Returns a new subclass of tuple with named fields.
+
+ >>> Point = namedtuple('Point', 'x y')
+ >>> Point.__doc__ # docstring for the new class
+ 'Point(x, y)'
+ >>> p = Point(11, y=22) # instantiate with positional args or keywords
+ >>> p[0] + p[1] # indexable like a plain tuple
+ 33
+ >>> x, y = p # unpack like a regular tuple
+ >>> x, y
+ (11, 22)
+ >>> p.x + p.y # fields also accessible by name
+ 33
+ >>> d = p._asdict() # convert to a dictionary
+ >>> d['x']
+ 11
+ >>> Point(**d) # convert from a dictionary
+ Point(x=11, y=22)
+ >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields
+ Point(x=100, y=22)
+
+ """
+
+ # Parse and validate the field names. Validation serves two purposes,
+ # generating informative error messages and preventing template injection attacks.
+ if isinstance(field_names, basestring):
+ field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas
+ field_names = tuple(map(str, field_names))
+ if rename:
+ names = list(field_names)
+ seen = set()
+ for i, name in enumerate(names):
+ if (not min(c.isalnum() or c == '_' for c in name)
+ or _iskeyword(name)
+ or not name or name[0].isdigit()
+ or name.startswith('_')
+ or name in seen):
+ names[i] = '_%d' % i
+
+ seen.add(name)
+ field_names = tuple(names)
+ for name in (typename,) + field_names:
+ if not min(c.isalnum() or c == '_' for c in name):
+ raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name)
+ if _iskeyword(name):
+ raise ValueError('Type names and field names cannot be a keyword: %r' % name)
+ if name[0].isdigit():
+ raise ValueError('Type names and field names cannot start with a number: %r' % name)
+ seen_names = set()
+ for name in field_names:
+ if name.startswith('_') and not rename:
+ raise ValueError('Field names cannot start with an underscore: %r' % name)
+ if name in seen_names:
+ raise ValueError('Encountered duplicate field name: %r' % name)
+ seen_names.add(name)
+
+ # Create and fill-in the class template
+ numfields = len(field_names)
+ argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes
+ reprtxt = ', '.join('%s=%%r' % name for name in field_names)
+ template = '''class %(typename)s(tuple):
+ '%(typename)s(%(argtxt)s)' \n
+ __slots__ = () \n
+ _fields = %(field_names)r \n
+ def __new__(_cls, %(argtxt)s):
+ return _tuple.__new__(_cls, (%(argtxt)s)) \n
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new %(typename)s object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != %(numfields)d:
+ raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result))
+ return result \n
+ def __repr__(self):
+ return '%(typename)s(%(reprtxt)s)' %% self \n
+ def _asdict(self):
+ 'Return a new dict which maps field names to their values'
+ return dict(zip(self._fields, self)) \n
+ def _replace(_self, **kwds):
+ 'Return a new %(typename)s object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, %(field_names)r, _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %%r' %% kwds.keys())
+ return result \n
+ def __getnewargs__(self):
+ return tuple(self) \n\n''' % locals()
+ for i, name in enumerate(field_names):
+ template += ' %s = _property(_itemgetter(%d))\n' % (name, i)
+ if verbose:
+ print(template)
+
+ # Execute the template string in a temporary namespace
+ namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
+ _property=property, _tuple=tuple)
+ try:
+ exec(template) in namespace
+ except SyntaxError:
+ e = sys.exc_info()[1]
+ raise SyntaxError(str(e) + ':\n' + template)
+ result = namespace[typename]
+
+ # For pickling to work, the __module__ variable needs to be set to the frame
+ # where the named tuple is created. Bypass this step in environments where
+ # sys._getframe is not defined (Jython for example) or sys._getframe is not
+ # defined for arguments greater than 0 (IronPython).
+ try:
+ result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__')
+ except (AttributeError, ValueError):
+ pass
+
+ return result
+
+
+if __name__ == '__main__':
+ # verify that instances can be pickled
+ from cPickle import loads, dumps
+ Point = namedtuple('Point', 'x, y', True)
+ p = Point(x=10, y=20)
+ assert p == loads(dumps(p, -1))
+
+ # test and demonstrate ability to override methods
+ class Point(namedtuple('Point', 'x y')):
+ @property
+ def hypot(self):
+ return (self.x ** 2 + self.y ** 2) ** 0.5
+
+ def __str__(self):
+ return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot)
+
+ for p in Point(3, 4), Point(14, 5), Point(9. / 7, 6):
+ print(p)
+
+ class Point(namedtuple('Point', 'x y')):
+ 'Point class with optimized _make() and _replace() without error-checking'
+ _make = classmethod(tuple.__new__)
+
+ def _replace(self, _map=map, **kwds):
+ return self._make(_map(kwds.get, ('x', 'y'), self))
+
+ print(Point(11, 22)._replace(x=100))
+
+ import doctest
+ TestResults = namedtuple('TestResults', 'failed attempted')
+ print(TestResults(*doctest.testmod()))
diff --git a/xlsxwriter/compatibility.py b/xlsxwriter/compatibility.py
new file mode 100644
index 0000000..daefba8
--- /dev/null
+++ b/xlsxwriter/compatibility.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Python 2/3 compatibility functions for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import sys
+from decimal import Decimal
+
+try:
+ # For compatibility between Python 2 and 3.
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+try:
+ # For Python 2.6+.
+ from fractions import Fraction
+except ImportError:
+ Fraction = float
+
+try:
+ # For Python 2.6+.
+ from collections import defaultdict
+ from collections import namedtuple
+except ImportError:
+ # For Python 2.5 support.
+ from .compat_collections import defaultdict
+ from .compat_collections import namedtuple
+
+# Types to check in Python 2/3.
+if sys.version_info[0] == 2:
+ int_types = (int, long)
+ num_types = (float, int, long, Decimal, Fraction)
+ str_types = basestring
+else:
+ int_types = (int)
+ num_types = (float, int, Decimal, Fraction)
+ str_types = str
+
+
+if sys.version_info < (2, 6, 0):
+ from StringIO import StringIO as BytesIO
+else:
+ from io import BytesIO as BytesIO
+
+
+def force_unicode(string):
+ """Return string as a native string"""
+ if sys.version_info[0] == 2:
+ if isinstance(string, unicode):
+ return string.encode('utf-8')
+ return string
diff --git a/xlsxwriter/contenttypes.py b/xlsxwriter/contenttypes.py
new file mode 100644
index 0000000..2c9a0af
--- /dev/null
+++ b/xlsxwriter/contenttypes.py
@@ -0,0 +1,202 @@
+###############################################################################
+#
+# ContentTypes - A class for writing the Excel XLSX ContentTypes file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import copy
+from . import xmlwriter
+
+# Long namespace strings used in the class.
+app_package = 'application/vnd.openxmlformats-package.'
+app_document = 'application/vnd.openxmlformats-officedocument.'
+
+defaults = [
+ ['rels', app_package + 'relationships+xml'],
+ ['xml', 'application/xml'],
+]
+
+overrides = [
+ ['/docProps/app.xml', app_document + 'extended-properties+xml'],
+ ['/docProps/core.xml', app_package + 'core-properties+xml'],
+ ['/xl/styles.xml', app_document + 'spreadsheetml.styles+xml'],
+ ['/xl/theme/theme1.xml', app_document + 'theme+xml'],
+ ['/xl/workbook.xml', app_document + 'spreadsheetml.sheet.main+xml'],
+]
+
+
+class ContentTypes(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX ContentTypes file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(ContentTypes, self).__init__()
+
+ # Copy the defaults in case we need to change them.
+ self.defaults = copy.deepcopy(defaults)
+ self.overrides = copy.deepcopy(overrides)
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ self._write_types()
+ self._write_defaults()
+ self._write_overrides()
+
+ self._xml_end_tag('Types')
+
+ # Close the file.
+ self._xml_close()
+
+ def _add_default(self, default):
+ # Add elements to the ContentTypes defaults.
+ self.defaults.append(default)
+
+ def _add_override(self, override):
+ # Add elements to the ContentTypes overrides.
+ self.overrides.append(override)
+
+ def _add_worksheet_name(self, worksheet_name):
+ # Add the name of a worksheet to the ContentTypes overrides.
+ worksheet_name = "/xl/worksheets/" + worksheet_name + ".xml"
+
+ self._add_override((worksheet_name,
+ app_document + 'spreadsheetml.worksheet+xml'))
+
+ def _add_chartsheet_name(self, chartsheet_name):
+ # Add the name of a chartsheet to the ContentTypes overrides.
+ chartsheet_name = "/xl/chartsheets/" + chartsheet_name + ".xml"
+
+ self._add_override((chartsheet_name,
+ app_document + 'spreadsheetml.chartsheet+xml'))
+
+ def _add_chart_name(self, chart_name):
+ # Add the name of a chart to the ContentTypes overrides.
+ chart_name = "/xl/charts/" + chart_name + ".xml"
+
+ self._add_override((chart_name, app_document + 'drawingml.chart+xml'))
+
+ def _add_drawing_name(self, drawing_name):
+ # Add the name of a drawing to the ContentTypes overrides.
+ drawing_name = "/xl/drawings/" + drawing_name + ".xml"
+
+ self._add_override((drawing_name, app_document + 'drawing+xml'))
+
+ def _add_vml_name(self):
+ # Add the name of a VML drawing to the ContentTypes defaults.
+ self._add_default(('vml', app_document + 'vmlDrawing'))
+
+ def _add_comment_name(self, comment_name):
+ # Add the name of a comment to the ContentTypes overrides.
+ comment_name = "/xl/" + comment_name + ".xml"
+
+ self._add_override((comment_name,
+ app_document + 'spreadsheetml.comments+xml'))
+
+ def _add_shared_strings(self):
+ # Add the sharedStrings link to the ContentTypes overrides.
+ self._add_override(('/xl/sharedStrings.xml',
+ app_document + 'spreadsheetml.sharedStrings+xml'))
+
+ def _add_calc_chain(self):
+ # Add the calcChain link to the ContentTypes overrides.
+ self._add_override(('/xl/calcChain.xml',
+ app_document + 'spreadsheetml.calcChain+xml'))
+
+ def _add_image_types(self, image_types):
+ # Add the image default types.
+ for image_type in image_types:
+ self._add_default((image_type, 'image/' + image_type))
+
+ def _add_table_name(self, table_name):
+ # Add the name of a table to the ContentTypes overrides.
+ table_name = "/xl/tables/" + table_name + ".xml"
+
+ self._add_override((table_name,
+ app_document + 'spreadsheetml.table+xml'))
+
+ def _add_vba_project(self):
+ # Add a vbaProject to the ContentTypes defaults.
+
+ # Change the workbook.xml content-type from xlsx to xlsm.
+ for i, override in enumerate(self.overrides):
+ if override[0] == '/xl/workbook.xml':
+ self.overrides[i][1] = 'application/vnd.ms-excel.' \
+ 'sheet.macroEnabled.main+xml'
+
+ self._add_default(('bin', 'application/vnd.ms-office.vbaProject'))
+
+ def _add_custom_properties(self):
+ # Add the custom properties to the ContentTypes overrides.
+ self._add_override(('/docProps/custom.xml',
+ app_document + 'custom-properties+xml'))
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_defaults(self):
+ # Write out all of the <Default> types.
+
+ for extension, content_type in self.defaults:
+ self._xml_empty_tag('Default',
+ [('Extension', extension),
+ ('ContentType', content_type)])
+
+ def _write_overrides(self):
+ # Write out all of the <Override> types.
+ for part_name, content_type in self.overrides:
+ self._xml_empty_tag('Override',
+ [('PartName', part_name),
+ ('ContentType', content_type)])
+
+ def _write_types(self):
+ # Write the <Types> element.
+ xmlns = 'http://schemas.openxmlformats.org/package/2006/content-types'
+
+ attributes = [('xmlns', xmlns,)]
+ self._xml_start_tag('Types', attributes)
+
+ def _write_default(self, extension, content_type):
+ # Write the <Default> element.
+ attributes = [
+ ('Extension', extension),
+ ('ContentType', content_type),
+ ]
+
+ self._xml_empty_tag('Default', attributes)
+
+ def _write_override(self, part_name, content_type):
+ # Write the <Override> element.
+ attributes = [
+ ('PartName', part_name),
+ ('ContentType', content_type),
+ ]
+
+ self._xml_empty_tag('Override', attributes)
diff --git a/xlsxwriter/core.py b/xlsxwriter/core.py
new file mode 100644
index 0000000..34cc869
--- /dev/null
+++ b/xlsxwriter/core.py
@@ -0,0 +1,192 @@
+###############################################################################
+#
+# Core - A class for writing the Excel XLSX Worksheet file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Standard packages.
+from datetime import datetime
+
+# Package imports.
+from . import xmlwriter
+
+
+class Core(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX Core file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(Core, self).__init__()
+
+ self.properties = {}
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ self._write_cp_core_properties()
+ self._write_dc_title()
+ self._write_dc_subject()
+ self._write_dc_creator()
+ self._write_cp_keywords()
+ self._write_dc_description()
+ self._write_cp_last_modified_by()
+ self._write_dcterms_created()
+ self._write_dcterms_modified()
+ self._write_cp_category()
+ self._write_cp_content_status()
+
+ self._xml_end_tag('cp:coreProperties')
+
+ # Close the file.
+ self._xml_close()
+
+ def _set_properties(self, properties):
+ # Set the document properties.
+ self.properties = properties
+
+ def _datetime_to_iso8601_date(self, date):
+ # Convert to a ISO 8601 style "2010-01-01T00:00:00Z" date.
+ if not date:
+ date = datetime.utcnow()
+
+ return date.strftime("%Y-%m-%dT%H:%M:%SZ")
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_cp_core_properties(self):
+ # Write the <cp:coreProperties> element.
+
+ xmlns_cp = ('http://schemas.openxmlformats.org/package/2006/' +
+ 'metadata/core-properties')
+ xmlns_dc = 'http://purl.org/dc/elements/1.1/'
+ xmlns_dcterms = 'http://purl.org/dc/terms/'
+ xmlns_dcmitype = 'http://purl.org/dc/dcmitype/'
+ xmlns_xsi = 'http://www.w3.org/2001/XMLSchema-instance'
+
+ attributes = [
+ ('xmlns:cp', xmlns_cp),
+ ('xmlns:dc', xmlns_dc),
+ ('xmlns:dcterms', xmlns_dcterms),
+ ('xmlns:dcmitype', xmlns_dcmitype),
+ ('xmlns:xsi', xmlns_xsi),
+ ]
+
+ self._xml_start_tag('cp:coreProperties', attributes)
+
+ def _write_dc_creator(self):
+ # Write the <dc:creator> element.
+ data = self.properties.get('author', '')
+
+ self._xml_data_element('dc:creator', data)
+
+ def _write_cp_last_modified_by(self):
+ # Write the <cp:lastModifiedBy> element.
+ data = self.properties.get('author', '')
+
+ self._xml_data_element('cp:lastModifiedBy', data)
+
+ def _write_dcterms_created(self):
+ # Write the <dcterms:created> element.
+ date = self.properties.get('created', datetime.utcnow())
+
+ xsi_type = 'dcterms:W3CDTF'
+
+ date = self._datetime_to_iso8601_date(date)
+
+ attributes = [('xsi:type', xsi_type,)]
+
+ self._xml_data_element('dcterms:created', date, attributes)
+
+ def _write_dcterms_modified(self):
+ # Write the <dcterms:modified> element.
+ date = self.properties.get('created', datetime.utcnow())
+
+ xsi_type = 'dcterms:W3CDTF'
+
+ date = self._datetime_to_iso8601_date(date)
+
+ attributes = [('xsi:type', xsi_type,)]
+
+ self._xml_data_element('dcterms:modified', date, attributes)
+
+ def _write_dc_title(self):
+ # Write the <dc:title> element.
+ if 'title' in self.properties:
+ data = self.properties['title']
+ else:
+ return
+
+ self._xml_data_element('dc:title', data)
+
+ def _write_dc_subject(self):
+ # Write the <dc:subject> element.
+ if 'subject' in self.properties:
+ data = self.properties['subject']
+ else:
+ return
+
+ self._xml_data_element('dc:subject', data)
+
+ def _write_cp_keywords(self):
+ # Write the <cp:keywords> element.
+ if 'keywords' in self.properties:
+ data = self.properties['keywords']
+ else:
+ return
+
+ self._xml_data_element('cp:keywords', data)
+
+ def _write_dc_description(self):
+ # Write the <dc:description> element.
+ if 'comments' in self.properties:
+ data = self.properties['comments']
+ else:
+ return
+
+ self._xml_data_element('dc:description', data)
+
+ def _write_cp_category(self):
+ # Write the <cp:category> element.
+ if 'category' in self.properties:
+ data = self.properties['category']
+ else:
+ return
+
+ self._xml_data_element('cp:category', data)
+
+ def _write_cp_content_status(self):
+ # Write the <cp:contentStatus> element.
+ if 'status' in self.properties:
+ data = self.properties['status']
+ else:
+ return
+
+ self._xml_data_element('cp:contentStatus', data)
diff --git a/xlsxwriter/custom.py b/xlsxwriter/custom.py
new file mode 100644
index 0000000..9effc92
--- /dev/null
+++ b/xlsxwriter/custom.py
@@ -0,0 +1,140 @@
+###############################################################################
+#
+# Custom - A class for writing the Excel XLSX Custom Property file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Package imports.
+from . import xmlwriter
+
+
+class Custom(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX Custom Workbook Property file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(Custom, self).__init__()
+
+ self.properties = []
+ self.pid = 1
+
+ def _set_properties(self, properties):
+ # Set the document properties.
+ self.properties = properties
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ self._write_properties()
+
+ self._xml_end_tag('Properties')
+
+ # Close the file.
+ self._xml_close()
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_properties(self):
+ # Write the <Properties> element.
+ schema = 'http://schemas.openxmlformats.org/officeDocument/2006/'
+ xmlns = schema + 'custom-properties'
+ xmlns_vt = schema + 'docPropsVTypes'
+
+ attributes = [
+ ('xmlns', xmlns),
+ ('xmlns:vt', xmlns_vt),
+ ]
+
+ self._xml_start_tag('Properties', attributes)
+
+ for custom_property in self.properties:
+ # Write the property element.
+ self._write_property(custom_property)
+
+ def _write_property(self, custom_property):
+ # Write the <property> element.
+
+ fmtid = '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}'
+
+ name, value, property_type = custom_property
+ self.pid += 1
+
+ attributes = [
+ ('fmtid', fmtid),
+ ('pid', self.pid),
+ ('name', name),
+ ]
+
+ self._xml_start_tag('property', attributes)
+
+ if property_type == 'number_int':
+ # Write the vt:i4 element.
+ self._write_vt_i4(value)
+ elif property_type == 'number':
+ # Write the vt:r8 element.
+ self._write_vt_r8(value)
+ elif property_type == 'date':
+ # Write the vt:filetime element.
+ self._write_vt_filetime(value)
+ elif property_type == 'bool':
+ # Write the vt:bool element.
+ self._write_vt_bool(value)
+ else:
+ # Write the vt:lpwstr element.
+ self._write_vt_lpwstr(value)
+
+ self._xml_end_tag('property')
+
+ def _write_vt_lpwstr(self, value):
+ # Write the <vt:lpwstr> element.
+ self._xml_data_element('vt:lpwstr', value)
+
+ def _write_vt_filetime(self, value):
+ # Write the <vt:filetime> element.
+ self._xml_data_element('vt:filetime', value)
+
+ def _write_vt_i4(self, value):
+ # Write the <vt:i4> element.
+ self._xml_data_element('vt:i4', value)
+
+ def _write_vt_r8(self, value):
+ # Write the <vt:r8> element.
+ self._xml_data_element('vt:r8', value)
+
+ def _write_vt_bool(self, value):
+ # Write the <vt:bool> element.
+
+ if value:
+ value = 'true'
+ else:
+ value = 'false'
+
+ self._xml_data_element('vt:bool', value)
diff --git a/xlsxwriter/drawing.py b/xlsxwriter/drawing.py
new file mode 100644
index 0000000..c9fb998
--- /dev/null
+++ b/xlsxwriter/drawing.py
@@ -0,0 +1,1091 @@
+###############################################################################
+#
+# Drawing - A class for writing the Excel XLSX Drawing file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from . import xmlwriter
+from .shape import Shape
+from .utility import get_rgb_color
+
+
+class Drawing(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX Drawing file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(Drawing, self).__init__()
+
+ self.drawings = []
+ self.embedded = 0
+ self.orientation = 0
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ # Write the xdr:wsDr element.
+ self._write_drawing_workspace()
+
+ if self.embedded:
+ index = 1
+ for drawing in self.drawings:
+ # Write the xdr:twoCellAnchor element.
+ self._write_two_cell_anchor(index, drawing)
+ index += 1
+
+ if drawing['url']:
+ index += 1
+
+ else:
+ # Write the xdr:absoluteAnchor element.
+ self._write_absolute_anchor(1)
+
+ self._xml_end_tag('xdr:wsDr')
+
+ # Close the file.
+ self._xml_close()
+
+ def _add_drawing_object(self, drawing_object):
+ # Add a chart, image or shape sub object to the drawing.
+
+ obj = {
+ 'anchor_type': drawing_object[0],
+ 'col_from': drawing_object[1],
+ 'row_from': drawing_object[2],
+ 'col_from_offset': drawing_object[3],
+ 'row_from_offset': drawing_object[4],
+ 'col_to': drawing_object[5],
+ 'row_to': drawing_object[6],
+ 'col_to_offset': drawing_object[7],
+ 'row_to_offset': drawing_object[8],
+ 'col_absolute': drawing_object[9],
+ 'row_absolute': drawing_object[10],
+ 'width': drawing_object[11],
+ 'height': drawing_object[12],
+ 'description': drawing_object[13],
+ 'shape': drawing_object[14],
+ 'url': None,
+ 'tip': None,
+ 'anchor': None
+ }
+
+ if len(drawing_object) > 15:
+ obj['url'] = drawing_object[15]
+ obj['tip'] = drawing_object[16]
+ obj['anchor'] = drawing_object[17]
+
+ self.drawings.append(obj)
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_drawing_workspace(self):
+ # Write the <xdr:wsDr> element.
+ schema = 'http://schemas.openxmlformats.org/drawingml/'
+ xmlns_xdr = schema + '2006/spreadsheetDrawing'
+ xmlns_a = schema + '2006/main'
+
+ attributes = [
+ ('xmlns:xdr', xmlns_xdr),
+ ('xmlns:a', xmlns_a),
+ ]
+
+ self._xml_start_tag('xdr:wsDr', attributes)
+
+ def _write_two_cell_anchor(self, index, drawing):
+ # Write the <xdr:twoCellAnchor> element.
+ shape = drawing['shape']
+
+ options = {
+ 'description': drawing['description'],
+ 'url': drawing['url'],
+ 'tip': drawing['tip']
+ }
+
+ attributes = []
+
+ # Add attribute for images.
+ if drawing['anchor_type'] == 2:
+ if drawing['anchor'] == 3:
+ attributes.append(('editAs', 'absolute'))
+ elif drawing['anchor'] == 1:
+ pass
+ else:
+ attributes.append(('editAs', 'oneCell'))
+
+ # Add editAs attribute for shapes.
+ if shape and shape.edit_as:
+ attributes.append(('editAs', shape.edit_as))
+
+ self._xml_start_tag('xdr:twoCellAnchor', attributes)
+
+ # Write the xdr:from element.
+ self._write_from(
+ drawing['col_from'],
+ drawing['row_from'],
+ drawing['col_from_offset'],
+ drawing['row_from_offset'])
+
+ # Write the xdr:from element.
+ self._write_to(
+ drawing['col_to'],
+ drawing['row_to'],
+ drawing['col_to_offset'],
+ drawing['row_to_offset'])
+
+ if drawing['anchor_type'] == 1:
+ # Graphic frame.
+ # Write the xdr:graphicFrame element for charts.
+ self._write_graphic_frame(index, drawing['description'])
+ elif drawing['anchor_type'] == 2:
+ # Write the xdr:pic element.
+ self._write_pic(index,
+ drawing['col_absolute'],
+ drawing['row_absolute'],
+ drawing['width'],
+ drawing['height'],
+ shape,
+ options)
+ else:
+ # Write the xdr:sp element for shapes.
+ self._write_sp(index,
+ drawing['col_absolute'],
+ drawing['row_absolute'],
+ drawing['width'],
+ drawing['height'],
+ shape)
+
+ # Write the xdr:clientData element.
+ self._write_client_data()
+
+ self._xml_end_tag('xdr:twoCellAnchor')
+
+ def _write_absolute_anchor(self, frame_index):
+ self._xml_start_tag('xdr:absoluteAnchor')
+ # Write the <xdr:absoluteAnchor> element.
+
+ # Different co-ordinates for horizontal (= 0) and vertical (= 1).
+ if self.orientation == 0:
+ # Write the xdr:pos element.
+ self._write_pos(0, 0)
+
+ # Write the xdr:ext element.
+ self._write_ext(9308969, 6078325)
+
+ else:
+ # Write the xdr:pos element.
+ self._write_pos(0, -47625)
+
+ # Write the xdr:ext element.
+ self._write_ext(6162675, 6124575)
+
+ # Write the xdr:graphicFrame element.
+ self._write_graphic_frame(frame_index)
+
+ # Write the xdr:clientData element.
+ self._write_client_data()
+
+ self._xml_end_tag('xdr:absoluteAnchor')
+
+ def _write_from(self, col, row, col_offset, row_offset):
+ # Write the <xdr:from> element.
+ self._xml_start_tag('xdr:from')
+
+ # Write the xdr:col element.
+ self._write_col(col)
+
+ # Write the xdr:colOff element.
+ self._write_col_off(col_offset)
+
+ # Write the xdr:row element.
+ self._write_row(row)
+
+ # Write the xdr:rowOff element.
+ self._write_row_off(row_offset)
+
+ self._xml_end_tag('xdr:from')
+
+ def _write_to(self, col, row, col_offset, row_offset):
+ # Write the <xdr:to> element.
+ self._xml_start_tag('xdr:to')
+
+ # Write the xdr:col element.
+ self._write_col(col)
+
+ # Write the xdr:colOff element.
+ self._write_col_off(col_offset)
+
+ # Write the xdr:row element.
+ self._write_row(row)
+
+ # Write the xdr:rowOff element.
+ self._write_row_off(row_offset)
+
+ self._xml_end_tag('xdr:to')
+
+ def _write_col(self, data):
+ # Write the <xdr:col> element.
+ self._xml_data_element('xdr:col', data)
+
+ def _write_col_off(self, data):
+ # Write the <xdr:colOff> element.
+ self._xml_data_element('xdr:colOff', data)
+
+ def _write_row(self, data):
+ # Write the <xdr:row> element.
+ self._xml_data_element('xdr:row', data)
+
+ def _write_row_off(self, data):
+ # Write the <xdr:rowOff> element.
+ self._xml_data_element('xdr:rowOff', data)
+
+ def _write_pos(self, x, y):
+ # Write the <xdr:pos> element.
+
+ attributes = [('x', x), ('y', y)]
+
+ self._xml_empty_tag('xdr:pos', attributes)
+
+ def _write_ext(self, cx, cy):
+ # Write the <xdr:ext> element.
+
+ attributes = [('cx', cx), ('cy', cy)]
+
+ self._xml_empty_tag('xdr:ext', attributes)
+
+ def _write_graphic_frame(self, index, name=None):
+ # Write the <xdr:graphicFrame> element.
+ attributes = [('macro', '')]
+
+ self._xml_start_tag('xdr:graphicFrame', attributes)
+
+ # Write the xdr:nvGraphicFramePr element.
+ self._write_nv_graphic_frame_pr(index, name)
+
+ # Write the xdr:xfrm element.
+ self._write_xfrm()
+
+ # Write the a:graphic element.
+ self._write_atag_graphic(index)
+
+ self._xml_end_tag('xdr:graphicFrame')
+
+ def _write_nv_graphic_frame_pr(self, index, name):
+ # Write the <xdr:nvGraphicFramePr> element.
+
+ if not name:
+ name = 'Chart ' + str(index)
+
+ self._xml_start_tag('xdr:nvGraphicFramePr')
+
+ # Write the xdr:cNvPr element.
+ self._write_c_nv_pr(index + 1, name)
+
+ # Write the xdr:cNvGraphicFramePr element.
+ self._write_c_nv_graphic_frame_pr()
+
+ self._xml_end_tag('xdr:nvGraphicFramePr')
+
+ def _write_c_nv_pr(self, index, name, options={}):
+ # Write the <xdr:cNvPr> element.
+ descr = options.get('description', None)
+ url = options.get('url', None)
+ tip = options.get('tip', None)
+
+ attributes = [('id', index), ('name', name)]
+
+ # Add description attribute for images.
+ if descr is not None:
+ attributes.append(('descr', descr))
+
+ if url:
+ self._xml_start_tag('xdr:cNvPr', attributes)
+ schema = "http://schemas.openxmlformats.org"
+ att = [
+ ('xmlns:r', schema + "/officeDocument/2006/relationships"),
+ ('r:id', "rId" + str(index - 1))
+ ]
+
+ if tip:
+ att.append(('tooltip', tip))
+
+ self._xml_empty_tag('a:hlinkClick', att)
+ self._xml_end_tag('xdr:cNvPr')
+ else:
+ self._xml_empty_tag('xdr:cNvPr', attributes)
+
+ def _write_c_nv_graphic_frame_pr(self):
+ # Write the <xdr:cNvGraphicFramePr> element.
+ if self.embedded:
+ self._xml_empty_tag('xdr:cNvGraphicFramePr')
+ else:
+ self._xml_start_tag('xdr:cNvGraphicFramePr')
+
+ # Write the a:graphicFrameLocks element.
+ self._write_a_graphic_frame_locks()
+
+ self._xml_end_tag('xdr:cNvGraphicFramePr')
+
+ def _write_a_graphic_frame_locks(self):
+ # Write the <a:graphicFrameLocks> element.
+ attributes = [('noGrp', 1)]
+
+ self._xml_empty_tag('a:graphicFrameLocks', attributes)
+
+ def _write_xfrm(self):
+ # Write the <xdr:xfrm> element.
+ self._xml_start_tag('xdr:xfrm')
+
+ # Write the xfrmOffset element.
+ self._write_xfrm_offset()
+
+ # Write the xfrmOffset element.
+ self._write_xfrm_extension()
+
+ self._xml_end_tag('xdr:xfrm')
+
+ def _write_xfrm_offset(self):
+ # Write the <a:off> xfrm sub-element.
+
+ attributes = [
+ ('x', 0),
+ ('y', 0),
+ ]
+
+ self._xml_empty_tag('a:off', attributes)
+
+ def _write_xfrm_extension(self):
+ # Write the <a:ext> xfrm sub-element.
+
+ attributes = [
+ ('cx', 0),
+ ('cy', 0),
+ ]
+
+ self._xml_empty_tag('a:ext', attributes)
+
+ def _write_atag_graphic(self, index):
+ # Write the <a:graphic> element.
+ self._xml_start_tag('a:graphic')
+
+ # Write the a:graphicData element.
+ self._write_atag_graphic_data(index)
+
+ self._xml_end_tag('a:graphic')
+
+ def _write_atag_graphic_data(self, index):
+ # Write the <a:graphicData> element.
+ uri = 'http://schemas.openxmlformats.org/drawingml/2006/chart'
+
+ attributes = [('uri', uri,)]
+
+ self._xml_start_tag('a:graphicData', attributes)
+
+ # Write the c:chart element.
+ self._write_c_chart('rId' + str(index))
+
+ self._xml_end_tag('a:graphicData')
+
+ def _write_c_chart(self, r_id):
+ # Write the <c:chart> element.
+
+ schema = 'http://schemas.openxmlformats.org/'
+ xmlns_c = schema + 'drawingml/2006/chart'
+ xmlns_r = schema + 'officeDocument/2006/relationships'
+
+ attributes = [
+ ('xmlns:c', xmlns_c),
+ ('xmlns:r', xmlns_r),
+ ('r:id', r_id),
+ ]
+
+ self._xml_empty_tag('c:chart', attributes)
+
+ def _write_client_data(self):
+ # Write the <xdr:clientData> element.
+ self._xml_empty_tag('xdr:clientData')
+
+ def _write_sp(self, index, col_absolute, row_absolute,
+ width, height, shape):
+ # Write the <xdr:sp> element.
+
+ if shape and shape.connect:
+ attributes = [('macro', '')]
+ self._xml_start_tag('xdr:cxnSp', attributes)
+
+ # Write the xdr:nvCxnSpPr element.
+ self._write_nv_cxn_sp_pr(index, shape)
+
+ # Write the xdr:spPr element.
+ self._write_xdr_sp_pr(index, col_absolute, row_absolute, width,
+ height, shape)
+
+ self._xml_end_tag('xdr:cxnSp')
+ else:
+ # Add attribute for shapes.
+ attributes = [('macro', ''),
+ ('textlink', '')]
+
+ self._xml_start_tag('xdr:sp', attributes)
+
+ # Write the xdr:nvSpPr element.
+ self._write_nv_sp_pr(index, shape)
+
+ # Write the xdr:spPr element.
+ self._write_xdr_sp_pr(index, col_absolute, row_absolute, width,
+ height, shape)
+
+ # Write the xdr:style element.
+ self._write_style()
+
+ # Write the xdr:txBody element.
+ if shape.text is not None:
+ self._write_tx_body(col_absolute, row_absolute, width, height,
+ shape)
+
+ self._xml_end_tag('xdr:sp')
+
+ def _write_nv_cxn_sp_pr(self, index, shape):
+ # Write the <xdr:nvCxnSpPr> element.
+ self._xml_start_tag('xdr:nvCxnSpPr')
+
+ name = shape.name + ' ' + str(index)
+ if name is not None:
+ self._write_c_nv_pr(index, name)
+
+ self._xml_start_tag('xdr:cNvCxnSpPr')
+
+ attributes = [('noChangeShapeType', '1')]
+ self._xml_empty_tag('a:cxnSpLocks', attributes)
+
+ if shape.start:
+ attributes = [('id', shape.start), ('idx', shape.start_index)]
+ self._xml_empty_tag('a:stCxn', attributes)
+
+ if shape.end:
+ attributes = [('id', shape.end), ('idx', shape.end_index)]
+ self._xml_empty_tag('a:endCxn', attributes)
+
+ self._xml_end_tag('xdr:cNvCxnSpPr')
+ self._xml_end_tag('xdr:nvCxnSpPr')
+
+ def _write_nv_sp_pr(self, index, shape):
+ # Write the <xdr:NvSpPr> element.
+ attributes = []
+
+ self._xml_start_tag('xdr:nvSpPr')
+
+ name = shape.name + ' ' + str(index)
+
+ self._write_c_nv_pr(index + 1, name)
+
+ if shape.name == 'TextBox':
+ attributes = [('txBox', 1)]
+
+ self._xml_empty_tag('xdr:cNvSpPr', attributes)
+
+ # attributes = [('noChangeArrowheads', '1')]
+ # self._xml_empty_tag('a:spLocks', attributes)
+ # self._xml_end_tag('xdr:cNvSpPr')
+
+ self._xml_end_tag('xdr:nvSpPr')
+
+ def _write_pic(self, index, col_absolute, row_absolute,
+ width, height, shape, options):
+ # Write the <xdr:pic> element.
+ self._xml_start_tag('xdr:pic')
+
+ # Write the xdr:nvPicPr element.
+ self._write_nv_pic_pr(index, options)
+
+ # Write the xdr:blipFill element.
+ if options.get('url', None):
+ index = index + 1
+
+ self._write_blip_fill(index)
+
+ # Write the xdr:spPr element.
+ self._write_sp_pr(col_absolute, row_absolute, width, height,
+ shape)
+
+ self._xml_end_tag('xdr:pic')
+
+ def _write_nv_pic_pr(self, index, options):
+ # Write the <xdr:nvPicPr> element.
+ self._xml_start_tag('xdr:nvPicPr')
+
+ # Write the xdr:cNvPr element.
+ self._write_c_nv_pr(index + 1, 'Picture ' + str(index), options)
+
+ # Write the xdr:cNvPicPr element.
+ self._write_c_nv_pic_pr()
+
+ self._xml_end_tag('xdr:nvPicPr')
+
+ def _write_c_nv_pic_pr(self):
+ # Write the <xdr:cNvPicPr> element.
+ self._xml_start_tag('xdr:cNvPicPr')
+
+ # Write the a:picLocks element.
+ self._write_a_pic_locks()
+
+ self._xml_end_tag('xdr:cNvPicPr')
+
+ def _write_a_pic_locks(self):
+ # Write the <a:picLocks> element.
+ attributes = [('noChangeAspect', 1)]
+
+ self._xml_empty_tag('a:picLocks', attributes)
+
+ def _write_blip_fill(self, index):
+ # Write the <xdr:blipFill> element.
+ self._xml_start_tag('xdr:blipFill')
+
+ # Write the a:blip element.
+ self._write_a_blip(index)
+
+ # Write the a:stretch element.
+ self._write_a_stretch()
+
+ self._xml_end_tag('xdr:blipFill')
+
+ def _write_a_blip(self, index):
+ # Write the <a:blip> element.
+ schema = 'http://schemas.openxmlformats.org/officeDocument/'
+ xmlns_r = schema + '2006/relationships'
+ r_embed = 'rId' + str(index)
+
+ attributes = [
+ ('xmlns:r', xmlns_r),
+ ('r:embed', r_embed)]
+
+ self._xml_empty_tag('a:blip', attributes)
+
+ def _write_a_stretch(self):
+ # Write the <a:stretch> element.
+ self._xml_start_tag('a:stretch')
+
+ # Write the a:fillRect element.
+ self._write_a_fill_rect()
+
+ self._xml_end_tag('a:stretch')
+
+ def _write_a_fill_rect(self):
+ # Write the <a:fillRect> element.
+ self._xml_empty_tag('a:fillRect')
+
+ def _write_sp_pr(self, col_absolute, row_absolute, width, height,
+ shape=None):
+ # Write the <xdr:spPr> element, for charts.
+
+ self._xml_start_tag('xdr:spPr')
+
+ # Write the a:xfrm element.
+ self._write_a_xfrm(col_absolute, row_absolute, width, height)
+
+ # Write the a:prstGeom element.
+ self._write_a_prst_geom(shape)
+
+ self._xml_end_tag('xdr:spPr')
+
+ def _write_xdr_sp_pr(self, index, col_absolute, row_absolute, width,
+ height, shape):
+ # Write the <xdr:spPr> element for shapes.
+
+ attributes = []
+ # attributes = [('bwMode', 'auto')]
+
+ self._xml_start_tag('xdr:spPr', attributes)
+
+ # Write the a:xfrm element.
+ self._write_a_xfrm(col_absolute, row_absolute, width, height, shape)
+
+ # Write the a:prstGeom element.
+ self._write_a_prst_geom(shape)
+
+ if shape.fill:
+ if not shape.fill['defined']:
+ # Write the a:solidFill element.
+ self._write_a_solid_fill_scheme('lt1')
+ elif 'none' in shape.fill:
+ # Write the a:noFill element.
+ self._xml_empty_tag('a:noFill')
+ elif 'color' in shape.fill:
+ # Write the a:solidFill element.
+ self._write_a_solid_fill(get_rgb_color(shape.fill['color']))
+
+ if shape.gradient:
+ # Write the a:gradFill element.
+ self._write_a_grad_fill(shape.gradient)
+
+ # Write the a:ln element.
+ self._write_a_ln(shape.line)
+
+ self._xml_end_tag('xdr:spPr')
+
+ def _write_a_xfrm(self, col_absolute, row_absolute, width, height,
+ shape=None):
+ # Write the <a:xfrm> element.
+ attributes = []
+
+ if shape:
+ if shape.rotation:
+ rotation = shape.rotation
+ rotation *= 60000
+ attributes.append(('rot', rotation))
+
+ if shape.flip_h:
+ attributes.append(('flipH', 1))
+ if shape.flip_v:
+ attributes.append(('flipV', 1))
+
+ self._xml_start_tag('a:xfrm', attributes)
+
+ # Write the a:off element.
+ self._write_a_off(col_absolute, row_absolute)
+
+ # Write the a:ext element.
+ self._write_a_ext(width, height)
+
+ self._xml_end_tag('a:xfrm')
+
+ def _write_a_off(self, x, y):
+ # Write the <a:off> element.
+ attributes = [
+ ('x', x),
+ ('y', y),
+ ]
+
+ self._xml_empty_tag('a:off', attributes)
+
+ def _write_a_ext(self, cx, cy):
+ # Write the <a:ext> element.
+ attributes = [
+ ('cx', cx),
+ ('cy', cy),
+ ]
+
+ self._xml_empty_tag('a:ext', attributes)
+
+ def _write_a_prst_geom(self, shape=None):
+ # Write the <a:prstGeom> element.
+ attributes = [('prst', 'rect')]
+
+ self._xml_start_tag('a:prstGeom', attributes)
+
+ # Write the a:avLst element.
+ self._write_a_av_lst(shape)
+
+ self._xml_end_tag('a:prstGeom')
+
+ def _write_a_av_lst(self, shape=None):
+ # Write the <a:avLst> element.
+ adjustments = []
+
+ if shape and shape.adjustments:
+ adjustments = shape.adjustments
+
+ if adjustments:
+ self._xml_start_tag('a:avLst')
+
+ i = 0
+ for adj in adjustments:
+ i += 1
+ # Only connectors have multiple adjustments.
+ if shape.connect:
+ suffix = i
+ else:
+ suffix = ''
+
+ # Scale Adjustments: 100,000 = 100%.
+ adj_int = str(int(adj * 1000))
+
+ attributes = [('name', 'adj' + suffix),
+ ('fmla', 'val' + adj_int)]
+
+ self._xml_empty_tag('a:gd', attributes)
+
+ self._xml_end_tag('a:avLst')
+ else:
+ self._xml_empty_tag('a:avLst')
+
+ def _write_a_solid_fill(self, rgb):
+ # Write the <a:solidFill> element.
+ if rgb is None:
+ rgb = 'FFFFFF'
+
+ self._xml_start_tag('a:solidFill')
+
+ # Write the a:srgbClr element.
+ self._write_a_srgb_clr(rgb)
+
+ self._xml_end_tag('a:solidFill')
+
+ def _write_a_solid_fill_scheme(self, color, shade=None):
+
+ attributes = [('val', color)]
+
+ self._xml_start_tag('a:solidFill')
+
+ if shade:
+ self._xml_start_tag('a:schemeClr', attributes)
+ self._write_a_shade(shade)
+ self._xml_end_tag('a:schemeClr')
+ else:
+ self._xml_empty_tag('a:schemeClr', attributes)
+
+ self._xml_end_tag('a:solidFill')
+
+ def _write_a_ln(self, line):
+ # Write the <a:ln> element.
+ width = line.get('width', 0.75)
+
+ # Round width to nearest 0.25, like Excel.
+ width = int((width + 0.125) * 4) / 4.0
+
+ # Convert to internal units.
+ width = int(0.5 + (12700 * width))
+
+ attributes = [
+ ('w', width),
+ ('cmpd', 'sng')
+ ]
+
+ self._xml_start_tag('a:ln', attributes)
+
+ if 'none' in line:
+ # Write the a:noFill element.
+ self._xml_empty_tag('a:noFill')
+
+ elif 'color' in line:
+ # Write the a:solidFill element.
+ self._write_a_solid_fill(get_rgb_color(line['color']))
+
+ else:
+ # Write the a:solidFill element.
+ self._write_a_solid_fill_scheme('lt1', '50000')
+
+ # Write the line/dash type.
+ line_type = line.get('dash_type')
+ if line_type:
+ # Write the a:prstDash element.
+ self._write_a_prst_dash(line_type)
+
+ self._xml_end_tag('a:ln')
+
+ def _write_tx_body(self, col_absolute, row_absolute, width, height, shape):
+ # Write the <xdr:txBody> element.
+ attributes = [
+ ('wrap', "square"),
+ ('rtlCol', "0"),
+ ]
+
+ if not shape.align['defined']:
+ attributes.append(('anchor', 't'))
+ else:
+
+ if 'vertical' in shape.align:
+ align = shape.align['vertical']
+ if align == 'top':
+ attributes.append(('anchor', 't'))
+ elif align == 'middle':
+ attributes.append(('anchor', 'ctr'))
+ elif align == 'bottom':
+ attributes.append(('anchor', 'b'))
+ else:
+ attributes.append(('anchor', 't'))
+
+ if 'horizontal' in shape.align:
+ align = shape.align['horizontal']
+ if align == 'center':
+ attributes.append(('anchorCtr', '1'))
+ else:
+ attributes.append(('anchorCtr', '0'))
+
+ self._xml_start_tag('xdr:txBody')
+ self._xml_empty_tag('a:bodyPr', attributes)
+ self._xml_empty_tag('a:lstStyle')
+
+ lines = shape.text.split('\n')
+
+ # Set the font attributes.
+ font = shape.font
+ style_attrs = Shape._get_font_style_attributes(font)
+ latin_attrs = Shape._get_font_latin_attributes(font)
+ style_attrs.insert(0, ('lang', font['lang']))
+
+ for line in lines:
+ self._xml_start_tag('a:p')
+
+ if line == '':
+ self._write_font_run(font, style_attrs, latin_attrs,
+ 'a:endParaRPr')
+ self._xml_end_tag('a:p')
+ continue
+
+ self._xml_start_tag('a:r')
+
+ self._write_font_run(font, style_attrs, latin_attrs, 'a:rPr')
+
+ self._xml_data_element('a:t', line)
+
+ self._xml_end_tag('a:r')
+ self._xml_end_tag('a:p')
+
+ self._xml_end_tag('xdr:txBody')
+
+ def _write_font_run(self, font, style_attrs, latin_attrs, run_type):
+ # Write a:rPr or a:endParaRPr.
+ if font.get('color') is not None:
+ has_color = True
+ else:
+ has_color = False
+
+ if latin_attrs or has_color:
+ self._xml_start_tag(run_type, style_attrs)
+
+ if has_color:
+ self._write_a_solid_fill(get_rgb_color(font['color']))
+
+ if latin_attrs:
+ self._write_a_latin(latin_attrs)
+ self._write_a_cs(latin_attrs)
+
+ self._xml_end_tag(run_type)
+ else:
+ self._xml_empty_tag(run_type, style_attrs)
+
+ def _write_style(self):
+ # Write the <xdr:style> element.
+ self._xml_start_tag('xdr:style')
+
+ # Write the a:lnRef element.
+ self._write_a_ln_ref()
+
+ # Write the a:fillRef element.
+ self._write_a_fill_ref()
+
+ # Write the a:effectRef element.
+ self._write_a_effect_ref()
+
+ # Write the a:fontRef element.
+ self._write_a_font_ref()
+
+ self._xml_end_tag('xdr:style')
+
+ def _write_a_ln_ref(self):
+ # Write the <a:lnRef> element.
+ attributes = [('idx', '0')]
+
+ self._xml_start_tag('a:lnRef', attributes)
+
+ # Write the a:scrgbClr element.
+ self._write_a_scrgb_clr()
+
+ self._xml_end_tag('a:lnRef')
+
+ def _write_a_fill_ref(self):
+ # Write the <a:fillRef> element.
+ attributes = [('idx', '0')]
+
+ self._xml_start_tag('a:fillRef', attributes)
+
+ # Write the a:scrgbClr element.
+ self._write_a_scrgb_clr()
+
+ self._xml_end_tag('a:fillRef')
+
+ def _write_a_effect_ref(self):
+ # Write the <a:effectRef> element.
+ attributes = [('idx', '0')]
+
+ self._xml_start_tag('a:effectRef', attributes)
+
+ # Write the a:scrgbClr element.
+ self._write_a_scrgb_clr()
+
+ self._xml_end_tag('a:effectRef')
+
+ def _write_a_scrgb_clr(self):
+ # Write the <a:scrgbClr> element.
+
+ attributes = [
+ ('r', '0'),
+ ('g', '0'),
+ ('b', '0'),
+ ]
+
+ self._xml_empty_tag('a:scrgbClr', attributes)
+
+ def _write_a_font_ref(self):
+ # Write the <a:fontRef> element.
+ attributes = [('idx', 'minor')]
+
+ self._xml_start_tag('a:fontRef', attributes)
+
+ # Write the a:schemeClr element.
+ self._write_a_scheme_clr('dk1')
+
+ self._xml_end_tag('a:fontRef')
+
+ def _write_a_scheme_clr(self, val):
+ # Write the <a:schemeClr> element.
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('a:schemeClr', attributes)
+
+ def _write_a_shade(self, shade):
+ # Write the <a:shade> element.
+ attributes = [('val', shade)]
+
+ self._xml_empty_tag('a:shade', attributes)
+
+ def _write_a_prst_dash(self, val):
+ # Write the <a:prstDash> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('a:prstDash', attributes)
+
+ def _write_a_grad_fill(self, gradient):
+ # Write the <a:gradFill> element.
+
+ attributes = [('flip', 'none'), ('rotWithShape', '1')]
+
+ if gradient['type'] == 'linear':
+ attributes = []
+
+ self._xml_start_tag('a:gradFill', attributes)
+
+ # Write the a:gsLst element.
+ self._write_a_gs_lst(gradient)
+
+ if gradient['type'] == 'linear':
+ # Write the a:lin element.
+ self._write_a_lin(gradient['angle'])
+ else:
+ # Write the a:path element.
+ self._write_a_path(gradient['type'])
+
+ # Write the a:tileRect element.
+ self._write_a_tile_rect(gradient['type'])
+
+ self._xml_end_tag('a:gradFill')
+
+ def _write_a_gs_lst(self, gradient):
+ # Write the <a:gsLst> element.
+ positions = gradient['positions']
+ colors = gradient['colors']
+
+ self._xml_start_tag('a:gsLst')
+
+ for i in range(len(colors)):
+ pos = int(positions[i] * 1000)
+ attributes = [('pos', pos)]
+ self._xml_start_tag('a:gs', attributes)
+
+ # Write the a:srgbClr element.
+ # TODO: Wait for a feature request to support transparency.
+ color = get_rgb_color(colors[i])
+ self._write_a_srgb_clr(color)
+
+ self._xml_end_tag('a:gs')
+
+ self._xml_end_tag('a:gsLst')
+
+ def _write_a_lin(self, angle):
+ # Write the <a:lin> element.
+
+ angle = int(60000 * angle)
+
+ attributes = [
+ ('ang', angle),
+ ('scaled', '0'),
+ ]
+
+ self._xml_empty_tag('a:lin', attributes)
+
+ def _write_a_path(self, gradient_type):
+ # Write the <a:path> element.
+
+ attributes = [('path', gradient_type)]
+
+ self._xml_start_tag('a:path', attributes)
+
+ # Write the a:fillToRect element.
+ self._write_a_fill_to_rect(gradient_type)
+
+ self._xml_end_tag('a:path')
+
+ def _write_a_fill_to_rect(self, gradient_type):
+ # Write the <a:fillToRect> element.
+
+ if gradient_type == 'shape':
+ attributes = [
+ ('l', '50000'),
+ ('t', '50000'),
+ ('r', '50000'),
+ ('b', '50000'),
+ ]
+ else:
+ attributes = [
+ ('l', '100000'),
+ ('t', '100000'),
+ ]
+
+ self._xml_empty_tag('a:fillToRect', attributes)
+
+ def _write_a_tile_rect(self, gradient_type):
+ # Write the <a:tileRect> element.
+
+ if gradient_type == 'shape':
+ attributes = []
+ else:
+ attributes = [
+ ('r', '-100000'),
+ ('b', '-100000'),
+ ]
+
+ self._xml_empty_tag('a:tileRect', attributes)
+
+ def _write_a_srgb_clr(self, val):
+ # Write the <a:srgbClr> element.
+
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('a:srgbClr', attributes)
+
+ def _write_a_latin(self, attributes):
+ # Write the <a:latin> element.
+ self._xml_empty_tag('a:latin', attributes)
+
+ def _write_a_cs(self, attributes):
+ # Write the <a:latin> element.
+ self._xml_empty_tag('a:cs', attributes)
diff --git a/xlsxwriter/format.py b/xlsxwriter/format.py
new file mode 100644
index 0000000..7c5b708
--- /dev/null
+++ b/xlsxwriter/format.py
@@ -0,0 +1,991 @@
+###############################################################################
+#
+# Format - A class for writing the Excel XLSX Worksheet file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Package imports.
+from . import xmlwriter
+
+
+class Format(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX Format file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self, properties={}, xf_indices=None, dxf_indices=None):
+ """
+ Constructor.
+
+ """
+
+ super(Format, self).__init__()
+
+ self.xf_format_indices = xf_indices
+ self.dxf_format_indices = dxf_indices
+ self.xf_index = None
+ self.dxf_index = None
+
+ self.num_format = 0
+ self.num_format_index = 0
+ self.font_index = 0
+ self.has_font = 0
+ self.has_dxf_font = 0
+
+ self.bold = 0
+ self.underline = 0
+ self.italic = 0
+ self.font_name = 'Calibri'
+ self.font_size = 11
+ self.font_color = 0x0
+ self.font_strikeout = 0
+ self.font_outline = 0
+ self.font_shadow = 0
+ self.font_script = 0
+ self.font_family = 2
+ self.font_charset = 0
+ self.font_scheme = 'minor'
+ self.font_condense = 0
+ self.font_extend = 0
+ self.theme = 0
+ self.hyperlink = 0
+
+ self.hidden = 0
+ self.locked = 1
+
+ self.text_h_align = 0
+ self.text_wrap = 0
+ self.text_v_align = 0
+ self.text_justlast = 0
+ self.rotation = 0
+
+ self.fg_color = 0
+ self.bg_color = 0
+ self.pattern = 0
+ self.has_fill = 0
+ self.has_dxf_fill = 0
+ self.fill_index = 0
+ self.fill_count = 0
+
+ self.border_index = 0
+ self.has_border = 0
+ self.has_dxf_border = 0
+ self.border_count = 0
+
+ self.bottom = 0
+ self.bottom_color = 0
+ self.diag_border = 0
+ self.diag_color = 0
+ self.diag_type = 0
+ self.left = 0
+ self.left_color = 0
+ self.right = 0
+ self.right_color = 0
+ self.top = 0
+ self.top_color = 0
+
+ self.indent = 0
+ self.shrink = 0
+ self.merge_range = 0
+ self.reading_order = 0
+ self.just_distrib = 0
+ self.color_indexed = 0
+ self.font_only = 0
+
+ # Convert properties in the constructor to method calls.
+ for key, value in properties.items():
+ getattr(self, 'set_' + key)(value)
+
+ ###########################################################################
+ #
+ # Format properties.
+ #
+ ###########################################################################
+
+ def set_font_name(self, font_name):
+ """
+ Set the Format font_name property such as 'Time New Roman'. The
+ default Excel font is 'Calibri'.
+
+ Args:
+ font_name: String with the font name. No default.
+
+ Returns:
+ Nothing.
+
+ """
+ self.font_name = font_name
+
+ def set_font_size(self, font_size=11):
+ """
+ Set the Format font_size property. The default Excel font size is 11.
+
+ Args:
+ font_size: Int with font size. No default.
+
+ Returns:
+ Nothing.
+
+ """
+ self.font_size = font_size
+
+ def set_font_color(self, font_color):
+ """
+ Set the Format font_color property. The Excel default is black.
+
+ Args:
+ font_color: String with the font color. No default.
+
+ Returns:
+ Nothing.
+
+ """
+ self.font_color = self._get_color(font_color)
+
+ def set_bold(self, bold=True):
+ """
+ Set the Format bold property.
+
+ Args:
+ bold: Default is True, turns property on.
+
+ Returns:
+ Nothing.
+
+ """
+ self.bold = bold
+
+ def set_italic(self, italic=True):
+ """
+ Set the Format italic property.
+
+ Args:
+ italic: Default is True, turns property on.
+
+ Returns:
+ Nothing.
+
+ """
+ self.italic = italic
+
+ def set_underline(self, underline=1):
+ """
+ Set the Format underline property.
+
+ Args:
+ underline: Default is 1, single underline.
+
+ Returns:
+ Nothing.
+
+ """
+ self.underline = underline
+
+ def set_font_strikeout(self, font_strikeout=True):
+ """
+ Set the Format font_strikeout property.
+
+ Args:
+ font_strikeout: Default is True, turns property on.
+
+ Returns:
+ Nothing.
+
+ """
+ self.font_strikeout = font_strikeout
+
+ def set_font_script(self, font_script=1):
+ """
+ Set the Format font_script property.
+
+ Args:
+ font_script: Default is 1, superscript.
+
+ Returns:
+ Nothing.
+
+ """
+ self.font_script = font_script
+
+ def set_font_outline(self, font_outline=True):
+ """
+ Set the Format font_outline property.
+
+ Args:
+ font_outline: Default is True, turns property on.
+
+ Returns:
+ Nothing.
+
+ """
+ self.font_outline = font_outline
+
+ def set_font_shadow(self, font_shadow=True):
+ """
+ Set the Format font_shadow property.
+
+ Args:
+ font_shadow: Default is True, turns property on.
+
+ Returns:
+ Nothing.
+
+ """
+ self.font_shadow = font_shadow
+
+ def set_num_format(self, num_format):
+ """
+ Set the Format num_format property such as '#,##0'.
+
+ Args:
+ num_format: String representing the number format. No default.
+
+ Returns:
+ Nothing.
+
+ """
+ self.num_format = num_format
+
+ def set_locked(self, locked=True):
+ """
+ Set the Format locked property.
+
+ Args:
+ locked: Default is True, turns property on.
+
+ Returns:
+ Nothing.
+
+ """
+ self.locked = locked
+
+ def set_hidden(self, hidden=True):
+ """
+ Set the Format hidden property.
+
+ Args:
+ hidden: Default is True, turns property on.
+
+ Returns:
+ Nothing.
+
+ """
+ self.hidden = hidden
+
+ def set_align(self, alignment):
+ """
+ Set the Format cell alignment.
+
+ Args:
+ alignment: String representing alignment. No default.
+
+ Returns:
+ Nothing.
+ """
+ alignment = alignment.lower()
+
+ # Set horizontal alignment properties.
+ if alignment == 'left':
+ self.set_text_h_align(1)
+ if alignment == 'centre':
+ self.set_text_h_align(2)
+ if alignment == 'center':
+ self.set_text_h_align(2)
+ if alignment == 'right':
+ self.set_text_h_align(3)
+ if alignment == 'fill':
+ self.set_text_h_align(4)
+ if alignment == 'justify':
+ self.set_text_h_align(5)
+ if alignment == 'center_across':
+ self.set_text_h_align(6)
+ if alignment == 'centre_across':
+ self.set_text_h_align(6)
+ if alignment == 'distributed':
+ self.set_text_h_align(7)
+ if alignment == 'justify_distributed':
+ self.set_text_h_align(7)
+
+ if alignment == 'justify_distributed':
+ self.just_distrib = 1
+
+ # Set vertical alignment properties.
+ if alignment == 'top':
+ self.set_text_v_align(1)
+ if alignment == 'vcentre':
+ self.set_text_v_align(2)
+ if alignment == 'vcenter':
+ self.set_text_v_align(2)
+ if alignment == 'bottom':
+ self.set_text_v_align(3)
+ if alignment == 'vjustify':
+ self.set_text_v_align(4)
+ if alignment == 'vdistributed':
+ self.set_text_v_align(5)
+
+ def set_center_across(self, align_type=None):
+ """
+ Set the Format center_across property.
+
+ Returns:
+ Nothing.
+
+ """
+ self.set_text_h_align(6)
+
+ def set_text_wrap(self, text_wrap=True):
+ """
+ Set the Format text_wrap property.
+
+ Args:
+ text_wrap: Default is True, turns property on.
+
+ Returns:
+ Nothing.
+
+ """
+ self.text_wrap = text_wrap
+
+ def set_rotation(self, rotation):
+ """
+ Set the Format rotation property.
+
+ Args:
+ rotation: Rotation angle. No default.
+
+ Returns:
+ Nothing.
+
+ """
+ rotation = int(rotation)
+
+ # Map user angle to Excel angle.
+ if rotation == 270:
+ rotation = 255
+ elif -90 <= rotation <= 90:
+ if rotation < 0:
+ rotation = -rotation + 90
+ else:
+ raise Exception(
+ "Rotation rotation outside range: -90 <= angle <= 90")
+
+ self.rotation = rotation
+
+ def set_indent(self, indent=1):
+ """
+ Set the Format indent property.
+
+ Args:
+ indent: Default is 1, first indentation level.
+
+ Returns:
+ Nothing.
+
+ """
+ self.indent = indent
+
+ def set_shrink(self, shrink=True):
+ """
+ Set the Format shrink property.
+
+ Args:
+ shrink: Default is True, turns property on.
+
+ Returns:
+ Nothing.
+
+ """
+ self.shrink = shrink
+
+ def set_text_justlast(self, text_justlast=True):
+ """
+ Set the Format text_justlast property.
+
+ Args:
+ text_justlast: Default is True, turns property on.
+
+ Returns:
+ Nothing.
+
+ """
+ self.text_justlast = text_justlast
+
+ def set_pattern(self, pattern=1):
+ """
+ Set the Format pattern property.
+
+ Args:
+ pattern: Default is 1, solid fill.
+
+ Returns:
+ Nothing.
+
+ """
+ self.pattern = pattern
+
+ def set_bg_color(self, bg_color):
+ """
+ Set the Format bg_color property.
+
+ Args:
+ bg_color: Background color. No default.
+
+ Returns:
+ Nothing.
+
+ """
+ self.bg_color = self._get_color(bg_color)
+
+ def set_fg_color(self, fg_color):
+ """
+ Set the Format fg_color property.
+
+ Args:
+ fg_color: Foreground color. No default.
+
+ Returns:
+ Nothing.
+
+ """
+ self.fg_color = self._get_color(fg_color)
+
+ # set_border(style) Set cells borders to the same style
+ def set_border(self, style=1):
+ """
+ Set the Format bottom property.
+
+ Args:
+ bottom: Default is 1, border type 1.
+
+ Returns:
+ Nothing.
+
+ """
+ self.set_bottom(style)
+ self.set_top(style)
+ self.set_left(style)
+ self.set_right(style)
+
+ # set_border_color(color) Set cells border to the same color
+ def set_border_color(self, color):
+ """
+ Set the Format bottom property.
+
+ Args:
+ color: Color string. No default.
+
+ Returns:
+ Nothing.
+
+ """
+ self.set_bottom_color(color)
+ self.set_top_color(color)
+ self.set_left_color(color)
+ self.set_right_color(color)
+
+ def set_bottom(self, bottom=1):
+ """
+ Set the Format bottom property.
+
+ Args:
+ bottom: Default is 1, border type 1.
+
+ Returns:
+ Nothing.
+
+ """
+ self.bottom = bottom
+
+ def set_bottom_color(self, bottom_color):
+ """
+ Set the Format bottom_color property.
+
+ Args:
+ bottom_color: Color string. No default.
+
+ Returns:
+ Nothing.
+
+ """
+ self.bottom_color = self._get_color(bottom_color)
+
+ def set_diag_type(self, diag_type=1):
+ """
+ Set the Format diag_type property.
+
+ Args:
+ diag_type: Default is 1, border type 1.
+
+ Returns:
+ Nothing.
+
+ """
+ self.diag_type = diag_type
+
+ def set_left(self, left=1):
+ """
+ Set the Format left property.
+
+ Args:
+ left: Default is 1, border type 1.
+
+ Returns:
+ Nothing.
+
+ """
+ self.left = left
+
+ def set_left_color(self, left_color):
+ """
+ Set the Format left_color property.
+
+ Args:
+ left_color: Color string. No default.
+
+ Returns:
+ Nothing.
+
+ """
+ self.left_color = self._get_color(left_color)
+
+ def set_right(self, right=1):
+ """
+ Set the Format right property.
+
+ Args:
+ right: Default is 1, border type 1.
+
+ Returns:
+ Nothing.
+
+ """
+ self.right = right
+
+ def set_right_color(self, right_color):
+ """
+ Set the Format right_color property.
+
+ Args:
+ right_color: Color string. No default.
+
+ Returns:
+ Nothing.
+
+ """
+ self.right_color = self._get_color(right_color)
+
+ def set_top(self, top=1):
+ """
+ Set the Format top property.
+
+ Args:
+ top: Default is 1, border type 1.
+
+ Returns:
+ Nothing.
+
+ """
+ self.top = top
+
+ def set_top_color(self, top_color):
+ """
+ Set the Format top_color property.
+
+ Args:
+ top_color: Color string. No default.
+
+ Returns:
+ Nothing.
+
+ """
+ self.top_color = self._get_color(top_color)
+
+ def set_diag_color(self, diag_color):
+ """
+ Set the Format diag_color property.
+
+ Args:
+ diag_color: Color string. No default.
+
+ Returns:
+ Nothing.
+
+ """
+ self.diag_color = self._get_color(diag_color)
+
+ def set_diag_border(self, diag_border=1):
+ """
+ Set the Format diag_border property.
+
+ Args:
+ diag_border: Default is 1, border type 1.
+
+ Returns:
+ Nothing.
+
+ """
+ self.diag_border = diag_border
+
+ ###########################################################################
+ #
+ # Internal Format properties. These aren't documented since they are
+ # either only used internally or else are unlikely to be set by the user.
+ #
+ ###########################################################################
+
+ def set_has_font(self, has_font=True):
+ # Set the has_font property.
+ self.has_font = has_font
+
+ def set_has_fill(self, has_fill=True):
+ # Set the has_fill property.
+ self.has_fill = has_fill
+
+ def set_font_index(self, font_index):
+ # Set the font_index property.
+ self.font_index = font_index
+
+ def set_xf_index(self, xf_index):
+ # Set the xf_index property.
+ self.xf_index = xf_index
+
+ def set_dxf_index(self, dxf_index):
+ # Set the xf_index property.
+ self.dxf_index = dxf_index
+
+ def set_num_format_index(self, num_format_index):
+ # Set the num_format_index property.
+ self.num_format_index = num_format_index
+
+ def set_text_h_align(self, text_h_align):
+ # Set the text_h_align property.
+ self.text_h_align = text_h_align
+
+ def set_text_v_align(self, text_v_align):
+ # Set the text_v_align property.
+ self.text_v_align = text_v_align
+
+ def set_reading_order(self, reading_order=True):
+ # Set the reading_order property.
+ self.reading_order = reading_order
+
+ def set_valign(self, align):
+ # Set vertical cell alignment. This is required by the constructor
+ # properties dict to differentiate between the vertical and horizontal
+ # properties.
+ self.set_align(align)
+
+ def set_font_family(self, font_family):
+ # Set the Format font_family property.
+ self.font_family = font_family
+
+ def set_font_charset(self, font_charset):
+ # Set the Format font_charset property.
+ self.font_charset = font_charset
+
+ def set_font_scheme(self, font_scheme):
+ # Set the Format font_scheme property.
+ self.font_scheme = font_scheme
+
+ def set_font_condense(self, font_condense):
+ # Set the Format font_condense property.
+ self.font_condense = font_condense
+
+ def set_font_extend(self, font_extend):
+ # Set the Format font_extend property.
+ self.font_extend = font_extend
+
+ def set_theme(self, theme):
+ # Set the Format theme property.
+ self.theme = theme
+
+ def set_hyperlink(self, hyperlink=True):
+ # Set the properties for the hyperlink style. This doesn't
+ # currently work. To be fixed when styles are supported.
+
+ self.set_underline(1)
+ self.set_theme(10)
+ self.set_align('top')
+ self.hyperlink = hyperlink
+
+ def set_color_indexed(self, color_index):
+ # Used in the cell comment format.
+ self.color_indexed = color_index
+
+ def set_font_only(self, font_only=True):
+ # Used in the cell comment format.
+ self.font_only = font_only
+
+ # Compatibility methods.
+ def set_font(self, font_name):
+ # For compatibility with Excel::Writer::XLSX.
+ self.font_name = font_name
+
+ def set_size(self, font_size):
+ # For compatibility with Excel::Writer::XLSX.
+ self.font_size = font_size
+
+ def set_color(self, font_color):
+ # For compatibility with Excel::Writer::XLSX.
+ self.font_color = self._get_color(font_color)
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _get_align_properties(self):
+ # Return properties for an Style xf <alignment> sub-element.
+ changed = 0
+ align = []
+
+ # Check if any alignment options in the format have been changed.
+ if (self.text_h_align or self.text_v_align or self.indent
+ or self.rotation or self.text_wrap or self.shrink
+ or self.reading_order):
+ changed = 1
+ else:
+ return changed, align
+
+ # Indent is only allowed for horizontal left, right and distributed.
+ # If it is defined for any other alignment or no alignment has
+ # been set then default to left alignment.
+ if (self.indent
+ and self.text_h_align != 1
+ and self.text_h_align != 3
+ and self.text_h_align != 7):
+ self.text_h_align = 1
+
+ # Check for properties that are mutually exclusive.
+ if self.text_wrap:
+ self.shrink = 0
+ if self.text_h_align == 4:
+ self.shrink = 0
+ if self.text_h_align == 5:
+ self.shrink = 0
+ if self.text_h_align == 7:
+ self.shrink = 0
+ if self.text_h_align != 7:
+ self.just_distrib = 0
+ if self.indent:
+ self.just_distrib = 0
+
+ continuous = 'centerContinuous'
+
+ if self.text_h_align == 1:
+ align.append(('horizontal', 'left'))
+ if self.text_h_align == 2:
+ align.append(('horizontal', 'center'))
+ if self.text_h_align == 3:
+ align.append(('horizontal', 'right'))
+ if self.text_h_align == 4:
+ align.append(('horizontal', 'fill'))
+ if self.text_h_align == 5:
+ align.append(('horizontal', 'justify'))
+ if self.text_h_align == 6:
+ align.append(('horizontal', continuous))
+ if self.text_h_align == 7:
+ align.append(('horizontal', 'distributed'))
+
+ if self.just_distrib:
+ align.append(('justifyLastLine', 1))
+
+ # Property 'vertical' => 'bottom' is a default. It sets applyAlignment
+ # without an alignment sub-element.
+ if self.text_v_align == 1:
+ align.append(('vertical', 'top'))
+ if self.text_v_align == 2:
+ align.append(('vertical', 'center'))
+ if self.text_v_align == 4:
+ align.append(('vertical', 'justify'))
+ if self.text_v_align == 5:
+ align.append(('vertical', 'distributed'))
+
+ if self.indent:
+ align.append(('indent', self.indent))
+ if self.rotation:
+ align.append(('textRotation', self.rotation))
+
+ if self.text_wrap:
+ align.append(('wrapText', 1))
+ if self.shrink:
+ align.append(('shrinkToFit', 1))
+
+ if self.reading_order == 1:
+ align.append(('readingOrder', 1))
+ if self.reading_order == 2:
+ align.append(('readingOrder', 2))
+
+ return changed, align
+
+ def _get_protection_properties(self):
+ # Return properties for an Excel XML <Protection> element.
+ attribs = []
+
+ if not self.locked:
+ attribs.append(('locked', 0))
+ if self.hidden:
+ attribs.append(('hidden', 1))
+
+ return attribs
+
+ def _get_format_key(self):
+ # Returns a unique hash key for a font. Used by Workbook.
+ key = ':'.join(self._to_string(x) for x in (
+ self._get_font_key(),
+ self._get_border_key(),
+ self._get_fill_key(),
+ self._get_alignment_key(),
+ self.num_format,
+ self.locked,
+ self.hidden))
+
+ return key
+
+ def _get_font_key(self):
+ # Returns a unique hash key for a font. Used by Workbook.
+ key = ':'.join(self._to_string(x) for x in (
+ self.bold,
+ self.font_color,
+ self.font_charset,
+ self.font_family,
+ self.font_outline,
+ self.font_script,
+ self.font_shadow,
+ self.font_strikeout,
+ self.font_name,
+ self.italic,
+ self.font_size,
+ self.underline))
+
+ return key
+
+ def _get_border_key(self):
+ # Returns a unique hash key for a border style. Used by Workbook.
+ key = ':'.join(self._to_string(x) for x in (
+ self.bottom,
+ self.bottom_color,
+ self.diag_border,
+ self.diag_color,
+ self.diag_type,
+ self.left,
+ self.left_color,
+ self.right,
+ self.right_color,
+ self.top,
+ self.top_color))
+
+ return key
+
+ def _get_fill_key(self):
+ # Returns a unique hash key for a fill style. Used by Workbook.
+ key = ':'.join(self._to_string(x) for x in (
+ self.pattern,
+ self.bg_color,
+ self.fg_color))
+
+ return key
+
+ def _get_alignment_key(self):
+ # Returns a unique hash key for alignment formats.
+
+ key = ':'.join(self._to_string(x) for x in (
+ self.text_h_align,
+ self.text_v_align,
+ self.indent,
+ self.rotation,
+ self.text_wrap,
+ self.shrink,
+ self.reading_order))
+
+ return key
+
+ def _get_xf_index(self):
+ # Returns the XF index number used by Excel to identify a format.
+ if self.xf_index is not None:
+ # Format already has an index number so return it.
+ return self.xf_index
+ else:
+ # Format doesn't have an index number so assign one.
+ key = self._get_format_key()
+
+ if key in self.xf_format_indices:
+ # Format matches existing format with an index.
+ return self.xf_format_indices[key]
+ else:
+ # New format requiring an index. Note. +1 since Excel
+ # has an implicit "General" format at index 0.
+ index = 1 + len(self.xf_format_indices)
+ self.xf_format_indices[key] = index
+ self.xf_index = index
+ return index
+
+ def _get_dxf_index(self):
+ # Returns the DXF index number used by Excel to identify a format.
+ if self.dxf_index is not None:
+ # Format already has an index number so return it.
+ return self.dxf_index
+ else:
+ # Format doesn't have an index number so assign one.
+ key = self._get_format_key()
+
+ if key in self.dxf_format_indices:
+ # Format matches existing format with an index.
+ return self.dxf_format_indices[key]
+ else:
+ # New format requiring an index.
+ index = len(self.dxf_format_indices)
+ self.dxf_format_indices[key] = index
+ self.dxf_index = index
+ return index
+
+ def _get_color(self, color):
+ # Used in conjunction with the set_xxx_color methods to convert a
+ # color name into an RGB formatted string. These colors are for
+ # backward compatibility with older versions of Excel.
+ named_colors = {
+ 'black': '#000000',
+ 'blue': '#0000FF',
+ 'brown': '#800000',
+ 'cyan': '#00FFFF',
+ 'gray': '#808080',
+ 'green': '#008000',
+ 'lime': '#00FF00',
+ 'magenta': '#FF00FF',
+ 'navy': '#000080',
+ 'orange': '#FF6600',
+ 'pink': '#FF00FF',
+ 'purple': '#800080',
+ 'red': '#FF0000',
+ 'silver': '#C0C0C0',
+ 'white': '#FFFFFF',
+ 'yellow': '#FFFF00',
+ }
+
+ if color in named_colors:
+ color = named_colors[color]
+
+ return color
+
+ def _to_string(self, value):
+ # Convert number to a string but allow for utf-8 strings in Python 2.
+ try:
+ return str(value)
+ except UnicodeEncodeError:
+ return value.encode('utf-8')
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
diff --git a/xlsxwriter/packager.py b/xlsxwriter/packager.py
new file mode 100644
index 0000000..c6e715e
--- /dev/null
+++ b/xlsxwriter/packager.py
@@ -0,0 +1,665 @@
+###############################################################################
+#
+# Packager - A class for writing the Excel XLSX Worksheet file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Standard packages.
+import os
+import stat
+import tempfile
+from shutil import copy
+
+from .compatibility import StringIO
+from .compatibility import BytesIO
+
+# Package imports.
+from .app import App
+from .contenttypes import ContentTypes
+from .core import Core
+from .custom import Custom
+from .relationships import Relationships
+from .sharedstrings import SharedStrings
+from .styles import Styles
+from .theme import Theme
+from .vml import Vml
+from .table import Table
+from .comments import Comments
+
+
+class Packager(object):
+ """
+ A class for writing the Excel XLSX Packager file.
+
+ This module is used in conjunction with XlsxWriter to create an
+ Excel XLSX container file.
+
+ From Wikipedia: The Open Packaging Conventions (OPC) is a
+ container-file technology initially created by Microsoft to store
+ a combination of XML and non-XML files that together form a single
+ entity such as an Open XML Paper Specification (OpenXPS)
+ document. http://en.wikipedia.org/wiki/Open_Packaging_Conventions.
+
+ At its simplest an Excel XLSX file contains the following elements::
+
+ ____ [Content_Types].xml
+ |
+ |____ docProps
+ | |____ app.xml
+ | |____ core.xml
+ |
+ |____ xl
+ | |____ workbook.xml
+ | |____ worksheets
+ | | |____ sheet1.xml
+ | |
+ | |____ styles.xml
+ | |
+ | |____ theme
+ | | |____ theme1.xml
+ | |
+ | |_____rels
+ | |____ workbook.xml.rels
+ |
+ |_____rels
+ |____ .rels
+
+ The Packager class coordinates the classes that represent the
+ elements of the package and writes them into the XLSX file.
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(Packager, self).__init__()
+
+ self.tmpdir = ''
+ self.in_memory = False
+ self.workbook = None
+ self.worksheet_count = 0
+ self.chartsheet_count = 0
+ self.chart_count = 0
+ self.drawing_count = 0
+ self.table_count = 0
+ self.num_vml_files = 0
+ self.num_comment_files = 0
+ self.named_ranges = []
+ self.filenames = []
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _set_tmpdir(self, tmpdir):
+ # Set an optional user defined temp directory.
+ self.tmpdir = tmpdir
+
+ def _set_in_memory(self, in_memory):
+ # Set the optional 'in_memory' mode.
+ self.in_memory = in_memory
+
+ def _add_workbook(self, workbook):
+ # Add the Excel::Writer::XLSX::Workbook object to the package.
+ self.workbook = workbook
+ self.chart_count = len(workbook.charts)
+ self.drawing_count = len(workbook.drawings)
+ self.num_vml_files = workbook.num_vml_files
+ self.num_comment_files = workbook.num_comment_files
+ self.named_ranges = workbook.named_ranges
+
+ for worksheet in self.workbook.worksheets():
+ if worksheet.is_chartsheet:
+ self.chartsheet_count += 1
+ else:
+ self.worksheet_count += 1
+
+ def _create_package(self):
+ # Write the xml files that make up the XLSX OPC package.
+ self._write_worksheet_files()
+ self._write_chartsheet_files()
+ self._write_workbook_file()
+ self._write_chart_files()
+ self._write_drawing_files()
+ self._write_vml_files()
+ self._write_comment_files()
+ self._write_table_files()
+ self._write_shared_strings_file()
+ self._write_app_file()
+ self._write_core_file()
+ self._write_custom_file()
+ self._write_content_types_file()
+ self._write_styles_file()
+ self._write_theme_file()
+ self._write_root_rels_file()
+ self._write_workbook_rels_file()
+ self._write_worksheet_rels_files()
+ self._write_chartsheet_rels_files()
+ self._write_drawing_rels_files()
+ self._add_image_files()
+ self._add_vba_project()
+
+ return self.filenames
+
+ def _filename(self, xml_filename):
+ # Create a temp filename to write the XML data to and store the Excel
+ # filename to use as the name in the Zip container.
+ if self.in_memory:
+ os_filename = StringIO()
+ else:
+ (fd, os_filename) = tempfile.mkstemp(dir=self.tmpdir)
+ os.close(fd)
+
+ self.filenames.append((os_filename, xml_filename, False))
+
+ return os_filename
+
+ def _write_workbook_file(self):
+ # Write the workbook.xml file.
+ workbook = self.workbook
+
+ workbook._set_xml_writer(self._filename('xl/workbook.xml'))
+ workbook._assemble_xml_file()
+
+ def _write_worksheet_files(self):
+ # Write the worksheet files.
+ index = 1
+ for worksheet in self.workbook.worksheets():
+ if worksheet.is_chartsheet:
+ continue
+
+ if worksheet.optimization == 1:
+ worksheet._opt_reopen()
+ worksheet._write_single_row()
+
+ worksheet._set_xml_writer(self._filename('xl/worksheets/sheet'
+ + str(index) + '.xml'))
+ worksheet._assemble_xml_file()
+ index += 1
+
+ def _write_chartsheet_files(self):
+ # Write the chartsheet files.
+ index = 1
+ for worksheet in self.workbook.worksheets():
+ if not worksheet.is_chartsheet:
+ continue
+
+ worksheet._set_xml_writer(self._filename('xl/chartsheets/sheet'
+ + str(index) + '.xml'))
+ worksheet._assemble_xml_file()
+ index += 1
+
+ def _write_chart_files(self):
+ # Write the chart files.
+ if not self.workbook.charts:
+ return
+
+ index = 1
+ for chart in self.workbook.charts:
+ # Check that the chart has at least one data series.
+ if not chart.series:
+ raise Exception("Chart%d must contain at least one "
+ "data series. See chart.add_series()."
+ % index)
+
+ chart._set_xml_writer(self._filename('xl/charts/chart'
+ + str(index) + '.xml'))
+ chart._assemble_xml_file()
+ index += 1
+
+ def _write_drawing_files(self):
+ # Write the drawing files.
+ if not self.drawing_count:
+ return
+
+ index = 1
+ for drawing in self.workbook.drawings:
+ drawing._set_xml_writer(self._filename('xl/drawings/drawing'
+ + str(index) + '.xml'))
+ drawing._assemble_xml_file()
+ index += 1
+
+ def _write_vml_files(self):
+ # Write the comment VML files.
+ index = 1
+ for worksheet in self.workbook.worksheets():
+ if not worksheet.has_vml and not worksheet.has_header_vml:
+ continue
+ if worksheet.has_vml:
+ vml = Vml()
+ vml._set_xml_writer(self._filename('xl/drawings/vmlDrawing'
+ + str(index) + '.vml'))
+ vml._assemble_xml_file(worksheet.vml_data_id,
+ worksheet.vml_shape_id,
+ worksheet.comments_list,
+ worksheet.buttons_list)
+ index += 1
+
+ if worksheet.has_header_vml:
+ vml = Vml()
+
+ vml._set_xml_writer(self._filename('xl/drawings/vmlDrawing'
+ + str(index) + '.vml'))
+ vml._assemble_xml_file(worksheet.vml_header_id,
+ worksheet.vml_header_id * 1024,
+ None,
+ None,
+ worksheet.header_images_list)
+
+ self._write_vml_drawing_rels_file(worksheet, index)
+ index += 1
+
+ def _write_comment_files(self):
+ # Write the comment files.
+ index = 1
+ for worksheet in self.workbook.worksheets():
+ if not worksheet.has_comments:
+ continue
+
+ comment = Comments()
+ comment._set_xml_writer(self._filename('xl/comments'
+ + str(index) + '.xml'))
+ comment._assemble_xml_file(worksheet.comments_list)
+ index += 1
+
+ def _write_shared_strings_file(self):
+ # Write the sharedStrings.xml file.
+ sst = SharedStrings()
+ sst.string_table = self.workbook.str_table
+
+ if not self.workbook.str_table.count:
+ return
+
+ sst._set_xml_writer(self._filename('xl/sharedStrings.xml'))
+ sst._assemble_xml_file()
+
+ def _write_app_file(self):
+ # Write the app.xml file.
+ properties = self.workbook.doc_properties
+ app = App()
+
+ # Add the Worksheet heading pairs.
+ app._add_heading_pair(['Worksheets', self.worksheet_count])
+
+ # Add the Chartsheet heading pairs.
+ app._add_heading_pair(['Charts', self.chartsheet_count])
+
+ # Add the Worksheet parts.
+ for worksheet in self.workbook.worksheets():
+ if worksheet.is_chartsheet:
+ continue
+ app._add_part_name(worksheet.name)
+
+ # Add the Chartsheet parts.
+ for worksheet in self.workbook.worksheets():
+ if not worksheet.is_chartsheet:
+ continue
+ app._add_part_name(worksheet.name)
+
+ # Add the Named Range heading pairs.
+ if self.named_ranges:
+ app._add_heading_pair(['Named Ranges', len(self.named_ranges)])
+
+ # Add the Named Ranges parts.
+ for named_range in self.named_ranges:
+ app._add_part_name(named_range)
+
+ app._set_properties(properties)
+
+ app._set_xml_writer(self._filename('docProps/app.xml'))
+ app._assemble_xml_file()
+
+ def _write_core_file(self):
+ # Write the core.xml file.
+ properties = self.workbook.doc_properties
+ core = Core()
+
+ core._set_properties(properties)
+ core._set_xml_writer(self._filename('docProps/core.xml'))
+ core._assemble_xml_file()
+
+ def _write_custom_file(self):
+ # Write the custom.xml file.
+ properties = self.workbook.custom_properties
+ custom = Custom()
+
+ if not len(properties):
+ return
+
+ custom._set_properties(properties)
+ custom._set_xml_writer(self._filename('docProps/custom.xml'))
+ custom._assemble_xml_file()
+
+ def _write_content_types_file(self):
+ # Write the ContentTypes.xml file.
+ content = ContentTypes()
+ content._add_image_types(self.workbook.image_types)
+
+ worksheet_index = 1
+ chartsheet_index = 1
+ for worksheet in self.workbook.worksheets():
+ if worksheet.is_chartsheet:
+ content._add_chartsheet_name('sheet' + str(chartsheet_index))
+ chartsheet_index += 1
+ else:
+ content._add_worksheet_name('sheet' + str(worksheet_index))
+ worksheet_index += 1
+
+ for i in range(1, self.chart_count + 1):
+ content._add_chart_name('chart' + str(i))
+
+ for i in range(1, self.drawing_count + 1):
+ content._add_drawing_name('drawing' + str(i))
+
+ if self.num_vml_files:
+ content._add_vml_name()
+
+ for i in range(1, self.table_count + 1):
+ content._add_table_name('table' + str(i))
+
+ for i in range(1, self.num_comment_files + 1):
+ content._add_comment_name('comments' + str(i))
+
+ # Add the sharedString rel if there is string data in the workbook.
+ if self.workbook.str_table.count:
+ content._add_shared_strings()
+
+ # Add vbaProject if present.
+ if self.workbook.vba_project:
+ content._add_vba_project()
+
+ # Add the custom properties if present.
+ if self.workbook.custom_properties:
+ content._add_custom_properties()
+
+ content._set_xml_writer(self._filename('[Content_Types].xml'))
+ content._assemble_xml_file()
+
+ def _write_styles_file(self):
+ # Write the style xml file.
+ xf_formats = self.workbook.xf_formats
+ palette = self.workbook.palette
+ font_count = self.workbook.font_count
+ num_format_count = self.workbook.num_format_count
+ border_count = self.workbook.border_count
+ fill_count = self.workbook.fill_count
+ custom_colors = self.workbook.custom_colors
+ dxf_formats = self.workbook.dxf_formats
+
+ styles = Styles()
+ styles._set_style_properties([
+ xf_formats,
+ palette,
+ font_count,
+ num_format_count,
+ border_count,
+ fill_count,
+ custom_colors,
+ dxf_formats])
+
+ styles._set_xml_writer(self._filename('xl/styles.xml'))
+ styles._assemble_xml_file()
+
+ def _write_theme_file(self):
+ # Write the theme xml file.
+ theme = Theme()
+
+ theme._set_xml_writer(self._filename('xl/theme/theme1.xml'))
+ theme._assemble_xml_file()
+
+ def _write_table_files(self):
+ # Write the table files.
+ index = 1
+ for worksheet in self.workbook.worksheets():
+ table_props = worksheet.tables
+
+ if not table_props:
+ continue
+
+ for table_props in table_props:
+ table = Table()
+ table._set_xml_writer(self._filename('xl/tables/table'
+ + str(index) + '.xml'))
+ table._set_properties(table_props)
+ table._assemble_xml_file()
+ self.table_count += 1
+ index += 1
+
+ def _write_root_rels_file(self):
+ # Write the _rels/.rels xml file.
+ rels = Relationships()
+
+ rels._add_document_relationship('/officeDocument', 'xl/workbook.xml')
+
+ rels._add_package_relationship('/metadata/core-properties',
+ 'docProps/core.xml')
+
+ rels._add_document_relationship('/extended-properties',
+ 'docProps/app.xml')
+
+ if self.workbook.custom_properties:
+ rels._add_document_relationship('/custom-properties',
+ 'docProps/custom.xml')
+
+ rels._set_xml_writer(self._filename('_rels/.rels'))
+
+ rels._assemble_xml_file()
+
+ def _write_workbook_rels_file(self):
+ # Write the _rels/.rels xml file.
+ rels = Relationships()
+
+ worksheet_index = 1
+ chartsheet_index = 1
+
+ for worksheet in self.workbook.worksheets():
+ if worksheet.is_chartsheet:
+ rels._add_document_relationship('/chartsheet',
+ 'chartsheets/sheet'
+ + str(chartsheet_index)
+ + '.xml')
+ chartsheet_index += 1
+ else:
+ rels._add_document_relationship('/worksheet',
+ 'worksheets/sheet'
+ + str(worksheet_index)
+ + '.xml')
+ worksheet_index += 1
+
+ rels._add_document_relationship('/theme', 'theme/theme1.xml')
+ rels._add_document_relationship('/styles', 'styles.xml')
+
+ # Add the sharedString rel if there is string data in the workbook.
+ if self.workbook.str_table.count:
+ rels._add_document_relationship('/sharedStrings',
+ 'sharedStrings.xml')
+
+ # Add vbaProject if present.
+ if self.workbook.vba_project:
+ rels._add_ms_package_relationship('/vbaProject', 'vbaProject.bin')
+
+ rels._set_xml_writer(self._filename('xl/_rels/workbook.xml.rels'))
+ rels._assemble_xml_file()
+
+ def _write_worksheet_rels_files(self):
+ # Write data such as hyperlinks or drawings.
+ index = 0
+ for worksheet in self.workbook.worksheets():
+
+ if worksheet.is_chartsheet:
+ continue
+
+ index += 1
+
+ external_links = (worksheet.external_hyper_links +
+ worksheet.external_drawing_links +
+ worksheet.external_vml_links +
+ worksheet.external_table_links +
+ worksheet.external_comment_links)
+
+ if not external_links:
+ continue
+
+ # Create the worksheet .rels dirs.
+ rels = Relationships()
+
+ for link_data in external_links:
+ rels._add_worksheet_relationship(*link_data)
+
+ # Create .rels file such as /xl/worksheets/_rels/sheet1.xml.rels.
+ rels._set_xml_writer(self._filename('xl/worksheets/_rels/sheet'
+ + str(index) + '.xml.rels'))
+ rels._assemble_xml_file()
+
+ def _write_chartsheet_rels_files(self):
+ # Write the chartsheet .rels files for links to drawing files.
+ index = 0
+ for worksheet in self.workbook.worksheets():
+
+ if not worksheet.is_chartsheet:
+ continue
+
+ index += 1
+
+ external_links = worksheet.external_drawing_links
+
+ if not external_links:
+ continue
+
+ # Create the chartsheet .rels xlsx_dir.
+ rels = Relationships()
+
+ for link_data in external_links:
+ rels._add_worksheet_relationship(*link_data)
+
+ # Create .rels file such as /xl/chartsheets/_rels/sheet1.xml.rels.
+ rels._set_xml_writer(self._filename('xl/chartsheets/_rels/sheet'
+ + str(index) + '.xml.rels'))
+ rels._assemble_xml_file()
+
+ def _write_drawing_rels_files(self):
+ # Write the drawing .rels files for worksheets with charts or drawings.
+ index = 0
+ for worksheet in self.workbook.worksheets():
+ if not worksheet.drawing_links:
+ continue
+ index += 1
+
+ # Create the drawing .rels xlsx_dir.
+ rels = Relationships()
+
+ for drawing_data in worksheet.drawing_links:
+ rels._add_document_relationship(*drawing_data)
+
+ # Create .rels file such as /xl/drawings/_rels/sheet1.xml.rels.
+ rels._set_xml_writer(self._filename('xl/drawings/_rels/drawing'
+ + str(index) + '.xml.rels'))
+ rels._assemble_xml_file()
+
+ def _write_vml_drawing_rels_file(self, worksheet, index):
+ # Write the vmlDdrawing .rels files for worksheets with images in
+ # headers or footers.
+
+ # Create the drawing .rels dir.
+ rels = Relationships()
+
+ for drawing_data in worksheet.vml_drawing_links:
+ rels._add_document_relationship(*drawing_data)
+
+ # Create .rels file such as /xl/drawings/_rels/vmlDrawing1.vml.rels.
+ rels._set_xml_writer(self._filename('xl/drawings/_rels/vmlDrawing'
+ + str(index)
+ + '.vml.rels'))
+ rels._assemble_xml_file()
+
+ def _add_image_files(self):
+ # Write the /xl/media/image?.xml files.
+ workbook = self.workbook
+ index = 1
+
+ for image in workbook.images:
+ filename = image[0]
+ ext = '.' + image[1]
+ image_data = image[2]
+
+ xml_image_name = 'xl/media/image' + str(index) + ext
+
+ if not self.in_memory:
+ # In file mode we just write or copy the image file.
+ os_filename = self._filename(xml_image_name)
+
+ if image_data:
+ # The data is in a byte stream. Write it to the target.
+ os_file = open(os_filename, mode='wb')
+ os_file.write(image_data.getvalue())
+ os_file.close()
+ else:
+ copy(filename, os_filename)
+
+ # Allow copies of Windows read-only images to be deleted.
+ try:
+ os.chmod(os_filename,
+ os.stat(os_filename).st_mode | stat.S_IWRITE)
+ except:
+ pass
+ else:
+ # For in-memory mode we read the image into a stream.
+ if image_data:
+ # The data is already in a byte stream.
+ os_filename = image_data
+ else:
+ image_file = open(filename, mode='rb')
+ image_data = image_file.read()
+ os_filename = BytesIO(image_data)
+ image_file.close()
+
+ self.filenames.append((os_filename, xml_image_name, True))
+
+ index += 1
+
+ def _add_vba_project(self):
+ # Copy in a vbaProject.bin file.
+ vba_project = self.workbook.vba_project
+ vba_is_stream = self.workbook.vba_is_stream
+
+ if not vba_project:
+ return
+
+ xml_vba_name = 'xl/vbaProject.bin'
+
+ if not self.in_memory:
+ # In file mode we just write or copy the VBA file.
+ os_filename = self._filename(xml_vba_name)
+
+ if vba_is_stream:
+ # The data is in a byte stream. Write it to the target.
+ os_file = open(os_filename, mode='wb')
+ os_file.write(vba_project.getvalue())
+ os_file.close()
+ else:
+ copy(vba_project, os_filename)
+
+ else:
+ # For in-memory mode we read the vba into a stream.
+ if vba_is_stream:
+ # The data is already in a byte stream.
+ os_filename = vba_project
+ else:
+ vba_file = open(vba_project, mode='rb')
+ vba_data = vba_file.read()
+ os_filename = BytesIO(vba_data)
+ vba_file.close()
+
+ self.filenames.append((os_filename, xml_vba_name, True))
diff --git a/xlsxwriter/relationships.py b/xlsxwriter/relationships.py
new file mode 100644
index 0000000..717923f
--- /dev/null
+++ b/xlsxwriter/relationships.py
@@ -0,0 +1,115 @@
+###############################################################################
+#
+# Relationships - A class for writing the Excel XLSX Worksheet file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Package imports.
+from . import xmlwriter
+
+# Long namespace strings used in the class.
+schema_root = 'http://schemas.openxmlformats.org'
+package_schema = schema_root + '/package/2006/relationships'
+document_schema = schema_root + '/officeDocument/2006/relationships'
+
+
+class Relationships(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX Relationships file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(Relationships, self).__init__()
+
+ self.relationships = []
+ self.id = 1
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ self._write_relationships()
+
+ # Close the file.
+ self._xml_close()
+
+ def _add_document_relationship(self, rel_type, target, target_mode=None):
+ # Add container relationship to XLSX .rels xml files.
+ rel_type = document_schema + rel_type
+
+ self.relationships.append((rel_type, target, target_mode))
+
+ def _add_package_relationship(self, rel_type, target):
+ # Add container relationship to XLSX .rels xml files.
+ rel_type = package_schema + rel_type
+
+ self.relationships.append((rel_type, target, None))
+
+ def _add_ms_package_relationship(self, rel_type, target):
+ # Add container relationship to XLSX .rels xml files. Uses MS schema.
+ schema = 'http://schemas.microsoft.com/office/2006/relationships'
+ rel_type = schema + rel_type
+
+ self.relationships.append((rel_type, target, None))
+
+ def _add_worksheet_relationship(self, rel_type, target, target_mode=None):
+ # Add worksheet relationship to sheet.rels xml files.
+ rel_type = document_schema + rel_type
+
+ self.relationships.append((rel_type, target, target_mode))
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_relationships(self):
+ # Write the <Relationships> element.
+ attributes = [('xmlns', package_schema,)]
+
+ self._xml_start_tag('Relationships', attributes)
+
+ for relationship in self.relationships:
+ self._write_relationship(relationship)
+
+ self._xml_end_tag('Relationships')
+
+ def _write_relationship(self, relationship):
+ # Write the <Relationship> element.
+ rel_type, target, target_mode = relationship
+
+ attributes = [
+ ('Id', 'rId' + str(self.id)),
+ ('Type', rel_type),
+ ('Target', target),
+ ]
+
+ self.id += 1
+
+ if target_mode:
+ attributes.append(('TargetMode', target_mode))
+
+ self._xml_empty_tag('Relationship', attributes)
diff --git a/xlsxwriter/shape.py b/xlsxwriter/shape.py
new file mode 100644
index 0000000..e87de68
--- /dev/null
+++ b/xlsxwriter/shape.py
@@ -0,0 +1,413 @@
+###############################################################################
+#
+# Shape - A class for to represent Excel XLSX shape objects.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import copy
+from warnings import warn
+
+
+class Shape(object):
+ """
+ A class for to represent Excel XLSX shape objects.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self, shape_type, name, options):
+ """
+ Constructor.
+
+ """
+ super(Shape, self).__init__()
+ self.name = name
+ self.shape_type = shape_type
+ self.connect = 0
+ self.drawing = 0
+ self.edit_as = ''
+ self.id = 0
+ self.text = ''
+ self.stencil = 1
+ self.element = -1
+ self.start = None
+ self.start_index = None
+ self.end = None
+ self.end_index = None
+ self.adjustments = []
+ self.start_side = ''
+ self.end_side = ''
+ self.flip_h = 0
+ self.flip_v = 0
+ self.rotation = 0
+ self.textbox = False
+
+ self.align = None
+ self.fill = None
+ self.font = None
+ self.format = None
+ self.line = None
+
+ self._set_options(options)
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _set_options(self, options):
+
+ self.align = self._get_align_properties(options.get('align'))
+ self.fill = self._get_fill_properties(options.get('fill'))
+ self.font = self._get_font_properties(options.get('font'))
+ self.gradient = self._get_gradient_properties(options.get('gradient'))
+ self.line = self._get_line_properties(options.get('line'))
+
+ if options.get('border'):
+ self.line = self._get_line_properties(options['border'])
+
+ # Gradient fill overrides solid fill.
+ if self.gradient:
+ self.fill = None
+
+ ###########################################################################
+ #
+ # Static methods for processing chart/shape style properties.
+ #
+ ###########################################################################
+
+ @staticmethod
+ def _get_line_properties(line):
+ # Convert user line properties to the structure required internally.
+
+ if not line:
+ return {'defined': False}
+
+ # Copy the user defined properties since they will be modified.
+ line = copy.deepcopy(line)
+
+ dash_types = {
+ 'solid': 'solid',
+ 'round_dot': 'sysDot',
+ 'square_dot': 'sysDash',
+ 'dash': 'dash',
+ 'dash_dot': 'dashDot',
+ 'long_dash': 'lgDash',
+ 'long_dash_dot': 'lgDashDot',
+ 'long_dash_dot_dot': 'lgDashDotDot',
+ 'dot': 'dot',
+ 'system_dash_dot': 'sysDashDot',
+ 'system_dash_dot_dot': 'sysDashDotDot',
+ }
+
+ # Check the dash type.
+ dash_type = line.get('dash_type')
+
+ if dash_type is not None:
+ if dash_type in dash_types:
+ line['dash_type'] = dash_types[dash_type]
+ else:
+ warn("Unknown dash type '%s'" % dash_type)
+ return
+
+ line['defined'] = True
+
+ return line
+
+ @staticmethod
+ def _get_fill_properties(fill):
+ # Convert user fill properties to the structure required internally.
+
+ if not fill:
+ return {'defined': False}
+
+ # Copy the user defined properties since they will be modified.
+ fill = copy.deepcopy(fill)
+
+ fill['defined'] = True
+
+ return fill
+
+ @staticmethod
+ def _get_pattern_properties(pattern):
+ # Convert user defined pattern to the structure required internally.
+
+ if not pattern:
+ return
+
+ # Copy the user defined properties since they will be modified.
+ pattern = copy.deepcopy(pattern)
+
+ if not pattern.get('pattern'):
+ warn("Pattern must include 'pattern'")
+ return
+
+ if not pattern.get('fg_color'):
+ warn("Pattern must include 'fg_color'")
+ return
+
+ types = {
+ 'percent_5': 'pct5',
+ 'percent_10': 'pct10',
+ 'percent_20': 'pct20',
+ 'percent_25': 'pct25',
+ 'percent_30': 'pct30',
+ 'percent_40': 'pct40',
+ 'percent_50': 'pct50',
+ 'percent_60': 'pct60',
+ 'percent_70': 'pct70',
+ 'percent_75': 'pct75',
+ 'percent_80': 'pct80',
+ 'percent_90': 'pct90',
+ 'light_downward_diagonal': 'ltDnDiag',
+ 'light_upward_diagonal': 'ltUpDiag',
+ 'dark_downward_diagonal': 'dkDnDiag',
+ 'dark_upward_diagonal': 'dkUpDiag',
+ 'wide_downward_diagonal': 'wdDnDiag',
+ 'wide_upward_diagonal': 'wdUpDiag',
+ 'light_vertical': 'ltVert',
+ 'light_horizontal': 'ltHorz',
+ 'narrow_vertical': 'narVert',
+ 'narrow_horizontal': 'narHorz',
+ 'dark_vertical': 'dkVert',
+ 'dark_horizontal': 'dkHorz',
+ 'dashed_downward_diagonal': 'dashDnDiag',
+ 'dashed_upward_diagonal': 'dashUpDiag',
+ 'dashed_horizontal': 'dashHorz',
+ 'dashed_vertical': 'dashVert',
+ 'small_confetti': 'smConfetti',
+ 'large_confetti': 'lgConfetti',
+ 'zigzag': 'zigZag',
+ 'wave': 'wave',
+ 'diagonal_brick': 'diagBrick',
+ 'horizontal_brick': 'horzBrick',
+ 'weave': 'weave',
+ 'plaid': 'plaid',
+ 'divot': 'divot',
+ 'dotted_grid': 'dotGrid',
+ 'dotted_diamond': 'dotDmnd',
+ 'shingle': 'shingle',
+ 'trellis': 'trellis',
+ 'sphere': 'sphere',
+ 'small_grid': 'smGrid',
+ 'large_grid': 'lgGrid',
+ 'small_check': 'smCheck',
+ 'large_check': 'lgCheck',
+ 'outlined_diamond': 'openDmnd',
+ 'solid_diamond': 'solidDmnd',
+ }
+
+ # Check for valid types.
+ if not pattern['pattern'] in types:
+ warn("unknown pattern type '%s'" % pattern['pattern'])
+ return
+ else:
+ pattern['pattern'] = types[pattern['pattern']]
+
+ # Specify a default background color.
+ pattern['bg_color'] = pattern.get('bg_color', '#FFFFFF')
+
+ return pattern
+
+ @staticmethod
+ def _get_gradient_properties(gradient):
+ # Convert user defined gradient to the structure required internally.
+
+ if not gradient:
+ return
+
+ # Copy the user defined properties since they will be modified.
+ gradient = copy.deepcopy(gradient)
+
+ types = {
+ 'linear': 'linear',
+ 'radial': 'circle',
+ 'rectangular': 'rect',
+ 'path': 'shape'
+ }
+
+ # Check the colors array exists and is valid.
+ if 'colors' not in gradient or type(gradient['colors']) != list:
+ warn("Gradient must include colors list")
+ return
+
+ # Check the colors array has the required number of entries.
+ if not 2 <= len(gradient['colors']) <= 10:
+ warn("Gradient colors list must at least 2 values "
+ "and not more than 10")
+ return
+
+ if 'positions' in gradient:
+ # Check the positions array has the right number of entries.
+ if len(gradient['positions']) != len(gradient['colors']):
+ warn("Gradient positions not equal to number of colors")
+ return
+
+ # Check the positions are in the correct range.
+ for pos in gradient['positions']:
+ if not 0 <= pos <= 100:
+ warn("Gradient position must be in the range "
+ "0 <= position <= 100")
+ return
+ else:
+ # Use the default gradient positions.
+ if len(gradient['colors']) == 2:
+ gradient['positions'] = [0, 100]
+
+ elif len(gradient['colors']) == 3:
+ gradient['positions'] = [0, 50, 100]
+
+ elif len(gradient['colors']) == 4:
+ gradient['positions'] = [0, 33, 66, 100]
+
+ else:
+ warn("Must specify gradient positions")
+ return
+
+ angle = gradient.get('angle')
+ if angle:
+ if not 0 <= angle < 360:
+ warn("Gradient angle must be in the range "
+ "0 <= angle < 360")
+ return
+ else:
+ gradient['angle'] = 90
+
+ # Check for valid types.
+ gradient_type = gradient.get('type')
+
+ if gradient_type is not None:
+
+ if gradient_type in types:
+ gradient['type'] = types[gradient_type]
+ else:
+ warn("Unknown gradient type '%s" % gradient_type)
+ return
+ else:
+ gradient['type'] = 'linear'
+
+ return gradient
+
+ @staticmethod
+ def _get_font_properties(options):
+ # Convert user defined font values into private dict values.
+ if options is None:
+ options = {}
+
+ font = {
+ 'name': options.get('name'),
+ 'color': options.get('color'),
+ 'size': options.get('size', 11),
+ 'bold': options.get('bold'),
+ 'italic': options.get('italic'),
+ 'underline': options.get('underline'),
+ 'pitch_family': options.get('pitch_family'),
+ 'charset': options.get('charset'),
+ 'baseline': options.get('baseline', -1),
+ 'rotation': options.get('rotation'),
+ 'lang': options.get('lang', 'en-US'),
+ }
+
+ # Convert font size units.
+ if font['size']:
+ font['size'] = int(font['size'] * 100)
+
+ # Convert rotation into 60,000ths of a degree.
+ if font['rotation']:
+ font['rotation'] = 60000 * int(font['rotation'])
+
+ return font
+
+ @staticmethod
+ def _get_font_style_attributes(font):
+ # _get_font_style_attributes.
+ attributes = []
+
+ if not font:
+ return attributes
+
+ if font.get('size'):
+ attributes.append(('sz', font['size']))
+
+ if font.get('bold') is not None:
+ attributes.append(('b', 0 + font['bold']))
+
+ if font.get('italic') is not None:
+ attributes.append(('i', 0 + font['italic']))
+
+ if font.get('underline') is not None:
+ attributes.append(('u', 'sng'))
+
+ if font.get('baseline') != -1:
+ attributes.append(('baseline', font['baseline']))
+
+ return attributes
+
+ @staticmethod
+ def _get_font_latin_attributes(font):
+ # _get_font_latin_attributes.
+ attributes = []
+
+ if not font:
+ return attributes
+
+ if font['name'] is not None:
+ attributes.append(('typeface', font['name']))
+
+ if font['pitch_family'] is not None:
+ attributes.append(('pitchFamily', font['pitch_family']))
+
+ if font['charset'] is not None:
+ attributes.append(('charset', font['charset']))
+
+ return attributes
+
+ @staticmethod
+ def _get_align_properties(align):
+ # Convert user defined align to the structure required internally.
+ if not align:
+ return {'defined': False}
+
+ # Copy the user defined properties since they will be modified.
+ align = copy.deepcopy(align)
+
+ if 'vertical' in align:
+ align_type = align['vertical']
+
+ align_types = {
+ 'top': 'top',
+ 'middle': 'middle',
+ 'bottom': 'bottom',
+ }
+
+ if align_type in align_types:
+ align['vertical'] = align_types[align_type]
+ else:
+ warn("Unknown alignment type '%s'" % align_type)
+ return {'defined': False}
+
+ if 'horizontal' in align:
+ align_type = align['horizontal']
+
+ align_types = {
+ 'left': 'left',
+ 'center': 'center',
+ 'right': 'right',
+ }
+
+ if align_type in align_types:
+ align['horizontal'] = align_types[align_type]
+ else:
+ warn("Unknown alignment type '%s'" % align_type)
+ return {'defined': False}
+
+ align['defined'] = True
+
+ return align
diff --git a/xlsxwriter/sharedstrings.py b/xlsxwriter/sharedstrings.py
new file mode 100644
index 0000000..c19b722
--- /dev/null
+++ b/xlsxwriter/sharedstrings.py
@@ -0,0 +1,153 @@
+###############################################################################
+#
+# SharedStrings - A class for writing the Excel XLSX sharedStrings file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Standard packages.
+import re
+
+# Package imports.
+from . import xmlwriter
+
+
+class SharedStrings(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX sharedStrings file.
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(SharedStrings, self).__init__()
+
+ self.string_table = None
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ # Write the sst element.
+ self._write_sst()
+
+ # Write the sst strings.
+ self._write_sst_strings()
+
+ # Close the sst tag.
+ self._xml_end_tag('sst')
+
+ # Close the file.
+ self._xml_close()
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_sst(self):
+ # Write the <sst> element.
+ xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
+
+ attributes = [
+ ('xmlns', xmlns),
+ ('count', self.string_table.count),
+ ('uniqueCount', self.string_table.unique_count),
+ ]
+
+ self._xml_start_tag('sst', attributes)
+
+ def _write_sst_strings(self):
+ # Write the sst string elements.
+
+ for string in (self.string_table._get_strings()):
+ self._write_si(string)
+
+ def _write_si(self, string):
+ # Write the <si> element.
+ attributes = []
+
+ # Excel escapes control characters with _xHHHH_ and also escapes any
+ # literal strings of that type by encoding the leading underscore.
+ # So "\0" -> _x0000_ and "_x0000_" -> _x005F_x0000_.
+ # The following substitutions deal with those cases.
+
+ # Escape the escape.
+ string = re.sub('(_x[0-9a-fA-F]{4}_)', r'_x005F\1', string)
+
+ # Convert control character to the _xHHHH_ escape.
+ string = re.sub(r'([\x00-\x08\x0B-\x1F])',
+ lambda match: "_x%04X_" %
+ ord(match.group(1)), string)
+
+ # Add attribute to preserve leading or trailing whitespace.
+ if re.search('^\s', string) or re.search('\s$', string):
+ attributes.append(('xml:space', 'preserve'))
+
+ # Write any rich strings without further tags.
+ if re.search('^<r>', string) and re.search('</r>$', string):
+ self._xml_rich_si_element(string)
+ else:
+ self._xml_si_element(string, attributes)
+
+
+# A metadata class to store Excel strings between worksheets.
+class SharedStringTable(object):
+ """
+ A class to track Excel shared strings between worksheets.
+
+ """
+
+ def __init__(self):
+ self.count = 0
+ self.unique_count = 0
+ self.string_table = {}
+ self.string_array = []
+
+ def _get_shared_string_index(self, string):
+ """" Get the index of the string in the Shared String table. """
+ if string not in self.string_table:
+ # String isn't already stored in the table so add it.
+ index = self.unique_count
+ self.string_table[string] = index
+ self.count += 1
+ self.unique_count += 1
+ return index
+ else:
+ # String exists in the table.
+ index = self.string_table[string]
+ self.count += 1
+ return index
+
+ def _get_shared_string(self, index):
+ """" Get a shared string from the index. """
+ return self.string_array[index]
+
+ def _sort_string_data(self):
+ """" Sort the shared string data and convert from dict to list. """
+ self.string_array = sorted(self.string_table,
+ key=self.string_table.__getitem__)
+ self.string_table = {}
+
+ def _get_strings(self):
+ """" Return the sorted string list. """
+ return self.string_array
diff --git a/xlsxwriter/styles.py b/xlsxwriter/styles.py
new file mode 100644
index 0000000..e6bd545
--- /dev/null
+++ b/xlsxwriter/styles.py
@@ -0,0 +1,700 @@
+###############################################################################
+#
+# Styles - A class for writing the Excel XLSX Worksheet file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Package imports.
+from . import xmlwriter
+
+
+class Styles(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX Styles file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(Styles, self).__init__()
+
+ self.xf_formats = []
+ self.palette = []
+ self.font_count = 0
+ self.num_format_count = 0
+ self.border_count = 0
+ self.fill_count = 0
+ self.custom_colors = []
+ self.dxf_formats = []
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ # Add the style sheet.
+ self._write_style_sheet()
+
+ # Write the number formats.
+ self._write_num_fmts()
+
+ # Write the fonts.
+ self._write_fonts()
+
+ # Write the fills.
+ self._write_fills()
+
+ # Write the borders element.
+ self._write_borders()
+
+ # Write the cellStyleXfs element.
+ self._write_cell_style_xfs()
+
+ # Write the cellXfs element.
+ self._write_cell_xfs()
+
+ # Write the cellStyles element.
+ self._write_cell_styles()
+
+ # Write the dxfs element.
+ self._write_dxfs()
+
+ # Write the tableStyles element.
+ self._write_table_styles()
+
+ # Write the colors element.
+ self._write_colors()
+
+ # Close the style sheet tag.
+ self._xml_end_tag('styleSheet')
+
+ # Close the file.
+ self._xml_close()
+
+ def _set_style_properties(self, properties):
+ # Pass in the Format objects and other properties used in the styles.
+
+ self.xf_formats = properties[0]
+ self.palette = properties[1]
+ self.font_count = properties[2]
+ self.num_format_count = properties[3]
+ self.border_count = properties[4]
+ self.fill_count = properties[5]
+ self.custom_colors = properties[6]
+ self.dxf_formats = properties[7]
+
+ def _get_palette_color(self, color):
+ # Convert the RGB color.
+ if color[0] == '#':
+ color = color[1:]
+
+ return "FF" + color.upper()
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_style_sheet(self):
+ # Write the <styleSheet> element.
+ xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
+
+ attributes = [('xmlns', xmlns)]
+ self._xml_start_tag('styleSheet', attributes)
+
+ def _write_num_fmts(self):
+ # Write the <numFmts> element.
+ if not self.num_format_count:
+ return
+
+ attributes = [('count', self.num_format_count)]
+ self._xml_start_tag('numFmts', attributes)
+
+ # Write the numFmts elements.
+ for xf_format in self.xf_formats:
+ # Ignore built-in number formats, i.e., < 164.
+ if xf_format.num_format_index >= 164:
+ self._write_num_fmt(xf_format.num_format_index,
+ xf_format.num_format)
+
+ self._xml_end_tag('numFmts')
+
+ def _write_num_fmt(self, num_fmt_id, format_code):
+ # Write the <numFmt> element.
+ format_codes = {
+ 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: 'm/d/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: '_(* #,##0_);_(* (#,##0);_(* "-"_);_(_)',
+ 42: '_($* #,##0_);_($* (#,##0);_($* "-"_);_(_)',
+ 43: '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(_)',
+ 44: '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(_)',
+ 45: 'mm:ss',
+ 46: '[h]:mm:ss',
+ 47: 'mm:ss.0',
+ 48: '##0.0E+0',
+ 49: '@'}
+
+ # Set the format code for built-in number formats.
+ if num_fmt_id < 164:
+ if num_fmt_id in format_codes:
+ format_code = format_codes[num_fmt_id]
+ else:
+ format_code = 'General'
+
+ attributes = [
+ ('numFmtId', num_fmt_id),
+ ('formatCode', format_code),
+ ]
+
+ self._xml_empty_tag('numFmt', attributes)
+
+ def _write_fonts(self):
+ # Write the <fonts> element.
+ attributes = [('count', self.font_count)]
+ self._xml_start_tag('fonts', attributes)
+
+ # Write the font elements for xf_format objects that have them.
+ for xf_format in self.xf_formats:
+ if xf_format.has_font:
+ self._write_font(xf_format)
+
+ self._xml_end_tag('fonts')
+
+ def _write_font(self, xf_format, is_dxf_format=False):
+ # Write the <font> element.
+ self._xml_start_tag('font')
+
+ # The condense and extend elements are mainly used in dxf formats.
+ if xf_format.font_condense:
+ self._write_condense()
+
+ if xf_format.font_extend:
+ self._write_extend()
+
+ if xf_format.bold:
+ self._xml_empty_tag('b')
+
+ if xf_format.italic:
+ self._xml_empty_tag('i')
+
+ if xf_format.font_strikeout:
+ self._xml_empty_tag('strike')
+
+ if xf_format.font_outline:
+ self._xml_empty_tag('outline')
+
+ if xf_format.font_shadow:
+ self._xml_empty_tag('shadow')
+
+ # Handle the underline variants.
+ if xf_format.underline:
+ self._write_underline(xf_format.underline)
+
+ if xf_format.font_script == 1:
+ self._write_vert_align('superscript')
+
+ if xf_format.font_script == 2:
+ self._write_vert_align('subscript')
+
+ if not is_dxf_format:
+ self._xml_empty_tag('sz', [('val', xf_format.font_size)])
+
+ if xf_format.theme == -1:
+ # Ignore for excel2003_style.
+ pass
+ elif xf_format.theme:
+ self._write_color('theme', xf_format.theme)
+ elif xf_format.color_indexed:
+ self._write_color('indexed', xf_format.color_indexed)
+ elif xf_format.font_color:
+ color = self._get_palette_color(xf_format.font_color)
+ self._write_color('rgb', color)
+ elif not is_dxf_format:
+ self._write_color('theme', 1)
+
+ if not is_dxf_format:
+ self._xml_empty_tag('name', [('val', xf_format.font_name)])
+
+ if xf_format.font_family:
+ self._xml_empty_tag('family', [('val', xf_format.font_family)])
+
+ if xf_format.font_charset:
+ self._xml_empty_tag('charset',
+ [('val', xf_format.font_charset)])
+
+ if xf_format.font_name == 'Calibri' and not xf_format.hyperlink:
+ self._xml_empty_tag(
+ 'scheme',
+ [('val', xf_format.font_scheme)])
+
+ self._xml_end_tag('font')
+
+ def _write_underline(self, underline):
+ # Write the underline font element.
+
+ if underline == 2:
+ attributes = [('val', 'double')]
+ elif underline == 33:
+ attributes = [('val', 'singleAccounting')]
+ elif underline == 34:
+ attributes = [('val', 'doubleAccounting')]
+ else:
+ # Default to single underline.
+ attributes = []
+
+ self._xml_empty_tag('u', attributes)
+
+ def _write_vert_align(self, val):
+ # Write the <vertAlign> font sub-element.
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('vertAlign', attributes)
+
+ def _write_color(self, name, value):
+ # Write the <color> element.
+ attributes = [(name, value)]
+
+ self._xml_empty_tag('color', attributes)
+
+ def _write_fills(self):
+ # Write the <fills> element.
+ attributes = [('count', self.fill_count)]
+
+ self._xml_start_tag('fills', attributes)
+
+ # Write the default fill element.
+ self._write_default_fill('none')
+ self._write_default_fill('gray125')
+
+ # Write the fill elements for xf_format objects that have them.
+ for xf_format in self.xf_formats:
+ if xf_format.has_fill:
+ self._write_fill(xf_format)
+
+ self._xml_end_tag('fills')
+
+ def _write_default_fill(self, pattern_type):
+ # Write the <fill> element for the default fills.
+ self._xml_start_tag('fill')
+ self._xml_empty_tag('patternFill', [('patternType', pattern_type)])
+ self._xml_end_tag('fill')
+
+ def _write_fill(self, xf_format, is_dxf_format=False):
+ # Write the <fill> element.
+ pattern = xf_format.pattern
+ bg_color = xf_format.bg_color
+ fg_color = xf_format.fg_color
+
+ # Colors for dxf formats are handled differently from normal formats
+ # since the normal xf_format reverses the meaning of BG and FG for
+ # solid fills.
+ if is_dxf_format:
+ bg_color = xf_format.dxf_bg_color
+ fg_color = xf_format.dxf_fg_color
+
+ patterns = (
+ 'none',
+ 'solid',
+ 'mediumGray',
+ 'darkGray',
+ 'lightGray',
+ 'darkHorizontal',
+ 'darkVertical',
+ 'darkDown',
+ 'darkUp',
+ 'darkGrid',
+ 'darkTrellis',
+ 'lightHorizontal',
+ 'lightVertical',
+ 'lightDown',
+ 'lightUp',
+ 'lightGrid',
+ 'lightTrellis',
+ 'gray125',
+ 'gray0625',
+ )
+
+ self._xml_start_tag('fill')
+
+ # The "none" pattern is handled differently for dxf formats.
+ if is_dxf_format and pattern <= 1:
+ self._xml_start_tag('patternFill')
+ else:
+ self._xml_start_tag(
+ 'patternFill',
+ [('patternType', patterns[pattern])])
+
+ if fg_color:
+ fg_color = self._get_palette_color(fg_color)
+ self._xml_empty_tag('fgColor', [('rgb', fg_color)])
+
+ if bg_color:
+ bg_color = self._get_palette_color(bg_color)
+ self._xml_empty_tag('bgColor', [('rgb', bg_color)])
+ else:
+ if not is_dxf_format:
+ self._xml_empty_tag('bgColor', [('indexed', 64)])
+
+ self._xml_end_tag('patternFill')
+ self._xml_end_tag('fill')
+
+ def _write_borders(self):
+ # Write the <borders> element.
+ attributes = [('count', self.border_count)]
+
+ self._xml_start_tag('borders', attributes)
+
+ # Write the border elements for xf_format objects that have them.
+ for xf_format in self.xf_formats:
+ if xf_format.has_border:
+ self._write_border(xf_format)
+
+ self._xml_end_tag('borders')
+
+ def _write_border(self, xf_format, is_dxf_format=False):
+ # Write the <border> element.
+ attributes = []
+
+ # Diagonal borders add attributes to the <border> element.
+ if xf_format.diag_type == 1:
+ attributes.append(('diagonalUp', 1))
+ elif xf_format.diag_type == 2:
+ attributes.append(('diagonalDown', 1))
+ elif xf_format.diag_type == 3:
+ attributes.append(('diagonalUp', 1))
+ attributes.append(('diagonalDown', 1))
+
+ # Ensure that a default diag border is set if the diag type is set.
+ if xf_format.diag_type and not xf_format.diag_border:
+ xf_format.diag_border = 1
+
+ # Write the start border tag.
+ self._xml_start_tag('border', attributes)
+
+ # Write the <border> sub elements.
+ self._write_sub_border(
+ 'left',
+ xf_format.left,
+ xf_format.left_color)
+
+ self._write_sub_border(
+ 'right',
+ xf_format.right,
+ xf_format.right_color)
+
+ self._write_sub_border(
+ 'top',
+ xf_format.top,
+ xf_format.top_color)
+
+ self._write_sub_border(
+ 'bottom',
+ xf_format.bottom,
+ xf_format.bottom_color)
+
+ # Condition DXF formats don't allow diagonal borders.
+ if not is_dxf_format:
+ self._write_sub_border(
+ 'diagonal',
+ xf_format.diag_border,
+ xf_format.diag_color)
+
+ if is_dxf_format:
+ self._write_sub_border('vertical', None, None)
+ self._write_sub_border('horizontal', None, None)
+
+ self._xml_end_tag('border')
+
+ def _write_sub_border(self, border_type, style, color):
+ # Write the <border> sub elements such as <right>, <top>, etc.
+ attributes = []
+
+ if not style:
+ self._xml_empty_tag(border_type)
+ return
+
+ border_styles = (
+ 'none',
+ 'thin',
+ 'medium',
+ 'dashed',
+ 'dotted',
+ 'thick',
+ 'double',
+ 'hair',
+ 'mediumDashed',
+ 'dashDot',
+ 'mediumDashDot',
+ 'dashDotDot',
+ 'mediumDashDotDot',
+ 'slantDashDot',
+ )
+
+ attributes.append(('style', border_styles[style]))
+
+ self._xml_start_tag(border_type, attributes)
+
+ if color:
+ color = self._get_palette_color(color)
+ self._xml_empty_tag('color', [('rgb', color)])
+ else:
+ self._xml_empty_tag('color', [('auto', 1)])
+
+ self._xml_end_tag(border_type)
+
+ def _write_cell_style_xfs(self):
+ # Write the <cellStyleXfs> element.
+ attributes = [('count', 1)]
+
+ self._xml_start_tag('cellStyleXfs', attributes)
+ self._write_style_xf()
+ self._xml_end_tag('cellStyleXfs')
+
+ def _write_cell_xfs(self):
+ # Write the <cellXfs> element.
+ formats = self.xf_formats
+
+ # Workaround for when the last xf_format is used for the comment font
+ # and shouldn't be used for cellXfs.
+ last_format = formats[-1]
+ if last_format.font_only:
+ formats.pop()
+
+ attributes = [('count', len(formats))]
+ self._xml_start_tag('cellXfs', attributes)
+
+ # Write the xf elements.
+ for xf_format in formats:
+ self._write_xf(xf_format)
+
+ self._xml_end_tag('cellXfs')
+
+ def _write_style_xf(self):
+ # Write the style <xf> element.
+ num_fmt_id = 0
+ font_id = 0
+ fill_id = 0
+ border_id = 0
+
+ attributes = [
+ ('numFmtId', num_fmt_id),
+ ('fontId', font_id),
+ ('fillId', fill_id),
+ ('borderId', border_id),
+ ]
+
+ self._xml_empty_tag('xf', attributes)
+
+ def _write_xf(self, xf_format):
+ # Write the <xf> element.
+ num_fmt_id = xf_format.num_format_index
+ font_id = xf_format.font_index
+ fill_id = xf_format.fill_index
+ border_id = xf_format.border_index
+ xf_id = 0
+ has_align = 0
+ has_protect = 0
+
+ attributes = [
+ ('numFmtId', num_fmt_id),
+ ('fontId', font_id),
+ ('fillId', fill_id),
+ ('borderId', border_id),
+ ('xfId', xf_id),
+ ]
+
+ if xf_format.num_format_index > 0:
+ attributes.append(('applyNumberFormat', 1))
+
+ # Add applyFont attribute if XF format uses a font element.
+ if xf_format.font_index > 0:
+ attributes.append(('applyFont', 1))
+
+ # Add applyFill attribute if XF format uses a fill element.
+ if xf_format.fill_index > 0:
+ attributes.append(('applyFill', 1))
+
+ # Add applyBorder attribute if XF format uses a border element.
+ if xf_format.border_index > 0:
+ attributes.append(('applyBorder', 1))
+
+ # Check if XF format has alignment properties set.
+ (apply_align, align) = xf_format._get_align_properties()
+
+ # Check if an alignment sub-element should be written.
+ if apply_align and align:
+ has_align = 1
+
+ # We can also have applyAlignment without a sub-element.
+ if apply_align:
+ attributes.append(('applyAlignment', 1))
+
+ # Check for cell protection properties.
+ protection = xf_format._get_protection_properties()
+
+ if protection:
+ attributes.append(('applyProtection', 1))
+ has_protect = 1
+
+ # Write XF with sub-elements if required.
+ if has_align or has_protect:
+ self._xml_start_tag('xf', attributes)
+ if has_align:
+ self._xml_empty_tag('alignment', align)
+ if has_protect:
+ self._xml_empty_tag('protection', protection)
+ self._xml_end_tag('xf')
+ else:
+ self._xml_empty_tag('xf', attributes)
+
+ def _write_cell_styles(self):
+ # Write the <cellStyles> element.
+ attributes = [('count', 1)]
+
+ self._xml_start_tag('cellStyles', attributes)
+ self._write_cell_style()
+ self._xml_end_tag('cellStyles')
+
+ def _write_cell_style(self):
+ # Write the <cellStyle> element.
+ name = 'Normal'
+ xf_id = 0
+ builtin_id = 0
+
+ attributes = [
+ ('name', name),
+ ('xfId', xf_id),
+ ('builtinId', builtin_id),
+ ]
+
+ self._xml_empty_tag('cellStyle', attributes)
+
+ def _write_dxfs(self):
+ # Write the <dxfs> element.
+ formats = self.dxf_formats
+ count = len(formats)
+
+ attributes = [('count', len(formats))]
+
+ if count:
+ self._xml_start_tag('dxfs', attributes)
+
+ # Write the font elements for xf_format objects that have them.
+ for xf_format in self.dxf_formats:
+ self._xml_start_tag('dxf')
+ if xf_format.has_dxf_font:
+ self._write_font(xf_format, True)
+
+ if xf_format.num_format_index:
+ self._write_num_fmt(xf_format.num_format_index,
+ xf_format.num_format)
+
+ if xf_format.has_dxf_fill:
+ self._write_fill(xf_format, True)
+ if xf_format.has_dxf_border:
+ self._write_border(xf_format, True)
+ self._xml_end_tag('dxf')
+
+ self._xml_end_tag('dxfs')
+ else:
+ self._xml_empty_tag('dxfs', attributes)
+
+ def _write_table_styles(self):
+ # Write the <tableStyles> element.
+ count = 0
+ default_table_style = 'TableStyleMedium9'
+ default_pivot_style = 'PivotStyleLight16'
+
+ attributes = [
+ ('count', count),
+ ('defaultTableStyle', default_table_style),
+ ('defaultPivotStyle', default_pivot_style),
+ ]
+
+ self._xml_empty_tag('tableStyles', attributes)
+
+ def _write_colors(self):
+ # Write the <colors> element.
+ custom_colors = self.custom_colors
+
+ if not custom_colors:
+ return
+
+ self._xml_start_tag('colors')
+ self._write_mru_colors(custom_colors)
+ self._xml_end_tag('colors')
+
+ def _write_mru_colors(self, custom_colors):
+ # Write the <mruColors> element for the most recently used colors.
+
+ # Write the custom custom_colors in reverse order.
+ custom_colors.reverse()
+
+ # Limit the mruColors to the last 10.
+ if len(custom_colors) > 10:
+ custom_colors = custom_colors[0:10]
+
+ self._xml_start_tag('mruColors')
+
+ # Write the custom custom_colors in reverse order.
+ for color in custom_colors:
+ self._write_color('rgb', color)
+
+ self._xml_end_tag('mruColors')
+
+ def _write_condense(self):
+ # Write the <condense> element.
+ attributes = [('val', 0)]
+
+ self._xml_empty_tag('condense', attributes)
+
+ def _write_extend(self):
+ # Write the <extend> element.
+ attributes = [('val', 0)]
+
+ self._xml_empty_tag('extend', attributes)
diff --git a/xlsxwriter/table.py b/xlsxwriter/table.py
new file mode 100644
index 0000000..77d7ce2
--- /dev/null
+++ b/xlsxwriter/table.py
@@ -0,0 +1,178 @@
+###############################################################################
+#
+# Table - A class for writing the Excel XLSX Worksheet file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from . import xmlwriter
+
+
+class Table(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX Table file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(Table, self).__init__()
+
+ self.properties = {}
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ # Write the table element.
+ self._write_table()
+
+ # Write the autoFilter element.
+ self._write_auto_filter()
+
+ # Write the tableColumns element.
+ self._write_table_columns()
+
+ # Write the tableStyleInfo element.
+ self._write_table_style_info()
+
+ # Close the table tag.
+ self._xml_end_tag('table')
+
+ # Close the file.
+ self._xml_close()
+
+ def _set_properties(self, properties):
+ # Set the document properties.
+ self.properties = properties
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_table(self):
+ # Write the <table> element.
+ schema = 'http://schemas.openxmlformats.org/'
+ xmlns = schema + 'spreadsheetml/2006/main'
+ table_id = self.properties['id']
+ name = self.properties['name']
+ display_name = self.properties['name']
+ ref = self.properties['range']
+ totals_row_shown = self.properties['totals_row_shown']
+ header_row_count = self.properties['header_row_count']
+
+ attributes = [
+ ('xmlns', xmlns),
+ ('id', table_id),
+ ('name', name),
+ ('displayName', display_name),
+ ('ref', ref),
+ ]
+
+ if not header_row_count:
+ attributes.append(('headerRowCount', 0))
+
+ if totals_row_shown:
+ attributes.append(('totalsRowCount', 1))
+ else:
+ attributes.append(('totalsRowShown', 0))
+
+ self._xml_start_tag('table', attributes)
+
+ def _write_auto_filter(self):
+ # Write the <autoFilter> element.
+ autofilter = self.properties.get('autofilter', 0)
+
+ if not autofilter:
+ return
+
+ attributes = [('ref', autofilter,)]
+
+ self._xml_empty_tag('autoFilter', attributes)
+
+ def _write_table_columns(self):
+ # Write the <tableColumns> element.
+ columns = self.properties['columns']
+
+ count = len(columns)
+
+ attributes = [('count', count)]
+
+ self._xml_start_tag('tableColumns', attributes)
+
+ for col_data in columns:
+ # Write the tableColumn element.
+ self._write_table_column(col_data)
+
+ self._xml_end_tag('tableColumns')
+
+ def _write_table_column(self, col_data):
+ # Write the <tableColumn> element.
+ attributes = [
+ ('id', col_data['id']),
+ ('name', col_data['name']),
+ ]
+
+ if col_data.get('total_string'):
+ attributes.append(('totalsRowLabel', col_data['total_string']))
+ elif col_data.get('total_function'):
+ attributes.append(('totalsRowFunction',
+ col_data['total_function']))
+
+ if 'format' in col_data and col_data['format'] is not None:
+ attributes.append(('dataDxfId', col_data['format']))
+
+ if col_data.get('formula'):
+ self._xml_start_tag('tableColumn', attributes)
+
+ # Write the calculatedColumnFormula element.
+ self._write_calculated_column_formula(col_data['formula'])
+
+ self._xml_end_tag('tableColumn')
+ else:
+ self._xml_empty_tag('tableColumn', attributes)
+
+ def _write_table_style_info(self):
+ # Write the <tableStyleInfo> element.
+ props = self.properties
+
+ name = props['style']
+ show_first_column = 0 + props['show_first_col']
+ show_last_column = 0 + props['show_last_col']
+ show_row_stripes = 0 + props['show_row_stripes']
+ show_column_stripes = 0 + props['show_col_stripes']
+
+ attributes = [
+ ('name', name),
+ ('showFirstColumn', show_first_column),
+ ('showLastColumn', show_last_column),
+ ('showRowStripes', show_row_stripes),
+ ('showColumnStripes', show_column_stripes),
+ ]
+
+ self._xml_empty_tag('tableStyleInfo', attributes)
+
+ def _write_calculated_column_formula(self, formula):
+ # Write the <calculatedColumnFormula> element.
+ self._xml_data_element('calculatedColumnFormula', formula)
diff --git a/xlsxwriter/test/__init__.py b/xlsxwriter/test/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/app/__init__.py b/xlsxwriter/test/app/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/app/test_app01.py b/xlsxwriter/test/app/test_app01.py
new file mode 100644
index 0000000..9d7a88c
--- /dev/null
+++ b/xlsxwriter/test/app/test_app01.py
@@ -0,0 +1,64 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...app import App
+
+
+class TestAssembleApp(unittest.TestCase):
+ """
+ Test assembling a complete App file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing an App file."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ app = App()
+ app._set_filehandle(fh)
+
+ app._add_part_name('Sheet1')
+ app._add_heading_pair(('Worksheets', 1))
+
+ app._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
+ <Application>Microsoft Excel</Application>
+ <DocSecurity>0</DocSecurity>
+ <ScaleCrop>false</ScaleCrop>
+ <HeadingPairs>
+ <vt:vector size="2" baseType="variant">
+ <vt:variant>
+ <vt:lpstr>Worksheets</vt:lpstr>
+ </vt:variant>
+ <vt:variant>
+ <vt:i4>1</vt:i4>
+ </vt:variant>
+ </vt:vector>
+ </HeadingPairs>
+ <TitlesOfParts>
+ <vt:vector size="1" baseType="lpstr">
+ <vt:lpstr>Sheet1</vt:lpstr>
+ </vt:vector>
+ </TitlesOfParts>
+ <Company>
+ </Company>
+ <LinksUpToDate>false</LinksUpToDate>
+ <SharedDoc>false</SharedDoc>
+ <HyperlinksChanged>false</HyperlinksChanged>
+ <AppVersion>12.0000</AppVersion>
+ </Properties>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/app/test_app02.py b/xlsxwriter/test/app/test_app02.py
new file mode 100644
index 0000000..5addd80
--- /dev/null
+++ b/xlsxwriter/test/app/test_app02.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...app import App
+
+
+class TestAssembleApp(unittest.TestCase):
+ """
+ Test assembling a complete App file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing an App file."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ app = App()
+ app._set_filehandle(fh)
+
+ app._add_part_name('Sheet1')
+ app._add_part_name('Sheet2')
+ app._add_heading_pair(('Worksheets', 2))
+
+ app._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
+ <Application>Microsoft Excel</Application>
+ <DocSecurity>0</DocSecurity>
+ <ScaleCrop>false</ScaleCrop>
+ <HeadingPairs>
+ <vt:vector size="2" baseType="variant">
+ <vt:variant>
+ <vt:lpstr>Worksheets</vt:lpstr>
+ </vt:variant>
+ <vt:variant>
+ <vt:i4>2</vt:i4>
+ </vt:variant>
+ </vt:vector>
+ </HeadingPairs>
+ <TitlesOfParts>
+ <vt:vector size="2" baseType="lpstr">
+ <vt:lpstr>Sheet1</vt:lpstr>
+ <vt:lpstr>Sheet2</vt:lpstr>
+ </vt:vector>
+ </TitlesOfParts>
+ <Company>
+ </Company>
+ <LinksUpToDate>false</LinksUpToDate>
+ <SharedDoc>false</SharedDoc>
+ <HyperlinksChanged>false</HyperlinksChanged>
+ <AppVersion>12.0000</AppVersion>
+ </Properties>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/app/test_app03.py b/xlsxwriter/test/app/test_app03.py
new file mode 100644
index 0000000..6991025
--- /dev/null
+++ b/xlsxwriter/test/app/test_app03.py
@@ -0,0 +1,73 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...app import App
+
+
+class TestAssembleApp(unittest.TestCase):
+ """
+ Test assembling a complete App file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing an App file."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ app = App()
+ app._set_filehandle(fh)
+
+ app._add_part_name('Sheet1')
+ app._add_part_name('Sheet1!Print_Titles')
+ app._add_heading_pair(('Worksheets', 1))
+ app._add_heading_pair(('Named Ranges', 1))
+
+ app._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
+ <Application>Microsoft Excel</Application>
+ <DocSecurity>0</DocSecurity>
+ <ScaleCrop>false</ScaleCrop>
+ <HeadingPairs>
+ <vt:vector size="4" baseType="variant">
+ <vt:variant>
+ <vt:lpstr>Worksheets</vt:lpstr>
+ </vt:variant>
+ <vt:variant>
+ <vt:i4>1</vt:i4>
+ </vt:variant>
+ <vt:variant>
+ <vt:lpstr>Named Ranges</vt:lpstr>
+ </vt:variant>
+ <vt:variant>
+ <vt:i4>1</vt:i4>
+ </vt:variant>
+ </vt:vector>
+ </HeadingPairs>
+ <TitlesOfParts>
+ <vt:vector size="2" baseType="lpstr">
+ <vt:lpstr>Sheet1</vt:lpstr>
+ <vt:lpstr>Sheet1!Print_Titles</vt:lpstr>
+ </vt:vector>
+ </TitlesOfParts>
+ <Company>
+ </Company>
+ <LinksUpToDate>false</LinksUpToDate>
+ <SharedDoc>false</SharedDoc>
+ <HyperlinksChanged>false</HyperlinksChanged>
+ <AppVersion>12.0000</AppVersion>
+ </Properties>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/app/test_initialisation.py b/xlsxwriter/test/app/test_initialisation.py
new file mode 100644
index 0000000..9200935
--- /dev/null
+++ b/xlsxwriter/test/app/test_initialisation.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...app import App
+
+
+class TestInitialisation(unittest.TestCase):
+ """
+ Test initialisation of the App class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.app = App()
+ self.app._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test App xml_declaration()"""
+
+ self.app._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/chart/__init__.py b/xlsxwriter/test/chart/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/chart/test_initialisation.py b/xlsxwriter/test/chart/test_initialisation.py
new file mode 100644
index 0000000..c99ed64
--- /dev/null
+++ b/xlsxwriter/test/chart/test_initialisation.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...chart import Chart
+
+
+class TestInitialisation(unittest.TestCase):
+ """
+ Test initialisation of the Chart class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.chart = Chart()
+ self.chart._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test Chart xml_declaration()"""
+
+ self.chart._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/chartsheet/__init__.py b/xlsxwriter/test/chartsheet/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/chartsheet/test_chartsheet01.py b/xlsxwriter/test/chartsheet/test_chartsheet01.py
new file mode 100644
index 0000000..bba6da7
--- /dev/null
+++ b/xlsxwriter/test/chartsheet/test_chartsheet01.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...chartsheet import Chartsheet
+
+
+class TestAssembleChartsheet(unittest.TestCase):
+ """
+ Test assembling a complete Chartsheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a chartsheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ chartsheet = Chartsheet()
+ chartsheet._set_filehandle(fh)
+
+ chartsheet.drawing = 1
+
+ chartsheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <chartsheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <sheetPr/>
+ <sheetViews>
+ <sheetView workbookViewId="0"/>
+ </sheetViews>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ <drawing r:id="rId1"/>
+ </chartsheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/chartsheet/test_initialisation.py b/xlsxwriter/test/chartsheet/test_initialisation.py
new file mode 100644
index 0000000..0e1e63f
--- /dev/null
+++ b/xlsxwriter/test/chartsheet/test_initialisation.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...chartsheet import Chartsheet
+
+
+class TestInitialisation(unittest.TestCase):
+ """
+ Test initialisation of the Chartsheet class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.chartsheet = Chartsheet()
+ self.chartsheet._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test Chartsheet xml_declaration()"""
+
+ self.chartsheet._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/comments/__init__.py b/xlsxwriter/test/comments/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/comments/test_comments01.py b/xlsxwriter/test/comments/test_comments01.py
new file mode 100644
index 0000000..d05e3b0
--- /dev/null
+++ b/xlsxwriter/test/comments/test_comments01.py
@@ -0,0 +1,55 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...comments import Comments
+
+
+class TestAssembleComments(unittest.TestCase):
+ """
+ Test assembling a complete Comments file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a comments with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ comments = Comments()
+ comments._set_filehandle(fh)
+
+ comments._assemble_xml_file([[1, 1, 'Some text', 'John', None, 81, [2, 0, 4, 4, 143, 10, 128, 74]]])
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <comments xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
+ <authors>
+ <author>John</author>
+ </authors>
+ <commentList>
+ <comment ref="B2" authorId="0">
+ <text>
+ <r>
+ <rPr>
+ <sz val="8"/>
+ <color indexed="81"/>
+ <rFont val="Tahoma"/>
+ <family val="2"/>
+ </rPr>
+ <t>Some text</t>
+ </r>
+ </text>
+ </comment>
+ </commentList>
+ </comments>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/comments/test_initialisation.py b/xlsxwriter/test/comments/test_initialisation.py
new file mode 100644
index 0000000..06fecad
--- /dev/null
+++ b/xlsxwriter/test/comments/test_initialisation.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...comments import Comments
+
+
+class TestInitialisation(unittest.TestCase):
+ """
+ Test initialisation of the Comments class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.comments = Comments()
+ self.comments._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test Comments xml_declaration()"""
+
+ self.comments._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/comments/test_write_text_t.py b/xlsxwriter/test/comments/test_write_text_t.py
new file mode 100644
index 0000000..64ca7ea
--- /dev/null
+++ b/xlsxwriter/test/comments/test_write_text_t.py
@@ -0,0 +1,72 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...comments import Comments
+
+
+class TestWriteText(unittest.TestCase):
+ """
+ Test the Comments _write_text_t() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.comments = Comments()
+ self.comments._set_filehandle(self.fh)
+
+ def test_write_text_t_1(self):
+ """Test the _write_text_t() method"""
+
+ self.comments._write_text_t('Some text')
+
+ exp = """<t>Some text</t>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_text_t_2(self):
+ """Test the _write_text_t() method"""
+
+ self.comments._write_text_t(' Some text')
+
+ exp = """<t xml:space="preserve"> Some text</t>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_text_t_3(self):
+ """Test the _write_text_t() method"""
+
+ self.comments._write_text_t('Some text ')
+
+ exp = """<t xml:space="preserve">Some text </t>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_text_t_4(self):
+ """Test the _write_text_t() method"""
+
+ self.comments._write_text_t(' Some text ')
+
+ exp = """<t xml:space="preserve"> Some text </t>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_text_t_5(self):
+ """Test the _write_text_t() method"""
+
+ self.comments._write_text_t("Some text\n")
+
+ exp = """<t xml:space="preserve">Some text\n</t>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/comparison/__init__.py b/xlsxwriter/test/comparison/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/comparison/images/black_150.jpg b/xlsxwriter/test/comparison/images/black_150.jpg
new file mode 100644
index 0000000..5210139
Binary files /dev/null and b/xlsxwriter/test/comparison/images/black_150.jpg differ
diff --git a/xlsxwriter/test/comparison/images/black_150.png b/xlsxwriter/test/comparison/images/black_150.png
new file mode 100644
index 0000000..68a3a3f
Binary files /dev/null and b/xlsxwriter/test/comparison/images/black_150.png differ
diff --git a/xlsxwriter/test/comparison/images/black_150e.png b/xlsxwriter/test/comparison/images/black_150e.png
new file mode 100644
index 0000000..b30176c
Binary files /dev/null and b/xlsxwriter/test/comparison/images/black_150e.png differ
diff --git a/xlsxwriter/test/comparison/images/black_300.jpg b/xlsxwriter/test/comparison/images/black_300.jpg
new file mode 100644
index 0000000..01796a0
Binary files /dev/null and b/xlsxwriter/test/comparison/images/black_300.jpg differ
diff --git a/xlsxwriter/test/comparison/images/black_300.png b/xlsxwriter/test/comparison/images/black_300.png
new file mode 100644
index 0000000..838d2c5
Binary files /dev/null and b/xlsxwriter/test/comparison/images/black_300.png differ
diff --git a/xlsxwriter/test/comparison/images/black_300e.png b/xlsxwriter/test/comparison/images/black_300e.png
new file mode 100644
index 0000000..62b5832
Binary files /dev/null and b/xlsxwriter/test/comparison/images/black_300e.png differ
diff --git a/xlsxwriter/test/comparison/images/black_72.jpg b/xlsxwriter/test/comparison/images/black_72.jpg
new file mode 100644
index 0000000..c2b3a2c
Binary files /dev/null and b/xlsxwriter/test/comparison/images/black_72.jpg differ
diff --git a/xlsxwriter/test/comparison/images/black_72.png b/xlsxwriter/test/comparison/images/black_72.png
new file mode 100644
index 0000000..b48f348
Binary files /dev/null and b/xlsxwriter/test/comparison/images/black_72.png differ
diff --git a/xlsxwriter/test/comparison/images/black_72e.png b/xlsxwriter/test/comparison/images/black_72e.png
new file mode 100644
index 0000000..89302ff
Binary files /dev/null and b/xlsxwriter/test/comparison/images/black_72e.png differ
diff --git a/xlsxwriter/test/comparison/images/black_96.jpg b/xlsxwriter/test/comparison/images/black_96.jpg
new file mode 100644
index 0000000..17d96a5
Binary files /dev/null and b/xlsxwriter/test/comparison/images/black_96.jpg differ
diff --git a/xlsxwriter/test/comparison/images/black_96.png b/xlsxwriter/test/comparison/images/black_96.png
new file mode 100644
index 0000000..d22c616
Binary files /dev/null and b/xlsxwriter/test/comparison/images/black_96.png differ
diff --git a/xlsxwriter/test/comparison/images/blue.jpg b/xlsxwriter/test/comparison/images/blue.jpg
new file mode 100644
index 0000000..da58cce
Binary files /dev/null and b/xlsxwriter/test/comparison/images/blue.jpg differ
diff --git a/xlsxwriter/test/comparison/images/blue.png b/xlsxwriter/test/comparison/images/blue.png
new file mode 100644
index 0000000..3252bab
Binary files /dev/null and b/xlsxwriter/test/comparison/images/blue.png differ
diff --git a/xlsxwriter/test/comparison/images/grey.jpg b/xlsxwriter/test/comparison/images/grey.jpg
new file mode 100644
index 0000000..42edf01
Binary files /dev/null and b/xlsxwriter/test/comparison/images/grey.jpg differ
diff --git a/xlsxwriter/test/comparison/images/grey.png b/xlsxwriter/test/comparison/images/grey.png
new file mode 100644
index 0000000..cea719c
Binary files /dev/null and b/xlsxwriter/test/comparison/images/grey.png differ
diff --git a/xlsxwriter/test/comparison/images/issue32.png b/xlsxwriter/test/comparison/images/issue32.png
new file mode 100644
index 0000000..abb3414
Binary files /dev/null and b/xlsxwriter/test/comparison/images/issue32.png differ
diff --git a/xlsxwriter/test/comparison/images/logo.png b/xlsxwriter/test/comparison/images/logo.png
new file mode 100644
index 0000000..ed01e53
Binary files /dev/null and b/xlsxwriter/test/comparison/images/logo.png differ
diff --git a/xlsxwriter/test/comparison/images/mylogo.png b/xlsxwriter/test/comparison/images/mylogo.png
new file mode 100644
index 0000000..c5fa1ac
Binary files /dev/null and b/xlsxwriter/test/comparison/images/mylogo.png differ
diff --git a/xlsxwriter/test/comparison/images/red.bmp b/xlsxwriter/test/comparison/images/red.bmp
new file mode 100644
index 0000000..6c0c3d0
Binary files /dev/null and b/xlsxwriter/test/comparison/images/red.bmp differ
diff --git a/xlsxwriter/test/comparison/images/red.jpg b/xlsxwriter/test/comparison/images/red.jpg
new file mode 100644
index 0000000..95105a4
Binary files /dev/null and b/xlsxwriter/test/comparison/images/red.jpg differ
diff --git a/xlsxwriter/test/comparison/images/red.png b/xlsxwriter/test/comparison/images/red.png
new file mode 100644
index 0000000..867a2f1
Binary files /dev/null and b/xlsxwriter/test/comparison/images/red.png differ
diff --git a/xlsxwriter/test/comparison/images/red_208.png b/xlsxwriter/test/comparison/images/red_208.png
new file mode 100644
index 0000000..92d9368
Binary files /dev/null and b/xlsxwriter/test/comparison/images/red_208.png differ
diff --git a/xlsxwriter/test/comparison/images/red_64x20.png b/xlsxwriter/test/comparison/images/red_64x20.png
new file mode 100644
index 0000000..a794251
Binary files /dev/null and b/xlsxwriter/test/comparison/images/red_64x20.png differ
diff --git a/xlsxwriter/test/comparison/images/red_readonly.png b/xlsxwriter/test/comparison/images/red_readonly.png
new file mode 100644
index 0000000..867a2f1
Binary files /dev/null and b/xlsxwriter/test/comparison/images/red_readonly.png differ
diff --git a/xlsxwriter/test/comparison/images/train.jpg b/xlsxwriter/test/comparison/images/train.jpg
new file mode 100644
index 0000000..9b25d8c
Binary files /dev/null and b/xlsxwriter/test/comparison/images/train.jpg differ
diff --git a/xlsxwriter/test/comparison/images/yellow.jpg b/xlsxwriter/test/comparison/images/yellow.jpg
new file mode 100644
index 0000000..04d7dab
Binary files /dev/null and b/xlsxwriter/test/comparison/images/yellow.jpg differ
diff --git a/xlsxwriter/test/comparison/images/yellow.png b/xlsxwriter/test/comparison/images/yellow.png
new file mode 100644
index 0000000..7bd9f83
Binary files /dev/null and b/xlsxwriter/test/comparison/images/yellow.png differ
diff --git a/xlsxwriter/test/comparison/images/zero_dpi.jpg b/xlsxwriter/test/comparison/images/zero_dpi.jpg
new file mode 100644
index 0000000..0c419b8
Binary files /dev/null and b/xlsxwriter/test/comparison/images/zero_dpi.jpg differ
diff --git a/xlsxwriter/test/comparison/test_array_formula01.py b/xlsxwriter/test/comparison/test_array_formula01.py
new file mode 100644
index 0000000..369cd89
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_array_formula01.py
@@ -0,0 +1,99 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'array_formula01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml',
+ '[Content_Types].xml',
+ 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with an array formula."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('B1', 0)
+ worksheet.write('B2', 0)
+ worksheet.write('B3', 0)
+ worksheet.write('C1', 0)
+ worksheet.write('C2', 0)
+ worksheet.write('C3', 0)
+
+ worksheet.write_array_formula(0, 0, 2, 0, '{=SUM(B1:C1*B2:C2)}', None, 0)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_A1(self):
+ """
+ Test the creation of an XlsxWriter file with an array formula
+ and A1 Notation.
+
+ """
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('B1', 0)
+ worksheet.write('B2', 0)
+ worksheet.write('B3', 0)
+ worksheet.write('C1', 0)
+ worksheet.write('C2', 0)
+ worksheet.write('C3', 0)
+
+ worksheet.write_array_formula('A1:A3', '{=SUM(B1:C1*B2:C2)}', None, 0)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_kwargs(self):
+ """
+ Test the creation of an XlsxWriter file with an array formula
+ and keyword args
+ """
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('B1', 0)
+ worksheet.write('B2', 0)
+ worksheet.write('B3', 0)
+ worksheet.write('C1', 0)
+ worksheet.write('C2', 0)
+ worksheet.write('C3', 0)
+
+ worksheet.write_array_formula(first_row=0, first_col=0,
+ last_row=2, last_col=0,
+ formula='{=SUM(B1:C1*B2:C2)}')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_array_formula02.py b/xlsxwriter/test/comparison/test_array_formula02.py
new file mode 100644
index 0000000..6bc7752
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_array_formula02.py
@@ -0,0 +1,51 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'array_formula02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml',
+ '[Content_Types].xml',
+ 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with an array formula."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ xf_format = workbook.add_format({'bold': 1})
+
+ worksheet.write('B1', 0)
+ worksheet.write('B2', 0)
+ worksheet.write('B3', 0)
+ worksheet.write('C1', 0)
+ worksheet.write('C2', 0)
+ worksheet.write('C3', 0)
+
+ worksheet.write_array_formula(0, 0, 2, 0, '{=SUM(B1:C1*B2:C2)}', xf_format, 0)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_autofilter00.py b/xlsxwriter/test/comparison/test_autofilter00.py
new file mode 100644
index 0000000..c3435f7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_autofilter00.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'autofilter00.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+ self.txt_filename = test_dir + 'xlsx_files/' + 'autofilter_data.txt'
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a simple XlsxWriter file with an autofilter.
+ This test is the base comparison. It has data but no autofilter.
+ """
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Open a text file with autofilter example data.
+ textfile = open(self.txt_filename)
+
+ # Start writing data from the first worksheet row.
+ row = 0
+
+ # Read the text file and write it to the worksheet.
+ for line in textfile:
+ # Split the input data based on whitespace.
+ data = line.strip("\n").split()
+
+ # Convert the number data from the text file.
+ for i, item in enumerate(data):
+ try:
+ data[i] = float(item)
+ except ValueError:
+ pass
+
+ for col in range(len(data)):
+ worksheet.write(row, col, data[col])
+
+ # Move on to the next worksheet row.
+ row += 1
+
+ textfile.close()
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_autofilter01.py b/xlsxwriter/test/comparison/test_autofilter01.py
new file mode 100644
index 0000000..ada4bec
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_autofilter01.py
@@ -0,0 +1,72 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'autofilter01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+ self.txt_filename = test_dir + 'xlsx_files/' + 'autofilter_data.txt'
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a simple XlsxWriter file with an autofilter.
+ This test corresponds to the following examples/autofilter.py example:
+ Example 1. Autofilter without conditions.
+ """
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Set the autofilter.
+ worksheet.autofilter('A1:D51')
+
+ # Open a text file with autofilter example data.
+ textfile = open(self.txt_filename)
+
+ # Start writing data from the first worksheet row.
+ row = 0
+
+ # Read the text file and write it to the worksheet.
+ for line in textfile:
+
+ # Split the input data based on whitespace.
+ data = line.strip("\n").split()
+
+ # Convert the number data from the text file.
+ for i, item in enumerate(data):
+ try:
+ data[i] = float(item)
+ except ValueError:
+ pass
+
+ # Write out the row data.
+ worksheet.write_row(row, 0, data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+ textfile.close()
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_autofilter02.py b/xlsxwriter/test/comparison/test_autofilter02.py
new file mode 100644
index 0000000..f6bce74
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_autofilter02.py
@@ -0,0 +1,92 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'autofilter02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+ self.txt_filename = test_dir + 'xlsx_files/' + 'autofilter_data.txt'
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a simple XlsxWriter file with an autofilter.
+ This test corresponds to the following examples/autofilter.py example:
+ Example 2. Autofilter with a filter condition in the first column.
+ """
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Set the autofilter.
+ worksheet.autofilter('A1:D51')
+
+ # Add filter criteria.
+ worksheet.filter_column(0, 'region == East')
+
+ # Open a text file with autofilter example data.
+ textfile = open(self.txt_filename)
+
+ # Read the headers from the first line of the input file.
+ headers = textfile.readline().strip("\n").split()
+
+ # Write out the headers.
+ worksheet.write_row('A1', headers)
+
+ # Start writing data after the headers.
+ row = 1
+
+ # Read the rest of the text file and write it to the worksheet.
+ for line in textfile:
+
+ # Split the input data based on whitespace.
+ data = line.strip("\n").split()
+
+ # Convert the number data from the text file.
+ for i, item in enumerate(data):
+ try:
+ data[i] = float(item)
+ except ValueError:
+ pass
+
+ # Get some of the field data.
+ region = data[0]
+
+ # Check for rows that match the filter.
+ if region == 'East':
+ # Row matches the filter, no further action required.
+ pass
+ else:
+ # We need to hide rows that don't match the filter.
+ worksheet.set_row(row, options={'hidden': True})
+
+ # Write out the row data.
+ worksheet.write_row(row, 0, data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+ textfile.close()
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_autofilter03.py b/xlsxwriter/test/comparison/test_autofilter03.py
new file mode 100644
index 0000000..060c21b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_autofilter03.py
@@ -0,0 +1,93 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'autofilter03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+ self.txt_filename = test_dir + 'xlsx_files/' + 'autofilter_data.txt'
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a simple XlsxWriter file with an autofilter.
+ This test corresponds to the following examples/autofilter.py example:
+ Example 3. Autofilter with a dual filter condition in one of the
+ columns.
+ """
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Set the autofilter.
+ worksheet.autofilter('A1:D51')
+
+ # Add filter criteria.
+ worksheet.filter_column(0, 'x == East or x = South')
+
+ # Open a text file with autofilter example data.
+ textfile = open(self.txt_filename)
+
+ # Read the headers from the first line of the input file.
+ headers = textfile.readline().strip("\n").split()
+
+ # Write out the headers.
+ worksheet.write_row('A1', headers)
+
+ # Start writing data after the headers.
+ row = 1
+
+ # Read the rest of the text file and write it to the worksheet.
+ for line in textfile:
+
+ # Split the input data based on whitespace.
+ data = line.strip("\n").split()
+
+ # Convert the number data from the text file.
+ for i, item in enumerate(data):
+ try:
+ data[i] = float(item)
+ except ValueError:
+ pass
+
+ # Get some of the field data.
+ region = data[0]
+
+ # Check for rows that match the filter.
+ if region in ('East', 'South'):
+ # Row matches the filter, no further action required.
+ pass
+ else:
+ # We need to hide rows that don't match the filter.
+ worksheet.set_row(row, options={'hidden': True})
+
+ # Write out the row data.
+ worksheet.write_row(row, 0, data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+ textfile.close()
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_autofilter04.py b/xlsxwriter/test/comparison/test_autofilter04.py
new file mode 100644
index 0000000..7c39fc1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_autofilter04.py
@@ -0,0 +1,94 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'autofilter04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+ self.txt_filename = test_dir + 'xlsx_files/' + 'autofilter_data.txt'
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a simple XlsxWriter file with an autofilter.
+ This test corresponds to the following examples/autofilter.py example:
+ Example 4. Autofilter with filter conditions in two columns.
+ """
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Set the autofilter.
+ worksheet.autofilter('A1:D51')
+
+ # Add filter criteria.
+ worksheet.filter_column('A', 'x == East')
+ worksheet.filter_column('C', 'x > 3000 and x < 8000')
+
+ # Open a text file with autofilter example data.
+ textfile = open(self.txt_filename)
+
+ # Read the headers from the first line of the input file.
+ headers = textfile.readline().strip("\n").split()
+
+ # Write out the headers.
+ worksheet.write_row('A1', headers)
+
+ # Start writing data after the headers.
+ row = 1
+
+ # Read the rest of the text file and write it to the worksheet.
+ for line in textfile:
+
+ # Split the input data based on whitespace.
+ data = line.strip("\n").split()
+
+ # Convert the number data from the text file.
+ for i, item in enumerate(data):
+ try:
+ data[i] = float(item)
+ except ValueError:
+ pass
+
+ # Get some of the field data.
+ region = data[0]
+ volume = int(data[2])
+
+ # Check for rows that match the filter.
+ if region == 'East' and volume > 3000 and volume < 8000:
+ # Row matches the filter, no further action required.
+ pass
+ else:
+ # We need to hide rows that don't match the filter.
+ worksheet.set_row(row, options={'hidden': True})
+
+ # Write out the row data.
+ worksheet.write_row(row, 0, data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+ textfile.close()
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_autofilter05.py b/xlsxwriter/test/comparison/test_autofilter05.py
new file mode 100644
index 0000000..8870d41
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_autofilter05.py
@@ -0,0 +1,96 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'autofilter05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+ self.txt_filename = test_dir + 'xlsx_files/' + 'autofilter_data.txt'
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a simple XlsxWriter file with an autofilter.
+ This test corresponds to the following examples/autofilter.pl example:
+ Example 5. Autofilter with filter for blanks.
+ """
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Set the autofilter.
+ worksheet.autofilter('A1:D51')
+
+ # Add filter criteria.
+ worksheet.filter_column(0, 'x == Blanks')
+
+ # Open a text file with autofilter example data.
+ textfile = open(self.txt_filename)
+
+ # Read the headers from the first line of the input file.
+ headers = textfile.readline().strip("\n").split()
+
+ # Write out the headers.
+ worksheet.write_row('A1', headers)
+
+ # Start writing data after the headers.
+ row = 1
+
+ # Read the rest of the text file and write it to the worksheet.
+ for line in textfile:
+
+ # Split the input data based on whitespace.
+ data = line.strip("\n").split()
+
+ # Convert the number data from the text file.
+ for i, item in enumerate(data):
+ try:
+ data[i] = float(item)
+ except ValueError:
+ pass
+
+ # Simulate a blank cell in the data.
+ if row == 6:
+ data[0] = ''
+
+ # Get some of the field data.
+ region = data[0]
+
+ # Check for rows that match the filter.
+ if region == '':
+ # Row matches the filter, no further action required.
+ pass
+ else:
+ # We need to hide rows that don't match the filter.
+ worksheet.set_row(row, options={'hidden': True})
+
+ # Write out the row data.
+ worksheet.write_row(row, 0, data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+ textfile.close()
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_autofilter06.py b/xlsxwriter/test/comparison/test_autofilter06.py
new file mode 100644
index 0000000..786f045
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_autofilter06.py
@@ -0,0 +1,96 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'autofilter06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+ self.txt_filename = test_dir + 'xlsx_files/' + 'autofilter_data.txt'
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a simple XlsxWriter file with an autofilter.
+ This test corresponds to the following examples/autofilter.pl example:
+ Example 6. Autofilter with filter for non-blanks.
+ """
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Set the autofilter.
+ worksheet.autofilter('A1:D51')
+
+ # Add filter criteria.
+ worksheet.filter_column(0, 'x == NonBlanks')
+
+ # Open a text file with autofilter example data.
+ textfile = open(self.txt_filename)
+
+ # Read the headers from the first line of the input file.
+ headers = textfile.readline().strip("\n").split()
+
+ # Write out the headers.
+ worksheet.write_row('A1', headers)
+
+ # Start writing data after the headers.
+ row = 1
+
+ # Read the rest of the text file and write it to the worksheet.
+ for line in textfile:
+
+ # Split the input data based on whitespace.
+ data = line.strip("\n").split()
+
+ # Convert the number data from the text file.
+ for i, item in enumerate(data):
+ try:
+ data[i] = float(item)
+ except ValueError:
+ pass
+
+ # Simulate a blank cell in the data.
+ if row == 6:
+ data[0] = ''
+
+ # Get some of the field data.
+ region = data[0]
+
+ # Check for rows that match the filter.
+ if region != '':
+ # Row matches the filter, no further action required.
+ pass
+ else:
+ # We need to hide rows that don't match the filter.
+ worksheet.set_row(row, options={'hidden': True})
+
+ # Write out the row data.
+ worksheet.write_row(row, 0, data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+ textfile.close()
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_autofilter07.py b/xlsxwriter/test/comparison/test_autofilter07.py
new file mode 100644
index 0000000..446a96c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_autofilter07.py
@@ -0,0 +1,92 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'autofilter07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+ self.txt_filename = test_dir + 'xlsx_files/' + 'autofilter_data.txt'
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a simple XlsxWriter file with an autofilter.
+ Test autofilters where column filter ids are relative to autofilter
+ range.
+ """
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Set the autofilter.
+ worksheet.autofilter('D3:G53')
+
+ # Add filter criteria.
+ worksheet.filter_column('D', 'region == East')
+
+ # Open a text file with autofilter example data.
+ textfile = open(self.txt_filename)
+
+ # Read the headers from the first line of the input file.
+ headers = textfile.readline().strip("\n").split()
+
+ # Write out the headers.
+ worksheet.write_row('D3', headers)
+
+ # Start writing data after the headers.
+ row = 3
+
+ # Read the rest of the text file and write it to the worksheet.
+ for line in textfile:
+
+ # Split the input data based on whitespace.
+ data = line.strip("\n").split()
+
+ # Convert the number data from the text file.
+ for i, item in enumerate(data):
+ try:
+ data[i] = float(item)
+ except ValueError:
+ pass
+
+ # Get some of the field data.
+ region = data[0]
+
+ # Check for rows that match the filter.
+ if region == 'East':
+ # Row matches the filter, no further action required.
+ pass
+ else:
+ # We need to hide rows that don't match the filter.
+ worksheet.set_row(row, options={'hidden': True})
+
+ # Write out the row data.
+ worksheet.write_row(row, 3, data)
+
+ # Move on to the next worksheet row.
+ row += 1
+
+ textfile.close()
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_button01.py b/xlsxwriter/test/comparison/test_button01.py
new file mode 100644
index 0000000..d9d0e5a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_button01.py
@@ -0,0 +1,41 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'button01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_button('C2', {})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_button02.py b/xlsxwriter/test/comparison/test_button02.py
new file mode 100644
index 0000000..05a7af6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_button02.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'button02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_button('B4', {'x_offset': 4,
+ 'y_offset': 3,
+ 'caption': 'my text'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_button03.py b/xlsxwriter/test/comparison/test_button03.py
new file mode 100644
index 0000000..3bc8184
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_button03.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'button03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_button('C2', {})
+ worksheet.insert_button('E5', {})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_button04.py b/xlsxwriter/test/comparison/test_button04.py
new file mode 100644
index 0000000..66b0b9e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_button04.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'button04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+
+ worksheet1.insert_button('C2', {})
+ worksheet2.insert_button('E5', {})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_button05.py b/xlsxwriter/test/comparison/test_button05.py
new file mode 100644
index 0000000..4433ba0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_button05.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'button05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_button('C2', {'macro': 'my_macro',
+ 'x_scale': 2,
+ 'y_scale': 1.5
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_button06.py b/xlsxwriter/test/comparison/test_button06.py
new file mode 100644
index 0000000..39bb36c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_button06.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'button05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test2_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_button('C2', {'macro': 'my_macro',
+ 'width': 128,
+ 'height': 30
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_button07.py b/xlsxwriter/test/comparison/test_button07.py
new file mode 100644
index 0000000..3162c9f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_button07.py
@@ -0,0 +1,83 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'button07.xlsm'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.vba_dir = test_dir + 'xlsx_files/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ workbook.set_vba_name()
+ worksheet.set_vba_name()
+
+ worksheet.insert_button('C2', {'macro': 'say_hello',
+ 'caption': 'Hello'})
+
+ workbook.add_vba_project(self.vba_dir + 'vbaProject02.bin')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_explicit_vba_names(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ workbook.set_vba_name('ThisWorkbook')
+ worksheet.set_vba_name('Sheet1')
+
+ worksheet.insert_button('C2', {'macro': 'say_hello',
+ 'caption': 'Hello'})
+
+ workbook.add_vba_project(self.vba_dir + 'vbaProject02.bin')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_implicit_vba_names(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_button('C2', {'macro': 'say_hello',
+ 'caption': 'Hello'})
+
+ workbook.add_vba_project(self.vba_dir + 'vbaProject02.bin')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_button08.py b/xlsxwriter/test/comparison/test_button08.py
new file mode 100644
index 0000000..3d8af94
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_button08.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'button08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+
+ worksheet1.insert_button('C2', {})
+
+ worksheet2.write_comment('A1', 'Foo')
+
+ worksheet2.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_button09.py b/xlsxwriter/test/comparison/test_button09.py
new file mode 100644
index 0000000..ba2528f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_button09.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'button09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+
+ worksheet1.write_comment('A1', 'Foo')
+
+ worksheet2.insert_button('C2', {})
+
+ worksheet1.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_button10.py b/xlsxwriter/test/comparison/test_button10.py
new file mode 100644
index 0000000..5a93956
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_button10.py
@@ -0,0 +1,50 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'button10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet()
+
+ worksheet1.write_comment('A1', 'Some text')
+
+ worksheet2.insert_button('B2', {})
+
+ worksheet3.write_comment('C2', 'More text')
+
+ worksheet1.set_comments_author('John')
+ worksheet3.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_button11.py b/xlsxwriter/test/comparison/test_button11.py
new file mode 100644
index 0000000..a8bf98a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_button11.py
@@ -0,0 +1,50 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'button11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet()
+
+ worksheet1.insert_button('C2', {})
+
+ worksheet2.write_comment('B2', 'Some text')
+
+ worksheet3.write_comment('C3', 'More text')
+
+ worksheet2.set_comments_author('John')
+ worksheet3.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_button12.py b/xlsxwriter/test/comparison/test_button12.py
new file mode 100644
index 0000000..46e5286
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_button12.py
@@ -0,0 +1,49 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'button12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet()
+
+ worksheet1.write_comment('A1', 'Some text')
+ worksheet1.insert_button('C2', {})
+
+ worksheet3.write_comment('C3', 'More text')
+
+ worksheet1.set_comments_author('John')
+ worksheet3.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_area01.py b/xlsxwriter/test/comparison/test_chart_area01.py
new file mode 100644
index 0000000..42a05f8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_area01.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_area01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'area'})
+
+ chart.axis_ids = [43407616, 43433984]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [8, 7, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5'})
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_area02.py b/xlsxwriter/test/comparison/test_chart_area02.py
new file mode 100644
index 0000000..1b1718a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_area02.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_area02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'area', 'subtype': 'stacked'})
+
+ chart.axis_ids = [62813312, 62814848]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [8, 7, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5'})
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_area03.py b/xlsxwriter/test/comparison/test_chart_area03.py
new file mode 100644
index 0000000..371ee75
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_area03.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_area03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'area', 'subtype': 'percent_stacked'})
+
+ chart.axis_ids = [62813312, 62814848]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [8, 7, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5'})
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_area04.py b/xlsxwriter/test/comparison/test_chart_area04.py
new file mode 100644
index 0000000..a6ae359
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_area04.py
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_area04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/workbook.xml': ['<fileVersion', '<calcPr']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'area'})
+
+ chart.axis_ids = [63591168, 63592704]
+ chart.axis2_ids = [74921856, 73764224]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [6, 8, 6, 4, 2],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5', 'y2_axis': 1})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis01.py b/xlsxwriter/test/comparison/test_chart_axis01.py
new file mode 100644
index 0000000..d572bad
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis01.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [58955648, 68446848]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'name': 'XXX'})
+ chart.set_y_axis({'name': 'YYY'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis02.py b/xlsxwriter/test/comparison/test_chart_axis02.py
new file mode 100644
index 0000000..8e9e0d8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis02.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [43704320, 43706624]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'name': 'XXX'})
+ chart.set_y_axis({'name': 'YYY'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis03.py b/xlsxwriter/test/comparison/test_chart_axis03.py
new file mode 100644
index 0000000..f8a36b1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis03.py
@@ -0,0 +1,79 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'stock'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [65514112, 65556864]
+
+ data = [
+ [39083, 39084, 39085, 39086, 39087],
+ [27.2, 25.03, 19.05, 20.34, 18.5],
+ [23.49, 19.55, 15.12, 17.84, 16.34],
+ [25.45, 23.05, 17.32, 20.45, 17.34],
+ ]
+
+ for row in range(5):
+ worksheet.write(row, 0, data[0][row], date_format)
+ worksheet.write(row, 1, data[1][row])
+ worksheet.write(row, 2, data[2][row])
+ worksheet.write(row, 3, data[3][row])
+
+ worksheet.set_column('A:D', 11)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$D$1:$D$5',
+ })
+
+ chart.set_title({'name': 'Title'})
+ chart.set_x_axis({'name': 'XXX'})
+ chart.set_y_axis({'name': 'YYY'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis04.py b/xlsxwriter/test/comparison/test_chart_axis04.py
new file mode 100644
index 0000000..d178dc5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis04.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [46891776, 46893312]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_x_axis({'name': 'XXX'})
+ chart.set_y_axis({'name': 'YYY'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis05.py b/xlsxwriter/test/comparison/test_chart_axis05.py
new file mode 100644
index 0000000..23dd828
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis05.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [47076480, 47078016]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'name': 'XXX'})
+ chart.set_y_axis({'name': 'YYY'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis06.py b/xlsxwriter/test/comparison/test_chart_axis06.py
new file mode 100644
index 0000000..c4da7a9
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis06.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [
+ [2, 4, 6],
+ [60, 30, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$3',
+ 'values': '=Sheet1!$B$1:$B$3',
+ })
+
+ chart.set_title({'name': 'Title'})
+ chart.set_x_axis({'name': 'XXX'})
+ chart.set_y_axis({'name': 'YYY'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis07.py b/xlsxwriter/test/comparison/test_chart_axis07.py
new file mode 100644
index 0000000..e66597d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis07.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'area'})
+
+ chart.axis_ids = [43321216, 47077248]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [8, 7, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_x_axis({'name': 'XXX'})
+ chart.set_y_axis({'name': 'YYY'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis08.py b/xlsxwriter/test/comparison/test_chart_axis08.py
new file mode 100644
index 0000000..1ad2fa6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis08.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [49042560, 61199872]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_y_axis({'reverse': 1})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis09.py b/xlsxwriter/test/comparison/test_chart_axis09.py
new file mode 100644
index 0000000..efebf11
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis09.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [49043712, 60236160]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'reverse': 1})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis10.py b/xlsxwriter/test/comparison/test_chart_axis10.py
new file mode 100644
index 0000000..3fff84f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis10.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [41012608, 55821440]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_x_axis({'reverse': 1})
+ chart.set_y_axis({'reverse': 1})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis11.py b/xlsxwriter/test/comparison/test_chart_axis11.py
new file mode 100644
index 0000000..8982b8d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis11.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [45705472, 54518528]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'min': 0, 'max': 20})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis12.py b/xlsxwriter/test/comparison/test_chart_axis12.py
new file mode 100644
index 0000000..03a0ab5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis12.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45705088, 54517760]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_y_axis({'min': 0, 'max': 16})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis13.py b/xlsxwriter/test/comparison/test_chart_axis13.py
new file mode 100644
index 0000000..4b61c25
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis13.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis13.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [54045312, 54043776]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_y_axis({'min': 0, 'max': 16})
+ chart.set_x_axis({'min': 0, 'max': 6})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis14.py b/xlsxwriter/test/comparison/test_chart_axis14.py
new file mode 100644
index 0000000..5b83376
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis14.py
@@ -0,0 +1,78 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis14.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'stock'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [43814272, 54517760]
+
+ data = [
+ [39083, 39084, 39085, 39086, 39087],
+ [27.2, 25.03, 19.05, 20.34, 18.5],
+ [23.49, 19.55, 15.12, 17.84, 16.34],
+ [25.45, 23.05, 17.32, 20.45, 17.34],
+ ]
+
+ for row in range(5):
+ worksheet.write(row, 0, data[0][row], date_format)
+ worksheet.write(row, 1, data[1][row])
+ worksheet.write(row, 2, data[2][row])
+ worksheet.write(row, 3, data[3][row])
+
+ worksheet.set_column('A:D', 11)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$D$1:$D$5',
+ })
+
+ chart.set_y_axis({'min': 0, 'max': 30})
+ chart.set_x_axis({'min': 39083, 'max': 39087})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis15.py b/xlsxwriter/test/comparison/test_chart_axis15.py
new file mode 100644
index 0000000..189a910
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis15.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis15.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45705856, 54518528]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_y_axis({'minor_unit': 0.4, 'major_unit': 2})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis16.py b/xlsxwriter/test/comparison/test_chart_axis16.py
new file mode 100644
index 0000000..52255ac
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis16.py
@@ -0,0 +1,81 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis16.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'stock'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [43572608, 43812736]
+
+ data = [
+ [39083, 39084, 39085, 39086, 39087],
+ [27.2, 25.03, 19.05, 20.34, 18.5],
+ [23.49, 19.55, 15.12, 17.84, 16.34],
+ [25.45, 23.05, 17.32, 20.45, 17.34],
+ ]
+
+ for row in range(5):
+ worksheet.write(row, 0, data[0][row], date_format)
+ worksheet.write(row, 1, data[1][row])
+ worksheet.write(row, 2, data[2][row])
+ worksheet.write(row, 3, data[3][row])
+
+ worksheet.set_column('A:D', 11)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$D$1:$D$5',
+ })
+
+ chart.set_x_axis({
+ 'minor_unit': 14,
+ 'major_unit': 1,
+ 'major_unit_type': 'months'
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis17.py b/xlsxwriter/test/comparison/test_chart_axis17.py
new file mode 100644
index 0000000..a799a8b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis17.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis17.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [43812736, 45705088]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_y_axis({'log_base': 10})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis18.py b/xlsxwriter/test/comparison/test_chart_axis18.py
new file mode 100644
index 0000000..3b78b9e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis18.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis18.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [43813504, 45705472]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5', 'invert_if_negative': 1})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5', 'invert_if_negative': 0})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis19.py b/xlsxwriter/test/comparison/test_chart_axis19.py
new file mode 100644
index 0000000..16b73a1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis19.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis19.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [43813504, 45705472]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'label_position': 'high'})
+ chart.set_y_axis({'label_position': 'low'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis20.py b/xlsxwriter/test/comparison/test_chart_axis20.py
new file mode 100644
index 0000000..c280ced
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis20.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis20.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [43572224, 43812352]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'label_position': 'next_to'})
+ chart.set_y_axis({'label_position': 'none'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis21.py b/xlsxwriter/test/comparison/test_chart_axis21.py
new file mode 100644
index 0000000..c44345b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis21.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis21.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [58921344, 54519680]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_x_axis({'reverse': 1})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis22.py b/xlsxwriter/test/comparison/test_chart_axis22.py
new file mode 100644
index 0000000..6153c66
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis22.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis22.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [86799104, 86801792]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'num_format': '#,##0.00'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis23.py b/xlsxwriter/test/comparison/test_chart_axis23.py
new file mode 100644
index 0000000..4074149
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis23.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis23.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [46332160, 47470848]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'num_format': 'dd/mm/yyyy'})
+ chart.set_y_axis({'num_format': '0.00%'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis24.py b/xlsxwriter/test/comparison/test_chart_axis24.py
new file mode 100644
index 0000000..59d32bb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis24.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis24.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [47471232, 48509696]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'num_format': 'dd/mm/yyyy', 'num_format_linked': 1})
+ chart.set_y_axis({'num_format': '0.00%'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis25.py b/xlsxwriter/test/comparison/test_chart_axis25.py
new file mode 100644
index 0000000..ea1c1c8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis25.py
@@ -0,0 +1,63 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis25.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [47471232, 48509696]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'num_format': '[$¥-411]#,##0.00'})
+ chart.set_y_axis({'num_format': '0.00%'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis26.py b/xlsxwriter/test/comparison/test_chart_axis26.py
new file mode 100644
index 0000000..052baee
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis26.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis26.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<a:defRPr']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [73048448, 73049984]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ chart.set_x_axis({
+ 'num_font': {'rotation': 45}
+ })
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis27.py b/xlsxwriter/test/comparison/test_chart_axis27.py
new file mode 100644
index 0000000..0d31407
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis27.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis27.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<a:defRPr']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [73048448, 73049984]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ chart.set_x_axis({
+ 'num_font': {'rotation': -35}
+ })
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis28.py b/xlsxwriter/test/comparison/test_chart_axis28.py
new file mode 100644
index 0000000..f7980d1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis28.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis28.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<a:defRPr']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [45451904, 47401600]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ chart.set_x_axis({
+ 'num_font': {'rotation': 90}
+ })
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis29.py b/xlsxwriter/test/comparison/test_chart_axis29.py
new file mode 100644
index 0000000..8ae3225
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis29.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis29.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<a:defRPr']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [45444480, 47402368]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ chart.set_x_axis({
+ 'num_font': {'rotation': -90}
+ })
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis30.py b/xlsxwriter/test/comparison/test_chart_axis30.py
new file mode 100644
index 0000000..2ef3bd9
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis30.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis30.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [69200896, 69215360]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ chart.set_x_axis({'position_axis': 'on_tick'})
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis31.py b/xlsxwriter/test/comparison/test_chart_axis31.py
new file mode 100644
index 0000000..1e8dffd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis31.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis31.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [90616960, 90618496]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ chart.set_y_axis({'position_axis': 'on_tick'})
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis32.py b/xlsxwriter/test/comparison/test_chart_axis32.py
new file mode 100644
index 0000000..fa88229
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis32.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis32.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'area'})
+
+ chart.axis_ids = [96171520, 96173056]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ chart.set_x_axis({'position_axis': 'between'})
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis33.py b/xlsxwriter/test/comparison/test_chart_axis33.py
new file mode 100644
index 0000000..bb8e6ef
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis33.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis33.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [68827008, 68898816]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'name': 'XXX', 'name_font': {'rotation': -45, 'baseline': -1}})
+ chart.set_y_axis({'name': 'YYY', 'name_font': {'rotation': -45, 'baseline': -1}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis34.py b/xlsxwriter/test/comparison/test_chart_axis34.py
new file mode 100644
index 0000000..344c19d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis34.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis34.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [54712192, 54713728]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'interval_unit': 2})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis35.py b/xlsxwriter/test/comparison/test_chart_axis35.py
new file mode 100644
index 0000000..5a796e0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis35.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis35.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [63008128, 62522496]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_y_axis({'line': {'none': True}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis36.py b/xlsxwriter/test/comparison/test_chart_axis36.py
new file mode 100644
index 0000000..8e418ea
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis36.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis36.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45501056, 47505792]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'line': {'none': True}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis37.py b/xlsxwriter/test/comparison/test_chart_axis37.py
new file mode 100644
index 0000000..e125312
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis37.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis37.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [46032384, 48088960]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'line': {'color': 'yellow'}})
+ chart.set_y_axis({'line': {'color': 'red'}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis38.py b/xlsxwriter/test/comparison/test_chart_axis38.py
new file mode 100644
index 0000000..3730bee
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis38.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis38.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45642496, 45644416]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_y_axis({'line': {'color': 'yellow'},
+ 'fill': {'color': 'red'}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis39.py b/xlsxwriter/test/comparison/test_chart_axis39.py
new file mode 100644
index 0000000..66ef710
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis39.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis39.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [45884928, 45883392]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_x_axis({'line': {'none': True}})
+ chart.set_y_axis({'line': {'none': True}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis40.py b/xlsxwriter/test/comparison/test_chart_axis40.py
new file mode 100644
index 0000000..fd6ae62
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis40.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis40.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [108329216, 108635264]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'interval_unit': 3, 'interval_tick': 2})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_axis41.py b/xlsxwriter/test/comparison/test_chart_axis41.py
new file mode 100644
index 0000000..2ad4783
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_axis41.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_axis41.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [108321024, 108328448]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'major_tick_mark': 'none', 'minor_tick_mark': 'inside'})
+ chart.set_y_axis({'minor_tick_mark': 'cross'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar01.py b/xlsxwriter/test/comparison/test_chart_bar01.py
new file mode 100644
index 0000000..402743a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar01.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [64052224, 64055552]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5'
+ })
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar02.py b/xlsxwriter/test/comparison/test_chart_bar02.py
new file mode 100644
index 0000000..d4c1b9f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar02.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [93218304, 93219840]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet1.write('A1', 'Foo')
+
+ worksheet2.write_column('A1', data[0])
+ worksheet2.write_column('B1', data[1])
+ worksheet2.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': 'Sheet2!$A$1:$A$5',
+ 'values': 'Sheet2!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': 'Sheet2!$A$1:$A$5',
+ 'values': 'Sheet2!$C$1:$C$5',
+ })
+ worksheet2.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar03.py b/xlsxwriter/test/comparison/test_chart_bar03.py
new file mode 100644
index 0000000..9a0e247
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar03.py
@@ -0,0 +1,78 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart1 = workbook.add_chart({'type': 'bar'})
+ chart2 = workbook.add_chart({'type': 'bar'})
+
+ chart1.axis_ids = [64265216, 64447616]
+ chart2.axis_ids = [86048128, 86058112]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart1.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart1.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart1)
+
+ chart2.add_series({
+ 'categories': '=Sheet1!$A$1:$A$4',
+ 'values': '=Sheet1!$B$1:$B$4',
+ })
+
+ chart2.add_series({
+ 'categories': '=Sheet1!$A$1:$A$4',
+ 'values': '=Sheet1!$C$1:$C$4',
+ })
+
+ worksheet.insert_chart('F25', chart2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar04.py b/xlsxwriter/test/comparison/test_chart_bar04.py
new file mode 100644
index 0000000..43b65af
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar04.py
@@ -0,0 +1,83 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ chart1 = workbook.add_chart({'type': 'bar'})
+ chart2 = workbook.add_chart({'type': 'bar'})
+
+ chart1.axis_ids = [64446848, 64448384]
+ chart2.axis_ids = [85389696, 85391232]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet1.write_column('A1', data[0])
+ worksheet1.write_column('B1', data[1])
+ worksheet1.write_column('C1', data[2])
+
+ chart1.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart1.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet1.insert_chart('E9', chart1)
+
+ worksheet2.write_column('A1', data[0])
+ worksheet2.write_column('B1', data[1])
+ worksheet2.write_column('C1', data[2])
+
+ chart2.add_series({
+ 'categories': '=Sheet2!$A$1:$A$5',
+ 'values': '=Sheet2!$B$1:$B$5',
+ })
+
+ chart2.add_series({
+ 'categories': '=Sheet2!$A$1:$A$5',
+ 'values': '=Sheet2!$C$1:$C$5',
+ })
+
+ worksheet2.insert_chart('E9', chart2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar05.py b/xlsxwriter/test/comparison/test_chart_bar05.py
new file mode 100644
index 0000000..bea7c13
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar05.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [64264064, 64447232]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': ['Sheet1', 0, 0, 4, 0]})
+ chart.add_series({'values': ['Sheet1', 0, 1, 4, 1]})
+ chart.add_series({'values': ['Sheet1', 0, 2, 4, 2]})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar06.py b/xlsxwriter/test/comparison/test_chart_bar06.py
new file mode 100644
index 0000000..d8ce9d6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar06.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [64053248, 64446464]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'name': 'Apple'})
+ chart.set_y_axis({'name': 'Pear'})
+ chart.set_title({'name': 'Title'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar07.py b/xlsxwriter/test/comparison/test_chart_bar07.py
new file mode 100644
index 0000000..8f3efe1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar07.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [66558592, 66569344]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'name_formula': '=Sheet1!$A$2', 'data': [2]})
+ chart.set_y_axis({'name_formula': '=Sheet1!$A$3', 'data': [3]})
+ chart.set_title({'name_formula': '=Sheet1!$A$1', 'data': [1]})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar08.py b/xlsxwriter/test/comparison/test_chart_bar08.py
new file mode 100644
index 0000000..d4bde54
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar08.py
@@ -0,0 +1,64 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [40522880, 40524416]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ worksheet.write('A7', 'http://www.perl.com/')
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar09.py b/xlsxwriter/test/comparison/test_chart_bar09.py
new file mode 100644
index 0000000..94fbdc5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar09.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar', 'subtype': 'stacked'})
+
+ chart.axis_ids = [40274560, 40295040]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar10.py b/xlsxwriter/test/comparison/test_chart_bar10.py
new file mode 100644
index 0000000..af4832c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar10.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar', 'subtype': 'percent_stacked'})
+
+ chart.axis_ids = [40274560, 40295040]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar11.py b/xlsxwriter/test/comparison/test_chart_bar11.py
new file mode 100644
index 0000000..ddac9a1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar11.py
@@ -0,0 +1,76 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+ chart1 = workbook.add_chart({'type': 'bar'})
+ chart2 = workbook.add_chart({'type': 'bar'})
+ chart3 = workbook.add_chart({'type': 'bar'})
+
+ chart1.axis_ids = [40274944, 40294272]
+ chart2.axis_ids = [62355328, 62356864]
+ chart3.axis_ids = [79538816, 65422464]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ worksheet.write('A7', 'http://www.perl.com/')
+ worksheet.write('A8', 'http://www.perl.org/')
+ worksheet.write('A9', 'http://www.perl.net/')
+
+ chart1.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart1.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart1.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart2.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart2.add_series({'values': '=Sheet1!$B$1:$B$5'})
+
+ chart3.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ worksheet.insert_chart('E9', chart1)
+ worksheet.insert_chart('D25', chart2)
+ worksheet.insert_chart('L32', chart3)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar18.py b/xlsxwriter/test/comparison/test_chart_bar18.py
new file mode 100644
index 0000000..856b2f7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar18.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar18.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/chartsheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/chartsheets/sheet1.xml': ['<pageSetup', '<pageMargins', '<drawing']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chartsheet = workbook.add_chartsheet()
+
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [40294272, 40295808]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chartsheet.activate()
+ chartsheet.set_header('Page &P')
+ chartsheet.set_footer('&A')
+
+ chartsheet.set_chart(chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar19.py b/xlsxwriter/test/comparison/test_chart_bar19.py
new file mode 100644
index 0000000..ad40ac5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar19.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar19.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [66558592, 66569344]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'name': '=Sheet1!$A$2'})
+ chart.set_y_axis({'name': '=Sheet1!$A$3'})
+ chart.set_title({'name': '=Sheet1!$A$1'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar20.py b/xlsxwriter/test/comparison/test_chart_bar20.py
new file mode 100644
index 0000000..9e273a7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar20.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar20.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [45925120, 45927040]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ worksheet.write('A7', 'Pear')
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5', 'name': 'Apple'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5', 'name': '=Sheet1!$A$7'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar21.py b/xlsxwriter/test/comparison/test_chart_bar21.py
new file mode 100644
index 0000000..bfb530c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar21.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar21.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [64052224, 64055552]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'categories_data': data[0],
+ 'values_data': data[1],
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'categories_data': data[0],
+ 'values_data': data[2],
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar22.py b/xlsxwriter/test/comparison/test_chart_bar22.py
new file mode 100644
index 0000000..aa7a9a7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar22.py
@@ -0,0 +1,82 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar22.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [43706240, 43727104]
+
+ headers = ['Series 1', 'Series 2', 'Series 3']
+
+ data = [
+ ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
+ [4.3, 2.5, 3.5, 4.5],
+ [2.4, 4.5, 1.8, 2.8],
+ [2, 2, 3, 5],
+ ]
+
+ worksheet.set_column('A:D', 11)
+
+ worksheet.write_row('B1', headers)
+ worksheet.write_column('A2', data[0])
+ worksheet.write_column('B2', data[1])
+ worksheet.write_column('C2', data[2])
+ worksheet.write_column('D2', data[3])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$2:$A$5',
+ 'values': '=Sheet1!$B$2:$B$5',
+ 'categories_data': data[0],
+ 'values_data': data[1],
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$2:$A$5',
+ 'values': '=Sheet1!$C$2:$C$5',
+ 'categories_data': data[0],
+ 'values_data': data[2],
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$2:$A$5',
+ 'values': '=Sheet1!$D$2:$D$5',
+ 'categories_data': data[0],
+ 'values_data': data[3],
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar23.py b/xlsxwriter/test/comparison/test_chart_bar23.py
new file mode 100644
index 0000000..c89b53b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar23.py
@@ -0,0 +1,76 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar23.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [43706240, 43727104]
+
+ headers = ['Series 1', 'Series 2', 'Series 3']
+
+ data = [
+ ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
+ [4.3, 2.5, 3.5, 4.5],
+ [2.4, 4.5, 1.8, 2.8],
+ [2, 2, 3, 5],
+ ]
+
+ worksheet.set_column('A:D', 11)
+
+ worksheet.write_row('B1', headers)
+ worksheet.write_column('A2', data[0])
+ worksheet.write_column('B2', data[1])
+ worksheet.write_column('C2', data[2])
+ worksheet.write_column('D2', data[3])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$2:$A$5',
+ 'values': '=Sheet1!$B$2:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$2:$A$5',
+ 'values': '=Sheet1!$C$2:$C$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$2:$A$5',
+ 'values': '=Sheet1!$D$2:$D$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_bar24.py b/xlsxwriter/test/comparison/test_chart_bar24.py
new file mode 100644
index 0000000..4da79b1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_bar24.py
@@ -0,0 +1,53 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_bar24.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/workbook.xml': ['<fileVersion', '<calcPr']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [63591168, 63592704]
+ chart.axis2_ids = [65934464, 72628864]
+
+ data = [[27, 33, 44, 12, 1], [6, 8, 6, 4, 2]]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5', 'y2_axis': 1})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_blank01.py b/xlsxwriter/test/comparison/test_chart_blank01.py
new file mode 100644
index 0000000..ee105ab
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_blank01.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_blank01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [45705856, 45843584]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.show_blanks_as('gap')
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_blank02.py b/xlsxwriter/test/comparison/test_chart_blank02.py
new file mode 100644
index 0000000..0c69aa1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_blank02.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_blank02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [44253568, 44269952]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.show_blanks_as('zero')
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_blank03.py b/xlsxwriter/test/comparison/test_chart_blank03.py
new file mode 100644
index 0000000..083149c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_blank03.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_blank03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [44253568, 44269952]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.show_blanks_as('span')
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_blank04.py b/xlsxwriter/test/comparison/test_chart_blank04.py
new file mode 100644
index 0000000..ddbf9ff
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_blank04.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_blank04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [42268928, 42990208]
+
+ data = [
+ [1, 2, None, 4, 5],
+ [2, 4, None, 8, 10],
+ [3, 6, None, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.show_blanks_as('span')
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_blank05.py b/xlsxwriter/test/comparison/test_chart_blank05.py
new file mode 100644
index 0000000..16b5773
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_blank05.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_blank05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/drawings/drawing1.xml': ['<xdr:ext']}
+
+ def test_create_file(self):
+ """Test the worksheet properties of an XlsxWriter chartsheet file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chartsheet = workbook.add_chartsheet()
+
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [57619968, 57621504]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.show_blanks_as('span')
+
+ chartsheet.set_chart(chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_blank06.py b/xlsxwriter/test/comparison/test_chart_blank06.py
new file mode 100644
index 0000000..d1c7963
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_blank06.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_blank06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [42270080, 42990208]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.show_hidden_data()
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_chartarea01.py b/xlsxwriter/test/comparison/test_chart_chartarea01.py
new file mode 100644
index 0000000..fb9a16d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_chartarea01.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_chartarea01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter chartarea properties."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [82933248, 82952960]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_chartarea({
+ 'border': {'none': 1},
+ 'fill': {'color': 'red'}
+ })
+
+ chart.set_plotarea({
+ 'border': {'color': 'yellow', 'width': 1, 'dash_type': 'dash'},
+ 'fill': {'color': '#92D050'}
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_chartarea02.py b/xlsxwriter/test/comparison/test_chart_chartarea02.py
new file mode 100644
index 0000000..d6828d8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_chartarea02.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_chartarea01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_2_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter chartarea properties."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [82933248, 82952960]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_chartarea({
+ 'border': {'none': 1},
+ 'fill': {'color': 'red'}
+ })
+
+ chart.set_plotarea({
+ 'border': {'color': 'yellow', 'width': 1, 'dash_type': 'dash'},
+ 'fill': {'color': '#92D050'}
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_chartarea03.py b/xlsxwriter/test/comparison/test_chart_chartarea03.py
new file mode 100644
index 0000000..8a9feac
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_chartarea03.py
@@ -0,0 +1,75 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_chartarea03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter chartarea properties."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [46210048, 46208512]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_chartarea({
+ 'border': {'dash_type': 'round_dot'},
+ 'fill': {'color': '#9999FF'}
+ })
+
+ chart.set_plotarea({
+ 'border': {'dash_type': 'square_dot'},
+ 'fill': {'color': '#FFC000'}
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_chartarea04.py b/xlsxwriter/test/comparison/test_chart_chartarea04.py
new file mode 100644
index 0000000..c23e179
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_chartarea04.py
@@ -0,0 +1,85 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_chartarea04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test XlsxWriter gridlines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'stock'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [82954112, 82956288]
+
+ data = [
+ [39083, 39084, 39085, 39086, 39087],
+ [27.2, 25.03, 19.05, 20.34, 18.5],
+ [23.49, 19.55, 15.12, 17.84, 16.34],
+ [25.45, 23.05, 17.32, 20.45, 17.34],
+ ]
+
+ for row in range(5):
+ worksheet.write(row, 0, data[0][row], date_format)
+ worksheet.write(row, 1, data[1][row])
+ worksheet.write(row, 2, data[2][row])
+ worksheet.write(row, 3, data[3][row])
+
+ worksheet.set_column('A:D', 11)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$D$1:$D$5',
+ })
+
+ chart.set_chartarea({
+ 'border': {'color': '#FF0000'},
+ 'fill': {'color': '#00B050'}
+ })
+
+ chart.set_plotarea({
+ 'border': {'dash_type': 'dash_dot'},
+ 'fill': {'color': '#FFC000'}
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_chartarea05.py b/xlsxwriter/test/comparison/test_chart_chartarea05.py
new file mode 100644
index 0000000..8e55665
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_chartarea05.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_chartarea05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter chartarea properties."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [
+ [2, 4, 6],
+ [60, 30, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$3',
+ 'values': '=Sheet1!$B$1:$B$3',
+ })
+
+ chart.set_chartarea({
+ 'border': {'color': '#FFFF00', 'dash_type': 'long_dash'},
+ 'fill': {'color': '#92D050'}
+ })
+
+ chart.set_plotarea({
+ 'border': {'dash_type': 'dash_dot'},
+ 'fill': {'color': '#FFC000'}
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_clustered01.py b/xlsxwriter/test/comparison/test_chart_clustered01.py
new file mode 100644
index 0000000..eeb8330
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_clustered01.py
@@ -0,0 +1,81 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_clustered01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45886080, 45928832]
+
+ data = [
+ ['Types', 'Sub Type', 'Value 1', 'Value 2', 'Value 3'],
+ ['Type 1', 'Sub Type A', 5000, 8000, 6000],
+ ['', 'Sub Type B', 2000, 3000, 4000],
+ ['', 'Sub Type C', 250, 1000, 2000],
+ ['Type 2', 'Sub Type D', 6000, 6000, 6500],
+ ['', 'Sub Type E', 500, 300, 200],
+ ]
+
+ cat_data = [
+ ['Type 1', None, None, 'Type 2', None],
+ ['Sub Type A', 'Sub Type B', 'Sub Type C',
+ 'Sub Type D', 'Sub Type E']
+ ]
+
+ for row_num, row_data in enumerate(data):
+ worksheet.write_row(row_num, 0, row_data)
+
+ chart.add_series({
+ 'name': '=Sheet1!$C$1',
+ 'categories': '=Sheet1!$A$2:$B$6',
+ 'values': '=Sheet1!$C$2:$C$6',
+ 'categories_data': cat_data,
+ })
+
+ chart.add_series({
+ 'name': '=Sheet1!$D$1',
+ 'categories': '=Sheet1!$A$2:$B$6',
+ 'values': '=Sheet1!$D$2:$D$6',
+ })
+
+ chart.add_series({
+ 'name': '=Sheet1!$E$1',
+ 'categories': '=Sheet1!$A$2:$B$6',
+ 'values': '=Sheet1!$E$2:$E$6',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_column01.py b/xlsxwriter/test/comparison/test_chart_column01.py
new file mode 100644
index 0000000..b4fdaa2
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_column01.py
@@ -0,0 +1,92 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_column01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [43424000, 43434368]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_unused_chart(self):
+ """Test charts that were created but not inserted."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ # Unused chart.
+ workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [43424000, 43434368]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_column02.py b/xlsxwriter/test/comparison/test_chart_column02.py
new file mode 100644
index 0000000..4d0da30
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_column02.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_column02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column', 'subtype': 'stacked'})
+
+ chart.axis_ids = [49388544, 69387008]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_column03.py b/xlsxwriter/test/comparison/test_chart_column03.py
new file mode 100644
index 0000000..42332d5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_column03.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_column03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column', 'subtype': 'percent_stacked'})
+
+ chart.axis_ids = [49388544, 69387008]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_column04.py b/xlsxwriter/test/comparison/test_chart_column04.py
new file mode 100644
index 0000000..fce292c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_column04.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_column04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/workbook.xml': ['<fileVersion', '<calcPr']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [63591936, 63593856]
+ chart.axis2_ids = [63613568, 63612032]
+
+ data = [[1, 2, 3, 4, 5],
+ [6, 8, 6, 4, 2]]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5', 'y2_axis': 1})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_column05.py b/xlsxwriter/test/comparison/test_chart_column05.py
new file mode 100644
index 0000000..195040a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_column05.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_column05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet('Foo')
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [47292800, 47295104]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Foo!$A$1:$A$5'})
+ chart.add_series({'values': '=Foo!$B$1:$B$5'})
+ chart.add_series({'values': '=Foo!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_column06.py b/xlsxwriter/test/comparison/test_chart_column06.py
new file mode 100644
index 0000000..8a8bf37
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_column06.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_column06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [47363584, 49673344]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet1.write_column('A1', data[0])
+ worksheet1.write_column('B1', data[1])
+ worksheet1.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet2.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_column07.py b/xlsxwriter/test/comparison/test_chart_column07.py
new file mode 100644
index 0000000..62e9c30
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_column07.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_column07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [68810240, 68811776]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=(Sheet1!$A$1:$A$2,Sheet1!$A$4:$A$5)',
+ 'values_data': [1, 2, 4, 5],
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_column08.py b/xlsxwriter/test/comparison/test_chart_column08.py
new file mode 100644
index 0000000..9e88d36
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_column08.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_column08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [68809856, 68811392]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=(Sheet1!$A$1:$A$2,Sheet1!$A$4:$A$5)',
+ 'values': '=(Sheet1!$B$1:$B$2,Sheet1!$B$4:$B$5)',
+ 'categories_data': [1, 2, 4, 5],
+ 'values_data': [2, 4, 8, 10],
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_column09.py b/xlsxwriter/test/comparison/test_chart_column09.py
new file mode 100644
index 0000000..0e40ef0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_column09.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_column09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [47400832, 61387136]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [1, 2, 3, 2, 1],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5'
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_column10.py b/xlsxwriter/test/comparison/test_chart_column10.py
new file mode 100644
index 0000000..bbde958
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_column10.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_column10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45686144, 45722240]
+
+ data = [
+ ['A', 'B', 'C', 'D', 'E'],
+ [1, 2, 3, 2, 1],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5'
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_column11.py b/xlsxwriter/test/comparison/test_chart_column11.py
new file mode 100644
index 0000000..c765c44
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_column11.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_column11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [46847488, 46849408]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_style(1)
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_column12.py b/xlsxwriter/test/comparison/test_chart_column12.py
new file mode 100644
index 0000000..1d89838
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_column12.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_column12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [46847488, 46849408]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_style(48)
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_combined01.py b/xlsxwriter/test/comparison/test_chart_combined01.py
new file mode 100644
index 0000000..b010555
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_combined01.py
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_combined01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart1 = workbook.add_chart({'type': 'column'})
+
+ chart1.axis_ids = [84882560, 84884096]
+
+ data = [
+ [2, 7, 3, 6, 2],
+ [20, 25, 10, 10, 20],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart1.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart1.add_series({'values': '=Sheet1!$B$1:$B$5'})
+
+ worksheet.insert_chart('E9', chart1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_combined02.py b/xlsxwriter/test/comparison/test_chart_combined02.py
new file mode 100644
index 0000000..d4b61bf
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_combined02.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_combined02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:dispBlanksAs']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart1 = workbook.add_chart({'type': 'column'})
+ chart2 = workbook.add_chart({'type': 'line'})
+
+ data = [
+ [2, 7, 3, 6, 2],
+ [20, 25, 10, 10, 20],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart1.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart2.add_series({'values': '=Sheet1!$B$1:$B$5'})
+
+ chart1.combine(chart2)
+
+ worksheet.insert_chart('E9', chart1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_combined03.py b/xlsxwriter/test/comparison/test_chart_combined03.py
new file mode 100644
index 0000000..9848bb2
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_combined03.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_combined03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:dispBlanksAs']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart1 = workbook.add_chart({'type': 'column'})
+ chart2 = workbook.add_chart({'type': 'line'})
+
+ data = [
+ [2, 7, 3, 6, 2],
+ [20, 25, 10, 10, 20],
+ [4, 2, 5, 2, 1],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart1.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart1.add_series({'values': '=Sheet1!$B$1:$B$5'})
+
+ chart2.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart1.combine(chart2)
+
+ worksheet.insert_chart('E9', chart1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_combined04.py b/xlsxwriter/test/comparison/test_chart_combined04.py
new file mode 100644
index 0000000..7c88631
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_combined04.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_combined04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:dispBlanksAs',
+ '<c:tickLblPos',
+ '<c:crosses']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart1 = workbook.add_chart({'type': 'column'})
+ chart2 = workbook.add_chart({'type': 'line'})
+
+ data = [
+ [2, 7, 3, 6, 2],
+ [20, 25, 10, 10, 20],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart1.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart2.add_series({'values': '=Sheet1!$B$1:$B$5', 'y2_axis': 1})
+
+ chart1.combine(chart2)
+
+ worksheet.insert_chart('E9', chart1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_combined05.py b/xlsxwriter/test/comparison/test_chart_combined05.py
new file mode 100644
index 0000000..d6229fc
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_combined05.py
@@ -0,0 +1,64 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_combined05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:dispBlanksAs',
+ '<c:tickLblPos',
+ '<c:crosses',
+ '<c:axPos']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart1 = workbook.add_chart({'type': 'bar'})
+ chart2 = workbook.add_chart({'type': 'line'})
+
+ chart1.axis_ids = [60914304, 78899072]
+ chart2.axis2_ids = [85542016, 85183872]
+
+ data = [
+ [2, 7, 3, 6, 2],
+ [20, 25, 10, 10, 20],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart1.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart2.add_series({'values': '=Sheet1!$B$1:$B$5', 'y2_axis': 1})
+
+ chart1.combine(chart2)
+
+ worksheet.insert_chart('E9', chart1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_combined06.py b/xlsxwriter/test/comparison/test_chart_combined06.py
new file mode 100644
index 0000000..fb3f14c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_combined06.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_combined06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:dispBlanksAs']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart1 = workbook.add_chart({'type': 'area'})
+ chart2 = workbook.add_chart({'type': 'column'})
+
+ chart1.axis_ids = [91755648, 91757952]
+ chart2.axis_ids = [91755648, 91757952]
+
+ data = [
+ [2, 7, 3, 6, 2],
+ [20, 25, 10, 10, 20],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart1.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart2.add_series({'values': '=Sheet1!$B$1:$B$5'})
+
+ chart1.combine(chart2)
+
+ chart1.cross_between = 'between'
+
+ worksheet.insert_chart('E9', chart1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_combined07.py b/xlsxwriter/test/comparison/test_chart_combined07.py
new file mode 100644
index 0000000..9afabcf
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_combined07.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_combined07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:dispBlanksAs']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart1 = workbook.add_chart({'type': 'column'})
+ chart2 = workbook.add_chart({'type': 'scatter'})
+
+ chart1.axis_ids = [81267328, 81297792]
+ chart2.axis_ids = [81267328, 81297792]
+
+ data = [
+ [2, 3, 4, 5, 6],
+ [20, 25, 10, 10, 20],
+ [5, 10, 15, 10, 5],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart1.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5'
+ })
+
+ chart2.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5'
+ })
+
+ chart1.combine(chart2)
+
+ worksheet.insert_chart('E9', chart1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_combined08.py b/xlsxwriter/test/comparison/test_chart_combined08.py
new file mode 100644
index 0000000..a7a9e21
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_combined08.py
@@ -0,0 +1,82 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_combined08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ # TODO. There are too many ignored elements here. Remove when the axis
+ # writing is fixed for secondary scatter charts.
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:dispBlanksAs',
+ '<c:crossBetween',
+ '<c:tickLblPos',
+ '<c:auto',
+ '<c:valAx>',
+ '<c:catAx>',
+ '</c:valAx>',
+ '</c:catAx>',
+ '<c:crosses',
+ '<c:lblOffset',
+ '<c:lblAlgn']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart1 = workbook.add_chart({'type': 'column'})
+ chart2 = workbook.add_chart({'type': 'scatter'})
+
+ chart1.axis_ids = [81267328, 81297792]
+ chart2.axis_ids = [81267328, 81297792]
+ chart2.axis2_ids = [89510656, 84556032]
+
+ data = [
+ [2, 3, 4, 5, 6],
+ [20, 25, 10, 10, 20],
+ [5, 10, 15, 10, 5],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart1.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5'
+ })
+
+ chart2.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'y2_axis': 1,
+ })
+
+ chart1.combine(chart2)
+
+ worksheet.insert_chart('E9', chart1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_combined09.py b/xlsxwriter/test/comparison/test_chart_combined09.py
new file mode 100644
index 0000000..0e3e972
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_combined09.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_combined09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:dispBlanksAs',
+ '<c:tickLblPos']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart1 = workbook.add_chart({'type': 'column'})
+ chart2 = workbook.add_chart({'type': 'line'})
+
+ chart1.axis_ids = [114984064, 114985600]
+ chart2.axis2_ids = [114988928, 114987392]
+
+ data = [
+ [2, 7, 3, 6, 2],
+ [20, 25, 10, 10, 20],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart1.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart2.add_series({'values': '=Sheet1!$B$1:$B$5', 'y2_axis': 1})
+
+ chart1.set_y_axis({'num_font': {'bold': 1, 'baseline': -1}})
+ chart2.set_y2_axis({'num_font': {'bold': 1, 'baseline': -1}})
+
+ chart1.combine(chart2)
+
+ worksheet.insert_chart('E9', chart1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_crossing01.py b/xlsxwriter/test/comparison/test_chart_crossing01.py
new file mode 100644
index 0000000..45c2c3a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_crossing01.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_crossing01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [43812352, 43814272]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_y_axis({'crossing': 'max'})
+
+ chart.set_x_axis({'position': 't'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_crossing02.py b/xlsxwriter/test/comparison/test_chart_crossing02.py
new file mode 100644
index 0000000..23dd0b8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_crossing02.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_crossing02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [43812352, 43814272]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'crossing': 3})
+ chart.set_y_axis({'crossing': 8})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_crossing03.py b/xlsxwriter/test/comparison/test_chart_crossing03.py
new file mode 100644
index 0000000..daff7c5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_crossing03.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_crossing03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [54519680, 54518144]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_x_axis({'crossing': 'max', 'position': 'top'})
+ chart.set_y_axis({'crossing': 'max', 'position': 'right'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_crossing04.py b/xlsxwriter/test/comparison/test_chart_crossing04.py
new file mode 100644
index 0000000..c30b5fb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_crossing04.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_crossing04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [54519296, 54517760]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_x_axis({'crossing': 3})
+ chart.set_y_axis({'crossing': 8})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels01.py b/xlsxwriter/test/comparison/test_chart_data_labels01.py
new file mode 100644
index 0000000..404b2ac
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels01.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45848832, 47718784]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'position': 'outside_end'},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'position': 'inside_base'},
+ })
+
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels02.py b/xlsxwriter/test/comparison/test_chart_data_labels02.py
new file mode 100644
index 0000000..d2c323f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels02.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [47721856, 53641216]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'position': 'inside_end'},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'position': 'center'},
+ })
+
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels03.py b/xlsxwriter/test/comparison/test_chart_data_labels03.py
new file mode 100644
index 0000000..e6a6b15
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels03.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [45693952, 45762816]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'position': 'outside_end'},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'position': 'inside_base'},
+ })
+
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels04.py b/xlsxwriter/test/comparison/test_chart_data_labels04.py
new file mode 100644
index 0000000..9cb0773
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels04.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [47719168, 47720704]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'position': 'inside_end'},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'position': 'center'},
+ })
+
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels05.py b/xlsxwriter/test/comparison/test_chart_data_labels05.py
new file mode 100644
index 0000000..fa1f0b1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels05.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [45678592, 45680128]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'position': 'right'},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'position': 'top'},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'data_labels': {'value': 1, 'position': 'bottom'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels06.py b/xlsxwriter/test/comparison/test_chart_data_labels06.py
new file mode 100644
index 0000000..6cff167
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels06.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [45678592, 45680128]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'position': 'right'},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'position': 'left'},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'data_labels': {'value': 1, 'position': 'center'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels07.py b/xlsxwriter/test/comparison/test_chart_data_labels07.py
new file mode 100644
index 0000000..ec6a05c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels07.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'area'})
+
+ chart.axis_ids = [45703168, 45705472]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'position': 'center'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels08.py b/xlsxwriter/test/comparison/test_chart_data_labels08.py
new file mode 100644
index 0000000..8849860
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels08.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [45740416, 45705856]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'position': 'right'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels09.py b/xlsxwriter/test/comparison/test_chart_data_labels09.py
new file mode 100644
index 0000000..d0b2d91
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels09.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [45740416, 45705856]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'position': 'above'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'data_labels': {'value': 1, 'position': 'below'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels10.py b/xlsxwriter/test/comparison/test_chart_data_labels10.py
new file mode 100644
index 0000000..77f90c5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels10.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [45740416, 45705856]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'position': 'left'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'data_labels': {'value': 1, 'position': 'center'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels11.py b/xlsxwriter/test/comparison/test_chart_data_labels11.py
new file mode 100644
index 0000000..7a87e9d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels11.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'leader_lines': 1, 'position': 'best_fit'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels12.py b/xlsxwriter/test/comparison/test_chart_data_labels12.py
new file mode 100644
index 0000000..862bb01
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels12.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'leader_lines': 1, 'position': 'outside_end'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels13.py b/xlsxwriter/test/comparison/test_chart_data_labels13.py
new file mode 100644
index 0000000..7274bac
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels13.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels13.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'leader_lines': 1, 'position': 'inside_end'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels14.py b/xlsxwriter/test/comparison/test_chart_data_labels14.py
new file mode 100644
index 0000000..95b7cb8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels14.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels14.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'leader_lines': 1, 'position': 'center'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels15.py b/xlsxwriter/test/comparison/test_chart_data_labels15.py
new file mode 100644
index 0000000..a651f36
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels15.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels15.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'doughnut'})
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'leader_lines': 1, 'position': 'best_fit'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels16.py b/xlsxwriter/test/comparison/test_chart_data_labels16.py
new file mode 100644
index 0000000..1f582fe
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels16.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels16.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'radar'})
+
+ chart.axis_ids = [45858816, 45860352]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'position': 'center'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels17.py b/xlsxwriter/test/comparison/test_chart_data_labels17.py
new file mode 100644
index 0000000..fc5b5ca
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels17.py
@@ -0,0 +1,76 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels17.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'stock'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [45740032, 45747200]
+
+ data = [
+ [39083, 39084, 39085, 39086, 39087],
+ [27.2, 25.03, 19.05, 20.34, 18.5],
+ [23.49, 19.55, 15.12, 17.84, 16.34],
+ [25.45, 23.05, 17.32, 20.45, 17.34],
+ ]
+
+ for row in range(5):
+ worksheet.write(row, 0, data[0][row], date_format)
+ worksheet.write(row, 1, data[1][row])
+ worksheet.write(row, 2, data[2][row])
+ worksheet.write(row, 3, data[3][row])
+
+ worksheet.set_column('A:D', 11)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$D$1:$D$5',
+ 'data_labels': {'value': 1, 'position': 'right'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels18.py b/xlsxwriter/test/comparison/test_chart_data_labels18.py
new file mode 100644
index 0000000..93d3d79
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels18.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels18.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45740416, 45747584]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'category': 1},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'category': 1, 'separator': ';'},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'data_labels': {'value': 1, 'category': 1, 'separator': '.'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels19.py b/xlsxwriter/test/comparison/test_chart_data_labels19.py
new file mode 100644
index 0000000..aba2f69
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels19.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels19.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45740416, 45747584]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'category': 1},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'category': 1, 'separator': "\n"},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'data_labels': {'value': 1, 'category': 1, 'separator': ' '},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels20.py b/xlsxwriter/test/comparison/test_chart_data_labels20.py
new file mode 100644
index 0000000..e406a9c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels20.py
@@ -0,0 +1,64 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels20.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45740032, 45743104]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'legend_key': True},
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels21.py b/xlsxwriter/test/comparison/test_chart_data_labels21.py
new file mode 100644
index 0000000..7a9ad19
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels21.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels21.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {
+ 'value': True,
+ 'category': True,
+ 'series_name': True,
+ 'percentage': True,
+ 'separator': ';',
+ 'leader_lines': True,
+ 'position': 'inside_end',
+ 'legend_key': True,
+ 'num_format': '#,##0.00',
+ 'font': {'name': 'Consolas', 'baseline': 1 * -1, 'pitch_family': 49, 'charset': 0}
+ },
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels22.py b/xlsxwriter/test/comparison/test_chart_data_labels22.py
new file mode 100644
index 0000000..ddf283f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels22.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels22.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45705856, 45740416]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1, 'num_format': '#,##0.00'},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'position': 'inside_base', 'num_format': '0.00'},
+ })
+
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels23.py b/xlsxwriter/test/comparison/test_chart_data_labels23.py
new file mode 100644
index 0000000..0fcf934
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels23.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels23.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45705856, 45740416]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {
+ 'value': 1,
+ 'font': {'name': 'Consolas', 'baseline': 1 * -1, 'pitch_family': 49, 'charset': 0}
+ },
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'position': 'inside_base'},
+ })
+
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels24.py b/xlsxwriter/test/comparison/test_chart_data_labels24.py
new file mode 100644
index 0000000..f65d2fc
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels24.py
@@ -0,0 +1,74 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels24.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45937792, 45939712]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {
+ 'value': 1,
+ 'font': {'name': 'Consolas',
+ 'size': 12,
+ 'baseline': 1 * -1,
+ 'pitch_family': 49,
+ 'charset': 0}
+ },
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'position': 'inside_base'},
+ })
+
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_data_labels25.py b/xlsxwriter/test/comparison/test_chart_data_labels25.py
new file mode 100644
index 0000000..22d97ae
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_data_labels25.py
@@ -0,0 +1,71 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_data_labels25.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [108652800, 108656128]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'data_labels': {'value': 1,
+ 'position': 'outside_end',
+ 'font': {'rotation': 45, 'baseline': -1}},
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1,
+ 'position': 'inside_base',
+ 'font': {'rotation': -45, 'baseline': -1}},
+ })
+
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_date01.py b/xlsxwriter/test/comparison/test_chart_date01.py
new file mode 100644
index 0000000..ce7f6c8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_date01.py
@@ -0,0 +1,75 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from datetime import date
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_date01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [73655040, 73907584]
+
+ worksheet.set_column('A:A', 12)
+
+ dates = [date(2013, 1, 1),
+ date(2013, 1, 2),
+ date(2013, 1, 3),
+ date(2013, 1, 4),
+ date(2013, 1, 5),
+ date(2013, 1, 6),
+ date(2013, 1, 7),
+ date(2013, 1, 8),
+ date(2013, 1, 9),
+ date(2013, 1, 10)]
+
+ values = [10, 30, 20, 40, 20, 60, 50, 40, 30, 30]
+
+ worksheet.write_column('A1', dates, date_format)
+ worksheet.write_column('B1', values)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$10',
+ 'values': '=Sheet1!$B$1:$B$10',
+ })
+
+ chart.set_x_axis({
+ 'date_axis': True,
+ 'num_format': 'dd/mm/yyyy',
+ 'num_format_linked': True,
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_date02.py b/xlsxwriter/test/comparison/test_chart_date02.py
new file mode 100644
index 0000000..b8cebdb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_date02.py
@@ -0,0 +1,77 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from datetime import date
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_date02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [55112064, 55115136]
+
+ worksheet.set_column('A:A', 12)
+
+ dates = [date(2013, 1, 1),
+ date(2013, 1, 2),
+ date(2013, 1, 3),
+ date(2013, 1, 4),
+ date(2013, 1, 5),
+ date(2013, 1, 6),
+ date(2013, 1, 7),
+ date(2013, 1, 8),
+ date(2013, 1, 9),
+ date(2013, 1, 10)]
+
+ values = [10, 30, 20, 40, 20, 60, 50, 40, 30, 30]
+
+ worksheet.write_column('A1', dates, date_format)
+ worksheet.write_column('B1', values)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$10',
+ 'values': '=Sheet1!$B$1:$B$10',
+ })
+
+ chart.set_x_axis({
+ 'date_axis': True,
+ 'min': int(worksheet._convert_date_time(date(2013, 1, 2))),
+ 'max': int(worksheet._convert_date_time(date(2013, 1, 9))),
+ 'num_format': 'dd/mm/yyyy',
+ 'num_format_linked': True,
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_date03.py b/xlsxwriter/test/comparison/test_chart_date03.py
new file mode 100644
index 0000000..2214bf3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_date03.py
@@ -0,0 +1,77 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from datetime import date
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_date03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [51761152, 51762688]
+
+ worksheet.set_column('A:A', 12)
+
+ dates = [date(2013, 1, 1),
+ date(2013, 1, 2),
+ date(2013, 1, 3),
+ date(2013, 1, 4),
+ date(2013, 1, 5),
+ date(2013, 1, 6),
+ date(2013, 1, 7),
+ date(2013, 1, 8),
+ date(2013, 1, 9),
+ date(2013, 1, 10)]
+
+ values = [10, 30, 20, 40, 20, 60, 50, 40, 30, 30]
+
+ worksheet.write_column('A1', dates, date_format)
+ worksheet.write_column('B1', values)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$10',
+ 'values': '=Sheet1!$B$1:$B$10',
+ })
+
+ chart.set_x_axis({
+ 'date_axis': True,
+ 'minor_unit': 1,
+ 'major_unit': 1,
+ 'num_format': 'dd/mm/yyyy',
+ 'num_format_linked': True,
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_date04.py b/xlsxwriter/test/comparison/test_chart_date04.py
new file mode 100644
index 0000000..ef2c8bf
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_date04.py
@@ -0,0 +1,79 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from datetime import date
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_date04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [51761152, 51762688]
+
+ worksheet.set_column('A:A', 12)
+
+ dates = [date(2013, 1, 1),
+ date(2013, 1, 2),
+ date(2013, 1, 3),
+ date(2013, 1, 4),
+ date(2013, 1, 5),
+ date(2013, 1, 6),
+ date(2013, 1, 7),
+ date(2013, 1, 8),
+ date(2013, 1, 9),
+ date(2013, 1, 10)]
+
+ values = [10, 30, 20, 40, 20, 60, 50, 40, 30, 30]
+
+ worksheet.write_column('A1', dates, date_format)
+ worksheet.write_column('B1', values)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$10',
+ 'values': '=Sheet1!$B$1:$B$10',
+ })
+
+ chart.set_x_axis({
+ 'date_axis': True,
+ 'minor_unit': 1,
+ 'major_unit': 1,
+ 'minor_unit_type': 'months',
+ 'major_unit_type': 'years',
+ 'num_format': 'dd/mm/yyyy',
+ 'num_format_linked': True,
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_date05.py b/xlsxwriter/test/comparison/test_chart_date05.py
new file mode 100644
index 0000000..40ac6a3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_date05.py
@@ -0,0 +1,75 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from datetime import date
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_date05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [45937408, 45939328]
+
+ worksheet.set_column('A:A', 12)
+
+ dates = [date(2013, 1, 1),
+ date(2013, 1, 2),
+ date(2013, 1, 3),
+ date(2013, 1, 4),
+ date(2013, 1, 5),
+ date(2013, 1, 6),
+ date(2013, 1, 7),
+ date(2013, 1, 8),
+ date(2013, 1, 9),
+ date(2013, 1, 10)]
+
+ values = [10, 30, 20, 40, 20, 60, 50, 40, 30, 30]
+
+ worksheet.write_column('A1', dates, date_format)
+ worksheet.write_column('B1', values)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$10',
+ 'values': '=Sheet1!$B$1:$B$10',
+ })
+
+ chart.set_x_axis({
+ 'text_axis': True,
+ 'num_format': 'dd/mm/yyyy',
+ 'num_format_linked': True,
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_display_units01.py b/xlsxwriter/test/comparison/test_chart_display_units01.py
new file mode 100644
index 0000000..c850124
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_display_units01.py
@@ -0,0 +1,52 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_display_units01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [69572096, 93549312]
+
+ data = [
+ [10000000, 20000000, 30000000, 20000000, 10000000],
+ ]
+
+ worksheet.write_column(0, 0, data[0])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_display_units02.py b/xlsxwriter/test/comparison/test_chart_display_units02.py
new file mode 100644
index 0000000..abb6818
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_display_units02.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_display_units02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [56159232, 61364096]
+
+ data = [
+ [10000000, 20000000, 30000000, 20000000, 10000000],
+ ]
+
+ worksheet.write_column(0, 0, data[0])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart.set_y_axis({'display_units': 'hundreds', 'display_units_visible': 0})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_display_units03.py b/xlsxwriter/test/comparison/test_chart_display_units03.py
new file mode 100644
index 0000000..4023b62
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_display_units03.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_display_units03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [56159232, 61364096]
+
+ data = [
+ [10000000, 20000000, 30000000, 20000000, 10000000],
+ ]
+
+ worksheet.write_column(0, 0, data[0])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart.set_y_axis({'display_units': 'thousands', 'display_units_visible': 0})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_display_units04.py b/xlsxwriter/test/comparison/test_chart_display_units04.py
new file mode 100644
index 0000000..14930f3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_display_units04.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_display_units04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [56159232, 61364096]
+
+ data = [
+ [10000000, 20000000, 30000000, 20000000, 10000000],
+ ]
+
+ worksheet.write_column(0, 0, data[0])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart.set_y_axis({'display_units': 'ten_thousands', 'display_units_visible': 0})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_display_units05.py b/xlsxwriter/test/comparison/test_chart_display_units05.py
new file mode 100644
index 0000000..3945cd4
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_display_units05.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_display_units05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [56159232, 61364096]
+
+ data = [
+ [10000000, 20000000, 30000000, 20000000, 10000000],
+ ]
+
+ worksheet.write_column(0, 0, data[0])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart.set_y_axis({'display_units': 'hundred_thousands', 'display_units_visible': 0})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_display_units06.py b/xlsxwriter/test/comparison/test_chart_display_units06.py
new file mode 100644
index 0000000..694563e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_display_units06.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_display_units06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [93548544, 93550464]
+
+ data = [
+ [10000000, 20000000, 30000000, 20000000, 10000000],
+ ]
+
+ worksheet.write_column(0, 0, data[0])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart.set_y_axis({'display_units': 'millions', 'display_units_visible': 0})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_display_units07.py b/xlsxwriter/test/comparison/test_chart_display_units07.py
new file mode 100644
index 0000000..21ea88b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_display_units07.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_display_units07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [56159232, 61364096]
+
+ data = [
+ [10000000, 20000000, 30000000, 20000000, 10000000],
+ ]
+
+ worksheet.write_column(0, 0, data[0])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart.set_y_axis({'display_units': 'ten_millions', 'display_units_visible': 0})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_display_units08.py b/xlsxwriter/test/comparison/test_chart_display_units08.py
new file mode 100644
index 0000000..ed12003
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_display_units08.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_display_units08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [56159232, 61364096]
+
+ data = [
+ [10000000, 20000000, 30000000, 20000000, 10000000],
+ ]
+
+ worksheet.write_column(0, 0, data[0])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart.set_y_axis({'display_units': 'hundred_millions', 'display_units_visible': 0})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_display_units09.py b/xlsxwriter/test/comparison/test_chart_display_units09.py
new file mode 100644
index 0000000..7ee8a78
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_display_units09.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_display_units09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [56159232, 61364096]
+
+ data = [
+ [10000000, 20000000, 30000000, 20000000, 10000000],
+ ]
+
+ worksheet.write_column(0, 0, data[0])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart.set_y_axis({'display_units': 'billions', 'display_units_visible': 0})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_display_units10.py b/xlsxwriter/test/comparison/test_chart_display_units10.py
new file mode 100644
index 0000000..e340a57
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_display_units10.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_display_units10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [56159232, 61364096]
+
+ data = [
+ [10000000, 20000000, 30000000, 20000000, 10000000],
+ ]
+
+ worksheet.write_column(0, 0, data[0])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart.set_y_axis({'display_units': 'trillions', 'display_units_visible': 0})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_display_units11.py b/xlsxwriter/test/comparison/test_chart_display_units11.py
new file mode 100644
index 0000000..62eb404
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_display_units11.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_display_units11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [69559424, 69560960]
+
+ data = [
+ [10000000, 20000000, 30000000, 20000000, 10000000],
+ ]
+
+ worksheet.write_column(0, 0, data[0])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart.set_y_axis({'display_units': 'hundreds'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_display_units12.py b/xlsxwriter/test/comparison/test_chart_display_units12.py
new file mode 100644
index 0000000..158ab30
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_display_units12.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_display_units12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [93550464, 93548544]
+
+ data = [
+ [10000000, 20000000, 30000000, 20000000, 10000000],
+ ]
+
+ worksheet.write_column(0, 0, data[0])
+ worksheet.write_column(0, 1, data[0])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5'
+ })
+
+ chart.set_y_axis({'display_units': 'hundreds',
+ 'display_units_visible': False})
+ chart.set_x_axis({'display_units': 'thousands',
+ 'display_units_visible': False})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_doughnut01.py b/xlsxwriter/test/comparison/test_chart_doughnut01.py
new file mode 100644
index 0000000..31471e2
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_doughnut01.py
@@ -0,0 +1,55 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_doughnut01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'doughnut'})
+
+ data = [
+ [2, 4, 6],
+ [60, 30, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$3',
+ 'values': '=Sheet1!$B$1:$B$3',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_doughnut02.py b/xlsxwriter/test/comparison/test_chart_doughnut02.py
new file mode 100644
index 0000000..1caa043
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_doughnut02.py
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_doughnut02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'doughnut'})
+
+ data = [
+ [2, 4, 6],
+ [60, 30, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$3',
+ 'values': '=Sheet1!$B$1:$B$3',
+ })
+
+ chart.set_hole_size(10)
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_doughnut03.py b/xlsxwriter/test/comparison/test_chart_doughnut03.py
new file mode 100644
index 0000000..6e03176
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_doughnut03.py
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_doughnut03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'doughnut'})
+
+ data = [
+ [2, 4, 6],
+ [60, 30, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$3',
+ 'values': '=Sheet1!$B$1:$B$3',
+ })
+
+ chart.set_hole_size(90)
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_doughnut04.py b/xlsxwriter/test/comparison/test_chart_doughnut04.py
new file mode 100644
index 0000000..6b3fc9c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_doughnut04.py
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_doughnut04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'doughnut'})
+
+ data = [
+ [2, 4, 6],
+ [60, 30, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$3',
+ 'values': '=Sheet1!$B$1:$B$3',
+ })
+
+ chart.set_rotation(30)
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_doughnut05.py b/xlsxwriter/test/comparison/test_chart_doughnut05.py
new file mode 100644
index 0000000..a0b8b29
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_doughnut05.py
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_doughnut05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'doughnut'})
+
+ data = [
+ [2, 4, 6],
+ [60, 30, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$3',
+ 'values': '=Sheet1!$B$1:$B$3',
+ })
+
+ chart.set_rotation(360)
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_doughnut06.py b/xlsxwriter/test/comparison/test_chart_doughnut06.py
new file mode 100644
index 0000000..e4fc095
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_doughnut06.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_doughnut06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'doughnut'})
+
+ data = [
+ [2, 4, 6],
+ [60, 30, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$3',
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$3',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_drop_lines01.py b/xlsxwriter/test/comparison/test_chart_drop_lines01.py
new file mode 100644
index 0000000..e912549
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_drop_lines01.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_drop_lines01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with drop down lines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [48034944, 48036864]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.set_drop_lines()
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_drop_lines02.py b/xlsxwriter/test/comparison/test_chart_drop_lines02.py
new file mode 100644
index 0000000..b8449aa
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_drop_lines02.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_drop_lines02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with drop down lines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [70938624, 70940160]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.set_drop_lines({'line': {'color': 'red',
+ 'dash_type':
+ 'square_dot'}})
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_drop_lines03.py b/xlsxwriter/test/comparison/test_chart_drop_lines03.py
new file mode 100644
index 0000000..e8eea30
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_drop_lines03.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_drop_lines03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with drop down lines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'area'})
+
+ chart.axis_ids = [61151872, 63947136]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 1, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.set_drop_lines()
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_drop_lines04.py b/xlsxwriter/test/comparison/test_chart_drop_lines04.py
new file mode 100644
index 0000000..c21cf01
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_drop_lines04.py
@@ -0,0 +1,77 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_drop_lines04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with drop down lines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'stock'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [49019520, 49222016]
+
+ data = [
+ [39083, 39084, 39085, 39086, 39087],
+ [27.2, 25.03, 19.05, 20.34, 18.5],
+ [23.49, 19.55, 15.12, 17.84, 16.34],
+ [25.45, 23.05, 17.32, 20.45, 17.34],
+ ]
+
+ for row in range(5):
+ worksheet.write(row, 0, data[0][row], date_format)
+ worksheet.write(row, 1, data[1][row])
+ worksheet.write(row, 2, data[2][row])
+ worksheet.write(row, 3, data[3][row])
+
+ worksheet.set_column('A:D', 11)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$D$1:$D$5',
+ })
+
+ chart.set_drop_lines()
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_errorbars01.py b/xlsxwriter/test/comparison/test_chart_errorbars01.py
new file mode 100644
index 0000000..c2e86ef
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_errorbars01.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_errorbars01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with error bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [63386752, 63388288]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'y_error_bars': {'type': 'standard_error'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_errorbars02.py b/xlsxwriter/test/comparison/test_chart_errorbars02.py
new file mode 100644
index 0000000..35fbe43
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_errorbars02.py
@@ -0,0 +1,77 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_errorbars02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with error bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [63385984, 63387904]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'y_error_bars': {
+ 'type': 'fixed',
+ 'value': 2,
+ 'end_style': 0,
+ 'direction': 'minus'
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'y_error_bars': {
+ 'type': 'percentage',
+ 'value': 5,
+ 'direction': 'plus'
+
+ },
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_errorbars03.py b/xlsxwriter/test/comparison/test_chart_errorbars03.py
new file mode 100644
index 0000000..d72f324
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_errorbars03.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_errorbars03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with error bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [52288896, 53605504]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'y_error_bars': {
+ 'type': 'standard_error',
+ 'line': {'color': 'red', 'dash_type': 'round_dot'}
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_errorbars04.py b/xlsxwriter/test/comparison/test_chart_errorbars04.py
new file mode 100644
index 0000000..164e202
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_errorbars04.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_errorbars04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with error bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [61626624, 69664128]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'y_error_bars': {'type': 'standard_error'},
+ 'x_error_bars': {'type': 'standard_deviation', 'value': 1},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_errorbars05.py b/xlsxwriter/test/comparison/test_chart_errorbars05.py
new file mode 100644
index 0000000..938ef86
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_errorbars05.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_errorbars05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with error bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [49016832, 49019136]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'x_error_bars': {'type': 'standard_error'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_errorbars06.py b/xlsxwriter/test/comparison/test_chart_errorbars06.py
new file mode 100644
index 0000000..cf690d3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_errorbars06.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_errorbars06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with error bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45472384, 49016832]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'y_error_bars': {'type': 'standard_error'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_errorbars07.py b/xlsxwriter/test/comparison/test_chart_errorbars07.py
new file mode 100644
index 0000000..6a4c663
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_errorbars07.py
@@ -0,0 +1,78 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_errorbars07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with error bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'stock'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [45470848, 45472768]
+
+ data = [
+ [39083, 39084, 39085, 39086, 39087],
+ [27.2, 25.03, 19.05, 20.34, 18.5],
+ [23.49, 19.55, 15.12, 17.84, 16.34],
+ [25.45, 23.05, 17.32, 20.45, 17.34],
+ ]
+
+ for row in range(5):
+ worksheet.write(row, 0, data[0][row], date_format)
+ worksheet.write(row, 1, data[1][row])
+ worksheet.write(row, 2, data[2][row])
+ worksheet.write(row, 3, data[3][row])
+
+ worksheet.set_column('A:D', 11)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'y_error_bars': {'type': 'standard_error'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'y_error_bars': {'type': 'standard_error'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$D$1:$D$5',
+ 'y_error_bars': {'type': 'standard_error'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_errorbars08.py b/xlsxwriter/test/comparison/test_chart_errorbars08.py
new file mode 100644
index 0000000..82bfe54
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_errorbars08.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_errorbars08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with error bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [69198976, 69200896]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'y_error_bars': {
+ 'type': 'custom',
+ 'plus_values': [1],
+ 'minus_values': [2]
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_errorbars09.py b/xlsxwriter/test/comparison/test_chart_errorbars09.py
new file mode 100644
index 0000000..87eb7ee
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_errorbars09.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_errorbars09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with error bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [71917952, 71919488]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'y_error_bars': {
+ 'type': 'custom',
+ 'plus_values': [1, 2, 3],
+ 'minus_values': [2, 4, 6]
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_errorbars10.py b/xlsxwriter/test/comparison/test_chart_errorbars10.py
new file mode 100644
index 0000000..f63e112
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_errorbars10.py
@@ -0,0 +1,72 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_errorbars10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with error bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [69198976, 69200896]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'y_error_bars': {
+ 'type': 'custom',
+ 'plus_values': '=Sheet1!$A$1',
+ 'minus_values': '=Sheet1!$B$1:$B$3',
+ 'plus_data': [1],
+ 'minus_data': [2, 4, 6],
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_errorbars11.py b/xlsxwriter/test/comparison/test_chart_errorbars11.py
new file mode 100644
index 0000000..46223cd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_errorbars11.py
@@ -0,0 +1,75 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_errorbars10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_chart_errorbars11.xlsx'
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+
+ # Test for issue #115. We don't add plus_data and minus_data, like in
+ # test_chart_errorbars10.py, as would be done from user API. Instead
+ # we ignore the point data in the comparison test.
+ self.ignore_elements = {'xl/charts/chart1.xml':
+ ['<c:ptCount', '<c:pt', '<c:v', '</c:pt>']}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with error bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [69198976, 69200896]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'y_error_bars': {
+ 'type': 'custom',
+ 'plus_values': '=Sheet1!$A$1',
+ 'minus_values': '=Sheet1!$B$1:$B$3',
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_font01.py b/xlsxwriter/test/comparison/test_chart_font01.py
new file mode 100644
index 0000000..5521f9f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_font01.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_font01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [43945344, 45705856]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_title({'name': 'Title'})
+
+ chart.set_x_axis({
+ 'name': 'XXX',
+ 'num_font': {'size': 11, 'bold': 1, 'italic': 1}
+ })
+
+ chart.set_y_axis({
+ 'name': 'YYY',
+ 'num_font': {'size': 9, 'bold': 0, 'italic': True}
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_font02.py b/xlsxwriter/test/comparison/test_chart_font02.py
new file mode 100644
index 0000000..d661258
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_font02.py
@@ -0,0 +1,73 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_font02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [43945344, 45705856]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_title({'name': 'Title'})
+
+ chart.set_x_axis({
+ 'name': 'XXX',
+ 'name_font': {'bold': 0, 'italic': 1},
+ 'num_font': {'size': 11, 'bold': 1, 'italic': 1},
+ })
+
+ chart.set_y_axis({
+ 'name': 'YYY',
+ 'name_font': {'bold': 1, 'italic': 1},
+ 'num_font': {'size': 9, 'bold': 0, 'italic': 1},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_font03.py b/xlsxwriter/test/comparison/test_chart_font03.py
new file mode 100644
index 0000000..fdeac12
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_font03.py
@@ -0,0 +1,76 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_font03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [45704704, 45716224]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_title({
+ 'name': 'Title',
+ 'name_font': {'bold': 0, 'italic': 1},
+ })
+
+ chart.set_x_axis({
+ 'name': 'XXX',
+ 'name_font': {'bold': 0, 'italic': 1},
+ 'num_font': {'size': 11, 'bold': 1, 'italic': 1},
+ })
+
+ chart.set_y_axis({
+ 'name': 'YYY',
+ 'name_font': {'bold': 1, 'italic': 1},
+ 'num_font': {'size': 9, 'bold': 0, 'italic': 1},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_font04.py b/xlsxwriter/test/comparison/test_chart_font04.py
new file mode 100644
index 0000000..4683f2e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_font04.py
@@ -0,0 +1,74 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_font04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [43944960, 45705472]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_title({
+ 'name': '=Sheet1!$A$1',
+ 'name_font': {'bold': 0, 'italic': 1},
+ })
+
+ chart.set_x_axis({
+ 'name': 'Sheet1!$A$2',
+ 'name_font': {'bold': 0, 'italic': 1},
+ })
+
+ chart.set_y_axis({
+ 'name': '=Sheet1!$A$3',
+ 'name_font': {'bold': 1, 'italic': 1},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_font05.py b/xlsxwriter/test/comparison/test_chart_font05.py
new file mode 100644
index 0000000..64418e2
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_font05.py
@@ -0,0 +1,71 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_font05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [49407488, 53740288]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_title({'name': 'Title'})
+
+ chart.set_x_axis({
+ 'name': 'XXX',
+ 'num_font': {'name': 'Arial', 'pitch_family': 34, 'charset': 0}
+ })
+
+ chart.set_y_axis({
+ 'name': 'YYY',
+ 'num_font': {'bold': 1, 'italic': 1, 'underline': 1}
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_font06.py b/xlsxwriter/test/comparison/test_chart_font06.py
new file mode 100644
index 0000000..b864649
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_font06.py
@@ -0,0 +1,101 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_font06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [49407488, 53740288]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_title({
+ 'name': 'Title',
+ 'name_font': {
+ 'name': 'Calibri',
+ 'pitch_family': 34,
+ 'charset': 0,
+ 'color': 'yellow',
+ },
+ })
+
+ chart.set_x_axis({
+ 'name': 'XXX',
+ 'name_font': {
+ 'name': 'Courier New',
+ 'pitch_family': 49,
+ 'charset': 0,
+ 'color': '#92D050'
+ },
+ 'num_font': {
+ 'name': 'Arial',
+ 'pitch_family': 34,
+ 'charset': 0,
+ 'color': '#00B0F0',
+ },
+ })
+
+ chart.set_y_axis({
+ 'name': 'YYY',
+ 'name_font': {
+ 'name': 'Century',
+ 'pitch_family': 18,
+ 'charset': 0,
+ 'color': 'red'
+ },
+ 'num_font': {
+ 'bold': 1,
+ 'italic': 1,
+ 'underline': 1,
+ 'color': '#7030A0',
+ },
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_font07.py b/xlsxwriter/test/comparison/test_chart_font07.py
new file mode 100644
index 0000000..1d44db7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_font07.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_font07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [69215744, 69217280]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_legend({'font': {'size': 9, 'baseline': -1}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_font08.py b/xlsxwriter/test/comparison/test_chart_font08.py
new file mode 100644
index 0000000..941e0ad
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_font08.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_font08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [69199744, 69214976]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_legend({'font': {'bold': 1, 'italic': 1, 'baseline': -1}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_font09.py b/xlsxwriter/test/comparison/test_chart_font09.py
new file mode 100644
index 0000000..6cdceaa
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_font09.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_font09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [68825472, 68827392]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_title({'name': 'Title',
+ 'name_font': {'rotation': -45, 'baseline': -1}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format01.py b/xlsxwriter/test/comparison/test_chart_format01.py
new file mode 100644
index 0000000..98d6af6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format01.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [46335872, 46365696]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format02.py b/xlsxwriter/test/comparison/test_chart_format02.py
new file mode 100644
index 0000000..c5fa3ad
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format02.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [46335872, 46365696]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'line': {'color': 'red'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format03.py b/xlsxwriter/test/comparison/test_chart_format03.py
new file mode 100644
index 0000000..e0a5997
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format03.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [46175744, 46319488]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'border': {'color': 'yellow'},
+ 'fill': {'color': 'red'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format04.py b/xlsxwriter/test/comparison/test_chart_format04.py
new file mode 100644
index 0000000..9ec9a67
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format04.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [46175744, 46319488]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'fill': {'color': '#FF0000'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format05.py b/xlsxwriter/test/comparison/test_chart_format05.py
new file mode 100644
index 0000000..c749a4a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format05.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [46319488, 46335872]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'marker': {'type': 'automatic'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format06.py b/xlsxwriter/test/comparison/test_chart_format06.py
new file mode 100644
index 0000000..69e48e8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format06.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [46163840, 46175360]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'marker': {'type': 'diamond', 'size': 7},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format07.py b/xlsxwriter/test/comparison/test_chart_format07.py
new file mode 100644
index 0000000..e1e673c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format07.py
@@ -0,0 +1,71 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [46163840, 46175360]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'marker': {
+ 'type': 'square',
+ 'size': 5,
+ 'line': {'color': 'yellow'},
+ 'fill': {'color': 'red'},
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format08.py b/xlsxwriter/test/comparison/test_chart_format08.py
new file mode 100644
index 0000000..87f4476
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format08.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [46164608, 46176128]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {'type': 'linear'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format09.py b/xlsxwriter/test/comparison/test_chart_format09.py
new file mode 100644
index 0000000..5013e12
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format09.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [46115072, 46157184]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'line': {
+ 'color': 'red',
+ 'width': 1.25,
+ 'dash_type': 'square_dot',
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format10.py b/xlsxwriter/test/comparison/test_chart_format10.py
new file mode 100644
index 0000000..cc56c68
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format10.py
@@ -0,0 +1,73 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [54795648, 56296960]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {
+ 'type': 'linear',
+ 'line': {
+ 'color': 'red',
+ 'width': 1,
+ 'dash_type': 'long_dash',
+ },
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format11.py b/xlsxwriter/test/comparison/test_chart_format11.py
new file mode 100644
index 0000000..df9720b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format11.py
@@ -0,0 +1,76 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [50664576, 50666496]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {
+ 'type': 'polynomial',
+ 'name': 'My trend name',
+ 'order': 2,
+ 'forward': 0.5,
+ 'backward': 0.5,
+ 'line': {
+ 'color': 'red',
+ 'width': 1,
+ 'dash_type': 'long_dash',
+ },
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format12.py b/xlsxwriter/test/comparison/test_chart_format12.py
new file mode 100644
index 0000000..0b8a3c0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format12.py
@@ -0,0 +1,74 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [54794880, 56296576]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {
+ 'type': 'moving_average',
+ 'period': 2,
+ 'line': {
+ 'color': 'red',
+ 'width': 1,
+ 'dash_type': 'long_dash',
+ },
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format13.py b/xlsxwriter/test/comparison/test_chart_format13.py
new file mode 100644
index 0000000..384587e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format13.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format13.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [51785088, 51804032]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format14.py b/xlsxwriter/test/comparison/test_chart_format14.py
new file mode 100644
index 0000000..2073a09
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format14.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format14.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [51819264, 52499584]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'category': 1, 'series_name': 1},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format15.py b/xlsxwriter/test/comparison/test_chart_format15.py
new file mode 100644
index 0000000..8c34624
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format15.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format15.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [42401792, 42403712]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {'type': 'linear'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_legend({'delete_series': [2, 0]})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format16.py b/xlsxwriter/test/comparison/test_chart_format16.py
new file mode 100644
index 0000000..38cb013
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format16.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format16.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [43943040, 44287488]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'data_labels': {'value': 1, 'category': 1, 'series_name': 1, 'position': 'center'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format17.py b/xlsxwriter/test/comparison/test_chart_format17.py
new file mode 100644
index 0000000..4ca7dd5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format17.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format17.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [42379520, 47284608]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5', 'fill': {'none': 1}})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format18.py b/xlsxwriter/test/comparison/test_chart_format18.py
new file mode 100644
index 0000000..9b2cd1d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format18.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format18.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [42379520, 47284608]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'border': {'none': 1},
+ 'fill': {'none': 1}
+ })
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format19.py b/xlsxwriter/test/comparison/test_chart_format19.py
new file mode 100644
index 0000000..2866007
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format19.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format19.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({
+ 'type': 'column',
+ 'subtype': 'stacked',
+ })
+
+ chart.axis_ids = [56127488, 57455360]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5',
+ 'data_labels': {'value': 1,
+ 'position': 'inside_base'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format20.py b/xlsxwriter/test/comparison/test_chart_format20.py
new file mode 100644
index 0000000..77e199f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format20.py
@@ -0,0 +1,81 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format20.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart1 = workbook.add_chart({'type': 'line'})
+ chart2 = workbook.add_chart({'type': 'line'})
+
+ chart1.axis_ids = [80553856, 80555392]
+ chart2.axis_ids = [84583936, 84585856]
+
+ trend = {
+ 'type': 'linear',
+ 'line': {'color': 'red', 'dash_type': 'dash'}
+ }
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart1.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': trend,
+ })
+
+ chart1.add_series({
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart2.add_series({
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': trend,
+ })
+
+ chart2.add_series({
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart1)
+ worksheet.insert_chart('E25', chart2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format21.py b/xlsxwriter/test/comparison/test_chart_format21.py
new file mode 100644
index 0000000..458e17d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format21.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format21.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [115390336, 115417856]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'border': {'color': 'yellow'},
+ 'fill': {'color': 'red', 'transparency': 24},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format22.py b/xlsxwriter/test/comparison/test_chart_format22.py
new file mode 100644
index 0000000..35f6a61
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format22.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format22.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [108321024, 108328448]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'border': {'color': 'yellow'},
+ 'fill': {'color': 'red', 'transparency': 1},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format23.py b/xlsxwriter/test/comparison/test_chart_format23.py
new file mode 100644
index 0000000..5247507
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format23.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format23.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [108321024, 108328448]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'border': {'color': 'yellow'},
+ 'fill': {'color': 'red', 'transparency': 100},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format24.py b/xlsxwriter/test/comparison/test_chart_format24.py
new file mode 100644
index 0000000..4dca2b9
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format24.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format24.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [115374720, 115389568]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_chartarea({'fill': {'color': 'yellow', 'transparency': 75}})
+
+ chart.set_plotarea({'fill': {'color': 'red', 'transparency': 25}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format25.py b/xlsxwriter/test/comparison/test_chart_format25.py
new file mode 100644
index 0000000..ab5225e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format25.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format25.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [108178048, 108319488]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'border': {'color': 'red', 'transparency': 50}
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format26.py b/xlsxwriter/test/comparison/test_chart_format26.py
new file mode 100644
index 0000000..77063f6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format26.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format26.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [108652416, 108655744]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {'type': 'linear', 'display_equation': True},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_legend({'delete_series': [2, 0]})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format27.py b/xlsxwriter/test/comparison/test_chart_format27.py
new file mode 100644
index 0000000..3d32c43
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format27.py
@@ -0,0 +1,77 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format27.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [108645376, 108655360]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {
+ 'type': 'polynomial',
+ 'name': 'My trend name',
+ 'order': 2,
+ 'forward': 0.5,
+ 'backward': 0.5,
+ 'display_equation': True,
+ 'line': {
+ 'color': 'red',
+ 'width': 1,
+ 'dash_type': 'long_dash',
+ },
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format28.py b/xlsxwriter/test/comparison/test_chart_format28.py
new file mode 100644
index 0000000..5a52d9c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format28.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format28.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [108645376, 108655360]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {'type': 'linear',
+ 'display_equation': True,
+ 'display_r_squared': True},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_legend({'delete_series': [0, 2]})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format29.py b/xlsxwriter/test/comparison/test_chart_format29.py
new file mode 100644
index 0000000..d3ace87
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format29.py
@@ -0,0 +1,78 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format29.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [108652416, 108655744]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {
+ 'type': 'polynomial',
+ 'name': 'My trend name',
+ 'order': 2,
+ 'forward': 0.5,
+ 'backward': 0.5,
+ 'display_equation': True,
+ 'display_r_squared': True,
+ 'line': {
+ 'color': 'red',
+ 'width': 1,
+ 'dash_type': 'long_dash',
+ },
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format30.py b/xlsxwriter/test/comparison/test_chart_format30.py
new file mode 100644
index 0000000..df40a98
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format30.py
@@ -0,0 +1,71 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format30.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [108652416, 108655744]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {'type': 'linear',
+ 'intercept': 0.8,
+ 'display_equation': True,
+ 'display_r_squared': True},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_legend({'delete_series': [0, 2]})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_format31.py b/xlsxwriter/test/comparison/test_chart_format31.py
new file mode 100644
index 0000000..259c292
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_format31.py
@@ -0,0 +1,79 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_format31.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with chart formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [115443200, 115459200]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'trendline': {
+ 'type': 'polynomial',
+ 'name': 'My trend name',
+ 'order': 2,
+ 'forward': 0.5,
+ 'backward': 0.5,
+ 'intercept': 1.5,
+ 'display_equation': True,
+ 'display_r_squared': True,
+ 'line': {
+ 'color': 'red',
+ 'width': 1,
+ 'dash_type': 'long_dash',
+ },
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gap01.py b/xlsxwriter/test/comparison/test_chart_gap01.py
new file mode 100644
index 0000000..b7ba6aa
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gap01.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gap01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with bar gap/overlap."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45438848, 45470464]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'overlap': 5,
+ 'gap': 157,
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gap02.py b/xlsxwriter/test/comparison/test_chart_gap02.py
new file mode 100644
index 0000000..be1ba71
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gap02.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gap02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with bar gap/overlap."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45470464, 45472000]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'overlap': -100,
+ 'gap': 0,
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gap03.py b/xlsxwriter/test/comparison/test_chart_gap03.py
new file mode 100644
index 0000000..d9ccb2a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gap03.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gap03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with bar gap/overlap."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45470464, 45472000]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'overlap': 100,
+ 'gap': 500,
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gap04.py b/xlsxwriter/test/comparison/test_chart_gap04.py
new file mode 100644
index 0000000..e233fe5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gap04.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gap04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45938176, 59715584]
+ chart.axis2_ids = [62526208, 59718272]
+
+ data = [[1, 2, 3, 4, 5],
+ [6, 8, 6, 4, 2]]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5',
+ 'gap': 51,
+ 'overlap': 12})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5',
+ 'y2_axis': 1,
+ 'gap': 251,
+ 'overlap': -27})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gap05.py b/xlsxwriter/test/comparison/test_chart_gap05.py
new file mode 100644
index 0000000..9b85e65
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gap05.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gap05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [45938176, 59715584]
+ chart.axis2_ids = [70848512, 54519680]
+
+ data = [[1, 2, 3, 4, 5],
+ [6, 8, 6, 4, 2]]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5',
+ 'gap': 51,
+ 'overlap': 12})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5',
+ 'y2_axis': 1,
+ 'gap': 251,
+ 'overlap': -27})
+
+ chart.set_x2_axis({'label_position': 'next_to'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gradient01.py b/xlsxwriter/test/comparison/test_chart_gradient01.py
new file mode 100644
index 0000000..51eaf8f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gradient01.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gradient01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [61365248, 64275200]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {'colors': ['#DDEBCF', '#9CB86E', '#156B13']}
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gradient02.py b/xlsxwriter/test/comparison/test_chart_gradient02.py
new file mode 100644
index 0000000..700f679
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gradient02.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gradient02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [61363328, 61364864]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {'colors': ['#DDEBCF', '#9CB86E', '#156B13'],
+ 'angle': 30}
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gradient03.py b/xlsxwriter/test/comparison/test_chart_gradient03.py
new file mode 100644
index 0000000..8b0e50a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gradient03.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gradient03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [61363328, 61364864]
+ chart.axis_ids = [61363712, 61365248]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {
+ 'colors': ['#DDEBCF', '#9CB86E', '#156B13'],
+ 'positions': [0, 40, 100],
+ }
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gradient04.py b/xlsxwriter/test/comparison/test_chart_gradient04.py
new file mode 100644
index 0000000..897adcd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gradient04.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gradient04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [61363712, 61365248]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {
+ 'colors': ['#DDEBCF', '#9CB86E', '#156B13'],
+ 'type': 'radial'
+ }
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gradient05.py b/xlsxwriter/test/comparison/test_chart_gradient05.py
new file mode 100644
index 0000000..2e67c6a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gradient05.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gradient05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [61363712, 61365248]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {
+ 'colors': ['#DDEBCF', '#9CB86E', '#156B13'],
+ 'type': 'rectangular'
+ }
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gradient06.py b/xlsxwriter/test/comparison/test_chart_gradient06.py
new file mode 100644
index 0000000..dc8b091
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gradient06.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gradient06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [61363328, 61364864]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {
+ 'colors': ['#DDEBCF', '#9CB86E', '#156B13'],
+ 'type': 'path'
+ }
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gradient07.py b/xlsxwriter/test/comparison/test_chart_gradient07.py
new file mode 100644
index 0000000..6bd6d50
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gradient07.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gradient07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [68936064, 68937600]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {'colors': ['#DDEBCF', '#9CB86E', '#156B13']},
+ 'invert_if_negative': 1
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gradient08.py b/xlsxwriter/test/comparison/test_chart_gradient08.py
new file mode 100644
index 0000000..f873354
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gradient08.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gradient08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [69014272, 69016192]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart.set_chartarea({
+ 'gradient': {'colors': ['#DDEBCF', '#9CB86E', '#156B13']}
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gradient09.py b/xlsxwriter/test/comparison/test_chart_gradient09.py
new file mode 100644
index 0000000..426037f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gradient09.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gradient09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [56159616, 61364480]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart.set_plotarea({
+ 'gradient': {'colors': ['#DDEBCF', '#9CB86E', '#156B13']}
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gradient10.py b/xlsxwriter/test/comparison/test_chart_gradient10.py
new file mode 100644
index 0000000..e8ec9d9
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gradient10.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gradient10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [56159232, 61364096]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {'colors': ['#DDEBCF', '#156B13']}
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gradient11.py b/xlsxwriter/test/comparison/test_chart_gradient11.py
new file mode 100644
index 0000000..d44db54
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gradient11.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gradient11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [66738048, 66748416]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {'colors': ['#DDEBCF', '#DDEBCF',
+ '#9CB86E', '#156B13']}
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gradient12.py b/xlsxwriter/test/comparison/test_chart_gradient12.py
new file mode 100644
index 0000000..6d6b400
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gradient12.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gradient12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [56159232, 61364096]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {
+ 'colors': ['#DDEBCF', '#156B13'],
+ 'positions': [10, 90],
+ }
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gradient13.py b/xlsxwriter/test/comparison/test_chart_gradient13.py
new file mode 100644
index 0000000..a2e171d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gradient13.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gradient13.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [56159232, 61364096]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'gradient': {'colors': ['#DDEBCF', '#9CB86E', '#156B13'],
+ 'angle': 355}
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gridlines01.py b/xlsxwriter/test/comparison/test_chart_gridlines01.py
new file mode 100644
index 0000000..ad0eee5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gridlines01.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gridlines01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter gridlines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [67390848, 68472192]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'major_gridlines': {'visible': 1}})
+ chart.set_y_axis({'major_gridlines': {'visible': 0}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gridlines02.py b/xlsxwriter/test/comparison/test_chart_gridlines02.py
new file mode 100644
index 0000000..d3e20fd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gridlines02.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gridlines02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter gridlines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [57940608, 57938688]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_x_axis({'major_gridlines': {'visible': 1}})
+ chart.set_y_axis({'major_gridlines': {'visible': 0}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gridlines03.py b/xlsxwriter/test/comparison/test_chart_gridlines03.py
new file mode 100644
index 0000000..e1d864a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gridlines03.py
@@ -0,0 +1,79 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gridlines03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test XlsxWriter gridlines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'stock'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [54553600, 54977280]
+
+ data = [
+ [39083, 39084, 39085, 39086, 39087],
+ [27.2, 25.03, 19.05, 20.34, 18.5],
+ [23.49, 19.55, 15.12, 17.84, 16.34],
+ [25.45, 23.05, 17.32, 20.45, 17.34],
+ ]
+
+ for row in range(5):
+ worksheet.write(row, 0, data[0][row], date_format)
+
+ worksheet.write(row, 1, data[1][row])
+ worksheet.write(row, 2, data[2][row])
+ worksheet.write(row, 3, data[3][row])
+
+ worksheet.set_column('A:D', 11)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$D$1:$D$5',
+ })
+
+ chart.set_x_axis({'major_gridlines': {'visible': 1}})
+ chart.set_y_axis({'major_gridlines': {'visible': 0}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gridlines04.py b/xlsxwriter/test/comparison/test_chart_gridlines04.py
new file mode 100644
index 0000000..1e202e9
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gridlines04.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gridlines04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter gridlines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'radar'})
+
+ chart.axis_ids = [54977280, 54978816]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_y_axis({'major_gridlines': {'visible': 0},
+ 'major_tick_mark': 'cross'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gridlines05.py b/xlsxwriter/test/comparison/test_chart_gridlines05.py
new file mode 100644
index 0000000..0a98be9
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gridlines05.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gridlines05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter gridlines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [80072064, 79959168]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({
+ 'major_gridlines': {'visible': 1},
+ 'minor_gridlines': {'visible': 1},
+ })
+
+ chart.set_y_axis({
+ 'major_gridlines': {'visible': 1},
+ 'minor_gridlines': {'visible': 1},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gridlines06.py b/xlsxwriter/test/comparison/test_chart_gridlines06.py
new file mode 100644
index 0000000..c1a1a38
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gridlines06.py
@@ -0,0 +1,75 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gridlines06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter gridlines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [82812288, 46261376]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_x_axis({
+ 'major_gridlines': {'visible': 1},
+ 'minor_gridlines': {'visible': 1},
+ })
+
+ chart.set_y_axis({
+ 'major_gridlines': {'visible': 1},
+ 'minor_gridlines': {'visible': 1},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gridlines07.py b/xlsxwriter/test/comparison/test_chart_gridlines07.py
new file mode 100644
index 0000000..3108921
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gridlines07.py
@@ -0,0 +1,86 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gridlines07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test XlsxWriter gridlines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'stock'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [59313152, 59364096]
+
+ data = [
+ [39083, 39084, 39085, 39086, 39087],
+ [27.2, 25.03, 19.05, 20.34, 18.5],
+ [23.49, 19.55, 15.12, 17.84, 16.34],
+ [25.45, 23.05, 17.32, 20.45, 17.34],
+ ]
+
+ for row in range(5):
+ worksheet.write(row, 0, data[0][row], date_format)
+
+ worksheet.write(row, 1, data[1][row])
+ worksheet.write(row, 2, data[2][row])
+ worksheet.write(row, 3, data[3][row])
+
+ worksheet.set_column('A:D', 11)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$D$1:$D$5',
+ })
+
+ chart.set_x_axis({
+ 'major_gridlines': {'visible': 1},
+ 'minor_gridlines': {'visible': 1},
+ })
+
+ chart.set_y_axis({
+ 'major_gridlines': {'visible': 1},
+ 'minor_gridlines': {'visible': 1},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gridlines08.py b/xlsxwriter/test/comparison/test_chart_gridlines08.py
new file mode 100644
index 0000000..aeecac6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gridlines08.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gridlines08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter gridlines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'radar'})
+
+ chart.axis_ids = [60019072, 60020608]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_y_axis({
+ 'major_gridlines': {'visible': 1},
+ 'minor_gridlines': {'visible': 1},
+ 'major_tick_mark': 'cross'
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_gridlines09.py b/xlsxwriter/test/comparison/test_chart_gridlines09.py
new file mode 100644
index 0000000..5d64fd7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_gridlines09.py
@@ -0,0 +1,83 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_gridlines09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter gridlines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [48744320, 49566848]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({
+ 'major_gridlines': {
+ 'visible': 1,
+ 'line': {'color': 'red', 'width': 0.5, 'dash_type': 'square_dot'}
+ },
+
+ 'minor_gridlines': {
+ 'visible': 1,
+ 'line': {'color': 'yellow'}
+ },
+ })
+
+ chart.set_y_axis({
+ 'major_gridlines': {
+ 'visible': 1,
+ 'line': {'width': 1.25, 'dash_type': 'dash'}
+ },
+
+ 'minor_gridlines': {
+ 'visible': 1,
+ 'line': {'color': '#00B050'}
+ },
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_high_low_lines01.py b/xlsxwriter/test/comparison/test_chart_high_low_lines01.py
new file mode 100644
index 0000000..56ebc0d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_high_low_lines01.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_high_low_lines01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with high-low lines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [49018368, 49019904]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.set_high_low_lines()
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_high_low_lines02.py b/xlsxwriter/test/comparison/test_chart_high_low_lines02.py
new file mode 100644
index 0000000..4ff17e3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_high_low_lines02.py
@@ -0,0 +1,72 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_high_low_lines02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with high-low lines."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [61180928, 63898368]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.set_high_low_lines({
+ 'line': {
+ 'color': 'red',
+ 'dash_type': 'square_dot'
+ }
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_layout01.py b/xlsxwriter/test/comparison/test_chart_layout01.py
new file mode 100644
index 0000000..73a64a3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_layout01.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_layout01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with user defined layout."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [69198592, 69200128]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_plotarea({
+ 'layout': {
+ 'x': 0.13171062992125984,
+ 'y': 0.26436351706036748,
+ 'width': 0.73970734908136482,
+ 'height': 0.5713732137649461,
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_layout02.py b/xlsxwriter/test/comparison/test_chart_layout02.py
new file mode 100644
index 0000000..cd62187
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_layout02.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_layout02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with user defined layout."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [68311296, 69198208]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_legend({
+ 'layout': {
+ 'x': 0.80197353455818021,
+ 'y': 0.37442403032954213,
+ 'width': 0.12858202099737534,
+ 'height': 0.25115157480314959,
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_layout03.py b/xlsxwriter/test/comparison/test_chart_layout03.py
new file mode 100644
index 0000000..b3f180a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_layout03.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_layout03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with user defined layout."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [68312064, 69198592]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_legend({
+ 'position': 'overlay_right',
+ 'layout': {
+ 'x': 0.80197353455818043,
+ 'y': 0.3744240303295423,
+ 'width': 0.12858202099737534,
+ 'height': 0.25115157480314959,
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_layout04.py b/xlsxwriter/test/comparison/test_chart_layout04.py
new file mode 100644
index 0000000..c7629b6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_layout04.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_layout04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with user defined layout."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [68311296, 69198208]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_title({
+ 'name': 'Title',
+ 'layout': {
+ 'x': 0.42631933508311465,
+ 'y': 0.14351851851851852,
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_layout05.py b/xlsxwriter/test/comparison/test_chart_layout05.py
new file mode 100644
index 0000000..84f3e13
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_layout05.py
@@ -0,0 +1,80 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_layout05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with user defined layout."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'area'})
+
+ chart.axis_ids = [43495808, 43497728]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [8, 7, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_x_axis({
+ 'name': 'XXX',
+ 'name_layout': {
+ 'x': 0.34620319335083105,
+ 'y': 0.85090259550889469,
+ }
+ })
+
+ chart.set_y_axis({
+ 'name': 'YYY',
+ 'name_layout': {
+ 'x': 0.21388888888888888,
+ 'y': 0.26349919801691457,
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_layout06.py b/xlsxwriter/test/comparison/test_chart_layout06.py
new file mode 100644
index 0000000..4675db4
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_layout06.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_layout06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with user defined layout."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [43496576, 45486080]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_title({
+ 'name': 'Title',
+ 'overlay': 1,
+ 'layout': {
+ 'x': 0.42354155730533688,
+ 'y': 0.16203703703703703,
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_layout07.py b/xlsxwriter/test/comparison/test_chart_layout07.py
new file mode 100644
index 0000000..90175c6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_layout07.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_layout07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with user defined layout."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [45861120, 45867008]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_title({
+ 'name_formula': '=Sheet1!$A$1', 'data': [1],
+ 'layout': {
+ 'x': 0.359652668416448,
+ 'y': 0.1388888888888889,
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_layout08.py b/xlsxwriter/test/comparison/test_chart_layout08.py
new file mode 100644
index 0000000..478bcbd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_layout08.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_layout08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with user defined layout."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [46317568, 46319488]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_title({
+ 'name_formula': '=Sheet1!$A$1',
+ 'data': [1],
+ 'overlay': 1,
+ 'layout': {
+ 'x': 0.359652668416448,
+ 'y': 0.16203703703703703,
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_legend01.py b/xlsxwriter/test/comparison/test_chart_legend01.py
new file mode 100644
index 0000000..2f43d5d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_legend01.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_legend01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with legend options."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [54461952, 54463872]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_legend({'position': 'none'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_legend02.py b/xlsxwriter/test/comparison/test_chart_legend02.py
new file mode 100644
index 0000000..6a079ac
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_legend02.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_legend01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_2' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with legend options."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [54461952, 54463872]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_legend({'none': True})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_line01.py b/xlsxwriter/test/comparison/test_chart_line01.py
new file mode 100644
index 0000000..899edef
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_line01.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_line01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [43408000, 43434368]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_line02.py b/xlsxwriter/test/comparison/test_chart_line02.py
new file mode 100644
index 0000000..08447c9
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_line02.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_line02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/workbook.xml': ['<fileVersion', '<calcPr']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [63593856, 63612032]
+ chart.axis2_ids = [63615360, 63613568]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [6, 8, 6, 4, 2]
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5',
+ 'y2_axis': 1})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_line03.py b/xlsxwriter/test/comparison/test_chart_line03.py
new file mode 100644
index 0000000..486a531
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_line03.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_line03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [47673728, 47675264]
+
+ data = [
+ [5, 2, 3, 4, 3],
+ [10, 4, 6, 8, 6],
+ [15, 6, 9, 12, 9],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5', 'smooth': True})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_line04.py b/xlsxwriter/test/comparison/test_chart_line04.py
new file mode 100644
index 0000000..95589ec
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_line04.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_line04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [47670016, 47671552]
+
+ data = [
+ [5, 2, 3, 4, 3],
+ [10, 4, 6, 8, 6],
+ [15, 6, 9, 12, 9],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5', 'smooth': True})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5', 'smooth': True})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_name01.py b/xlsxwriter/test/comparison/test_chart_name01.py
new file mode 100644
index 0000000..b1c7e61
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_name01.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_name01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line',
+ 'name': 'New Chart Name'
+ })
+
+ chart.axis_ids = [47335296, 56029952]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_name02.py b/xlsxwriter/test/comparison/test_chart_name02.py
new file mode 100644
index 0000000..b690dba
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_name02.py
@@ -0,0 +1,74 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_name02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ chart1 = workbook.add_chart({
+ 'type': 'line',
+ 'name': 'New 1'
+ })
+
+ chart2 = workbook.add_chart({
+ 'type': 'line',
+ 'name': 'New 2'
+ })
+
+ chart1.axis_ids = [44271104, 45703168]
+ chart2.axis_ids = [80928128, 80934400]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart1.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart1.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart1.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart2.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart2.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart2.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart1)
+ worksheet.insert_chart('E24', chart2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_name03.py b/xlsxwriter/test/comparison/test_chart_name03.py
new file mode 100644
index 0000000..92f920e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_name03.py
@@ -0,0 +1,74 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_name03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ chart1 = workbook.add_chart({
+ 'type': 'line',
+ 'name': 'New 1'
+ })
+
+ chart2 = workbook.add_chart({
+ 'type': 'line',
+ 'name': 'New 2'
+ })
+
+ chart1.axis_ids = [44271104, 45703168]
+ chart2.axis_ids = [80928128, 80934400]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart1.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart1.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart1.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart2.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart2.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart2.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart1)
+ worksheet.insert_chart('E24', chart2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_name04.py b/xlsxwriter/test/comparison/test_chart_name04.py
new file mode 100644
index 0000000..32979d4
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_name04.py
@@ -0,0 +1,74 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_font04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [43944960, 45705472]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_title({
+ 'name': ['Sheet1', 0, 0],
+ 'name_font': {'bold': 0, 'italic': 1},
+ })
+
+ chart.set_x_axis({
+ 'name': ['Sheet1', 1, 0],
+ 'name_font': {'bold': 0, 'italic': 1},
+ })
+
+ chart.set_y_axis({
+ 'name': ['Sheet1', 2, 0],
+ 'name_font': {'bold': 1, 'italic': 1},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_order01.py b/xlsxwriter/test/comparison/test_chart_order01.py
new file mode 100644
index 0000000..b1808ce
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_order01.py
@@ -0,0 +1,79 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_order01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet()
+
+ chart1 = workbook.add_chart({'type': 'column'})
+ chart2 = workbook.add_chart({'type': 'bar'})
+ chart3 = workbook.add_chart({'type': 'line'})
+ chart4 = workbook.add_chart({'type': 'pie'})
+
+ chart1.axis_ids = [54976896, 54978432]
+ chart2.axis_ids = [54310784, 54312320]
+ chart3.axis_ids = [69816704, 69818240]
+ chart4.axis_ids = [69816704, 69818240]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet1.write_column('A1', data[0])
+ worksheet1.write_column('B1', data[1])
+ worksheet1.write_column('C1', data[2])
+
+ worksheet2.write_column('A1', data[0])
+ worksheet2.write_column('B1', data[1])
+ worksheet2.write_column('C1', data[2])
+
+ worksheet3.write_column('A1', data[0])
+ worksheet3.write_column('B1', data[1])
+ worksheet3.write_column('C1', data[2])
+
+ chart1.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart2.add_series({'values': '=Sheet2!$A$1:$A$5'})
+ chart3.add_series({'values': '=Sheet3!$A$1:$A$5'})
+ chart4.add_series({'values': '=Sheet1!$B$1:$B$5'})
+
+ worksheet1.insert_chart('E9', chart1)
+ worksheet2.insert_chart('E9', chart2)
+ worksheet3.insert_chart('E9', chart3)
+ worksheet1.insert_chart('E24', chart4)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_order02.py b/xlsxwriter/test/comparison/test_chart_order02.py
new file mode 100644
index 0000000..4fd87a4
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_order02.py
@@ -0,0 +1,80 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_order01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_2_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet()
+
+ chart4 = workbook.add_chart({'type': 'pie'})
+ chart3 = workbook.add_chart({'type': 'line'})
+ chart2 = workbook.add_chart({'type': 'bar'})
+ chart1 = workbook.add_chart({'type': 'column'})
+
+ chart1.axis_ids = [54976896, 54978432]
+ chart2.axis_ids = [54310784, 54312320]
+ chart3.axis_ids = [69816704, 69818240]
+ chart4.axis_ids = [69816704, 69818240]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet1.write_column('A1', data[0])
+ worksheet1.write_column('B1', data[1])
+ worksheet1.write_column('C1', data[2])
+
+ worksheet2.write_column('A1', data[0])
+ worksheet2.write_column('B1', data[1])
+ worksheet2.write_column('C1', data[2])
+
+ worksheet3.write_column('A1', data[0])
+ worksheet3.write_column('B1', data[1])
+ worksheet3.write_column('C1', data[2])
+
+ chart1.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart2.add_series({'values': '=Sheet2!$A$1:$A$5'})
+ chart3.add_series({'values': '=Sheet3!$A$1:$A$5'})
+ chart4.add_series({'values': '=Sheet1!$B$1:$B$5'})
+
+ worksheet1.insert_chart('E9', chart1)
+ worksheet2.insert_chart('E9', chart2)
+ worksheet3.insert_chart('E9', chart3)
+ worksheet1.insert_chart('E24', chart4)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pattern01.py b/xlsxwriter/test/comparison/test_chart_pattern01.py
new file mode 100644
index 0000000..dd15aa0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pattern01.py
@@ -0,0 +1,73 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pattern01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [86421504, 86423040]
+
+ data = [
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+ worksheet.write_column('D1', data[3])
+ worksheet.write_column('E1', data[4])
+ worksheet.write_column('F1', data[5])
+ worksheet.write_column('G1', data[6])
+ worksheet.write_column('H1', data[7])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$3'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$3'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$3'})
+ chart.add_series({'values': '=Sheet1!$D$1:$D$3'})
+ chart.add_series({'values': '=Sheet1!$E$1:$E$3'})
+ chart.add_series({'values': '=Sheet1!$F$1:$F$3'})
+ chart.add_series({'values': '=Sheet1!$G$1:$G$3'})
+ chart.add_series({'values': '=Sheet1!$H$1:$H$3'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pattern02.py b/xlsxwriter/test/comparison/test_chart_pattern02.py
new file mode 100644
index 0000000..9dfbd25
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pattern02.py
@@ -0,0 +1,129 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pattern02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [86421504, 86423040]
+
+ data = [
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+ worksheet.write_column('D1', data[3])
+ worksheet.write_column('E1', data[4])
+ worksheet.write_column('F1', data[5])
+ worksheet.write_column('G1', data[6])
+ worksheet.write_column('H1', data[7])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$3',
+ 'pattern': {
+ 'pattern': 'percent_5',
+ 'fg_color': '#C00000',
+ 'bg_color': '#FFFFFF'
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$3',
+ 'pattern': {
+ 'pattern': 'percent_50',
+ 'fg_color': '#FF0000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$C$1:$C$3',
+ 'pattern': {
+ 'pattern': 'light_downward_diagonal',
+ 'fg_color': '#FFC000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$D$1:$D$3',
+ 'pattern': {
+ 'pattern': 'light_vertical',
+ 'fg_color': '#FFFF00',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$E$1:$E$3',
+ 'pattern': {
+ 'pattern': 'dashed_downward_diagonal',
+ 'fg_color': '#92D050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$F$1:$F$3',
+ 'pattern': {
+ 'pattern': 'zigzag',
+ 'fg_color': '#00B050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$G$1:$G$3',
+ 'pattern': {
+ 'pattern': 'divot',
+ 'fg_color': '#00B0F0',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$H$1:$H$3',
+ 'pattern': {
+ 'pattern': 'small_grid',
+ 'fg_color': '#0070C0',
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pattern03.py b/xlsxwriter/test/comparison/test_chart_pattern03.py
new file mode 100644
index 0000000..49ec97a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pattern03.py
@@ -0,0 +1,129 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pattern03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [110902272, 110756608]
+
+ data = [
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+ worksheet.write_column('D1', data[3])
+ worksheet.write_column('E1', data[4])
+ worksheet.write_column('F1', data[5])
+ worksheet.write_column('G1', data[6])
+ worksheet.write_column('H1', data[7])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$3',
+ 'pattern': {
+ 'pattern': 'percent_10',
+ 'fg_color': '#C00000',
+ 'bg_color': '#FFFFFF'
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$3',
+ 'pattern': {
+ 'pattern': 'percent_60',
+ 'fg_color': '#FF0000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$C$1:$C$3',
+ 'pattern': {
+ 'pattern': 'light_upward_diagonal',
+ 'fg_color': '#FFC000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$D$1:$D$3',
+ 'pattern': {
+ 'pattern': 'light_horizontal',
+ 'fg_color': '#FFFF00',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$E$1:$E$3',
+ 'pattern': {
+ 'pattern': 'dashed_upward_diagonal',
+ 'fg_color': '#92D050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$F$1:$F$3',
+ 'pattern': {
+ 'pattern': 'wave',
+ 'fg_color': '#00B050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$G$1:$G$3',
+ 'pattern': {
+ 'pattern': 'dotted_grid',
+ 'fg_color': '#00B0F0',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$H$1:$H$3',
+ 'pattern': {
+ 'pattern': 'large_grid',
+ 'fg_color': '#0070C0',
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pattern04.py b/xlsxwriter/test/comparison/test_chart_pattern04.py
new file mode 100644
index 0000000..2c6e792
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pattern04.py
@@ -0,0 +1,129 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pattern04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [110902272, 110756608]
+
+ data = [
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+ worksheet.write_column('D1', data[3])
+ worksheet.write_column('E1', data[4])
+ worksheet.write_column('F1', data[5])
+ worksheet.write_column('G1', data[6])
+ worksheet.write_column('H1', data[7])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$3',
+ 'pattern': {
+ 'pattern': 'percent_20',
+ 'fg_color': '#C00000',
+ 'bg_color': '#FFFFFF'
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$3',
+ 'pattern': {
+ 'pattern': 'percent_70',
+ 'fg_color': '#FF0000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$C$1:$C$3',
+ 'pattern': {
+ 'pattern': 'dark_downward_diagonal',
+ 'fg_color': '#FFC000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$D$1:$D$3',
+ 'pattern': {
+ 'pattern': 'narrow_vertical',
+ 'fg_color': '#FFFF00',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$E$1:$E$3',
+ 'pattern': {
+ 'pattern': 'dashed_horizontal',
+ 'fg_color': '#92D050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$F$1:$F$3',
+ 'pattern': {
+ 'pattern': 'diagonal_brick',
+ 'fg_color': '#00B050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$G$1:$G$3',
+ 'pattern': {
+ 'pattern': 'dotted_diamond',
+ 'fg_color': '#00B0F0',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$H$1:$H$3',
+ 'pattern': {
+ 'pattern': 'small_check',
+ 'fg_color': '#0070C0',
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pattern05.py b/xlsxwriter/test/comparison/test_chart_pattern05.py
new file mode 100644
index 0000000..c0a14bf
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pattern05.py
@@ -0,0 +1,129 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pattern05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [110902272, 110756608]
+
+ data = [
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+ worksheet.write_column('D1', data[3])
+ worksheet.write_column('E1', data[4])
+ worksheet.write_column('F1', data[5])
+ worksheet.write_column('G1', data[6])
+ worksheet.write_column('H1', data[7])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$3',
+ 'pattern': {
+ 'pattern': 'percent_25',
+ 'fg_color': '#C00000',
+ 'bg_color': '#FFFFFF'
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$3',
+ 'pattern': {
+ 'pattern': 'percent_75',
+ 'fg_color': '#FF0000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$C$1:$C$3',
+ 'pattern': {
+ 'pattern': 'dark_upward_diagonal',
+ 'fg_color': '#FFC000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$D$1:$D$3',
+ 'pattern': {
+ 'pattern': 'narrow_horizontal',
+ 'fg_color': '#FFFF00',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$E$1:$E$3',
+ 'pattern': {
+ 'pattern': 'dashed_vertical',
+ 'fg_color': '#92D050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$F$1:$F$3',
+ 'pattern': {
+ 'pattern': 'horizontal_brick',
+ 'fg_color': '#00B050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$G$1:$G$3',
+ 'pattern': {
+ 'pattern': 'shingle',
+ 'fg_color': '#00B0F0',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$H$1:$H$3',
+ 'pattern': {
+ 'pattern': 'large_check',
+ 'fg_color': '#0070C0',
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pattern06.py b/xlsxwriter/test/comparison/test_chart_pattern06.py
new file mode 100644
index 0000000..06d389d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pattern06.py
@@ -0,0 +1,129 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pattern06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [110902272, 110756608]
+
+ data = [
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+ worksheet.write_column('D1', data[3])
+ worksheet.write_column('E1', data[4])
+ worksheet.write_column('F1', data[5])
+ worksheet.write_column('G1', data[6])
+ worksheet.write_column('H1', data[7])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$3',
+ 'pattern': {
+ 'pattern': 'percent_30',
+ 'fg_color': '#C00000',
+ 'bg_color': '#FFFFFF'
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$3',
+ 'pattern': {
+ 'pattern': 'percent_80',
+ 'fg_color': '#FF0000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$C$1:$C$3',
+ 'pattern': {
+ 'pattern': 'wide_downward_diagonal',
+ 'fg_color': '#FFC000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$D$1:$D$3',
+ 'pattern': {
+ 'pattern': 'dark_vertical',
+ 'fg_color': '#FFFF00',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$E$1:$E$3',
+ 'pattern': {
+ 'pattern': 'small_confetti',
+ 'fg_color': '#92D050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$F$1:$F$3',
+ 'pattern': {
+ 'pattern': 'weave',
+ 'fg_color': '#00B050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$G$1:$G$3',
+ 'pattern': {
+ 'pattern': 'trellis',
+ 'fg_color': '#00B0F0',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$H$1:$H$3',
+ 'pattern': {
+ 'pattern': 'outlined_diamond',
+ 'fg_color': '#0070C0',
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pattern07.py b/xlsxwriter/test/comparison/test_chart_pattern07.py
new file mode 100644
index 0000000..a0bb72a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pattern07.py
@@ -0,0 +1,129 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pattern07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [110902272, 110756608]
+
+ data = [
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+ worksheet.write_column('D1', data[3])
+ worksheet.write_column('E1', data[4])
+ worksheet.write_column('F1', data[5])
+ worksheet.write_column('G1', data[6])
+ worksheet.write_column('H1', data[7])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$3',
+ 'pattern': {
+ 'pattern': 'percent_40',
+ 'fg_color': '#C00000',
+ 'bg_color': '#FFFFFF'
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$3',
+ 'pattern': {
+ 'pattern': 'percent_90',
+ 'fg_color': '#FF0000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$C$1:$C$3',
+ 'pattern': {
+ 'pattern': 'wide_upward_diagonal',
+ 'fg_color': '#FFC000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$D$1:$D$3',
+ 'pattern': {
+ 'pattern': 'dark_horizontal',
+ 'fg_color': '#FFFF00',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$E$1:$E$3',
+ 'pattern': {
+ 'pattern': 'large_confetti',
+ 'fg_color': '#92D050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$F$1:$F$3',
+ 'pattern': {
+ 'pattern': 'plaid',
+ 'fg_color': '#00B050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$G$1:$G$3',
+ 'pattern': {
+ 'pattern': 'sphere',
+ 'fg_color': '#00B0F0',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$H$1:$H$3',
+ 'pattern': {
+ 'pattern': 'solid_diamond',
+ 'fg_color': '#0070C0',
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pattern08.py b/xlsxwriter/test/comparison/test_chart_pattern08.py
new file mode 100644
index 0000000..f3d76b0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pattern08.py
@@ -0,0 +1,129 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pattern08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [91631616, 91633152]
+
+ data = [
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+ worksheet.write_column('D1', data[3])
+ worksheet.write_column('E1', data[4])
+ worksheet.write_column('F1', data[5])
+ worksheet.write_column('G1', data[6])
+ worksheet.write_column('H1', data[7])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$3',
+ 'pattern': {
+ 'pattern': 'percent_5',
+ 'fg_color': 'yellow',
+ 'bg_color': 'red'
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$B$1:$B$3',
+ 'pattern': {
+ 'pattern': 'percent_50',
+ 'fg_color': '#FF0000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$C$1:$C$3',
+ 'pattern': {
+ 'pattern': 'light_downward_diagonal',
+ 'fg_color': '#FFC000',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$D$1:$D$3',
+ 'pattern': {
+ 'pattern': 'light_vertical',
+ 'fg_color': '#FFFF00',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$E$1:$E$3',
+ 'pattern': {
+ 'pattern': 'dashed_downward_diagonal',
+ 'fg_color': '#92D050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$F$1:$F$3',
+ 'pattern': {
+ 'pattern': 'zigzag',
+ 'fg_color': '#00B050',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$G$1:$G$3',
+ 'pattern': {
+ 'pattern': 'divot',
+ 'fg_color': '#00B0F0',
+ }
+ })
+
+ chart.add_series({
+ 'values': '=Sheet1!$H$1:$H$3',
+ 'pattern': {
+ 'pattern': 'small_grid',
+ 'fg_color': '#0070C0',
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pattern09.py b/xlsxwriter/test/comparison/test_chart_pattern09.py
new file mode 100644
index 0000000..90abf3d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pattern09.py
@@ -0,0 +1,81 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pattern09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [115359744, 115361280]
+
+ data = [
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+ worksheet.write_column('D1', data[3])
+ worksheet.write_column('E1', data[4])
+ worksheet.write_column('F1', data[5])
+ worksheet.write_column('G1', data[6])
+ worksheet.write_column('H1', data[7])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$3'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$3'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$3'})
+ chart.add_series({'values': '=Sheet1!$D$1:$D$3'})
+ chart.add_series({'values': '=Sheet1!$E$1:$E$3'})
+ chart.add_series({'values': '=Sheet1!$F$1:$F$3'})
+ chart.add_series({'values': '=Sheet1!$G$1:$G$3'})
+ chart.add_series({'values': '=Sheet1!$H$1:$H$3'})
+
+ chart.set_chartarea({
+ 'pattern': {
+ 'pattern': 'percent_5',
+ 'fg_color': 'red',
+ 'bg_color': 'yellow',
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pattern10.py b/xlsxwriter/test/comparison/test_chart_pattern10.py
new file mode 100644
index 0000000..071bc56
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pattern10.py
@@ -0,0 +1,81 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pattern10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [143227136, 143245312]
+
+ data = [
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ [2, 2, 2],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+ worksheet.write_column('D1', data[3])
+ worksheet.write_column('E1', data[4])
+ worksheet.write_column('F1', data[5])
+ worksheet.write_column('G1', data[6])
+ worksheet.write_column('H1', data[7])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$3'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$3'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$3'})
+ chart.add_series({'values': '=Sheet1!$D$1:$D$3'})
+ chart.add_series({'values': '=Sheet1!$E$1:$E$3'})
+ chart.add_series({'values': '=Sheet1!$F$1:$F$3'})
+ chart.add_series({'values': '=Sheet1!$G$1:$G$3'})
+ chart.add_series({'values': '=Sheet1!$H$1:$H$3'})
+
+ chart.set_plotarea({
+ 'pattern': {
+ 'pattern': 'percent_5',
+ 'fg_color': 'red',
+ 'bg_color': 'yellow',
+ }
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pie01.py b/xlsxwriter/test/comparison/test_chart_pie01.py
new file mode 100644
index 0000000..24183f7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pie01.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pie01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [
+ [2, 4, 6],
+ [60, 30, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$3',
+ 'values': '=Sheet1!$B$1:$B$3',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pie02.py b/xlsxwriter/test/comparison/test_chart_pie02.py
new file mode 100644
index 0000000..b84d794
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pie02.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pie02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [
+ [2, 4, 6],
+ [60, 30, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$3',
+ 'values': '=Sheet1!$B$1:$B$3',
+ })
+
+ chart.set_legend({'font': {'bold': 1, 'italic': 1, 'baseline': -1}})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pie03.py b/xlsxwriter/test/comparison/test_chart_pie03.py
new file mode 100644
index 0000000..574998c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pie03.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pie03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [
+ [2, 4, 6],
+ [60, 30, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$3',
+ 'values': '=Sheet1!$B$1:$B$3',
+ })
+
+ chart.set_legend({'delete_series': [1]})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pie04.py b/xlsxwriter/test/comparison/test_chart_pie04.py
new file mode 100644
index 0000000..99420c0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pie04.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pie04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [
+ [2, 4, 6],
+ [60, 30, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$3',
+ 'values': '=Sheet1!$B$1:$B$3',
+ })
+
+ chart.set_legend({'position': 'overlay_right'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_pie05.py b/xlsxwriter/test/comparison/test_chart_pie05.py
new file mode 100644
index 0000000..3a959cd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_pie05.py
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_pie05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [
+ [2, 4, 6],
+ [60, 30, 10],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$3',
+ 'values': '=Sheet1!$B$1:$B$3',
+ })
+
+ chart.set_rotation(45)
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_points01.py b/xlsxwriter/test/comparison/test_chart_points01.py
new file mode 100644
index 0000000..cb47a5d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_points01.py
@@ -0,0 +1,51 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_points01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with point formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [2, 5, 4, 1, 7, 4]
+
+ worksheet.write_column('A1', data)
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'points': [{'fill': {'color': 'red'}}],
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_points02.py b/xlsxwriter/test/comparison/test_chart_points02.py
new file mode 100644
index 0000000..ddab73d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_points02.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_points02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with point formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ data = [2, 5, 4, 1, 7, 4]
+
+ worksheet.write_column('A1', data)
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$6',
+ 'points': [
+ None, {'border': {'color': 'red', 'dash_type': 'square_dot'}},
+ None, {'fill': {'color': 'yellow'}}
+ ],
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_points03.py b/xlsxwriter/test/comparison/test_chart_points03.py
new file mode 100644
index 0000000..0cfb222
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_points03.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_points03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with point formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'pie'})
+
+ workbook.custom_colors = ['FFCC0000', 'FF990000']
+
+ data = [2, 5, 4]
+
+ worksheet.write_column('A1', data)
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$3',
+ 'points': [
+ {'fill': {'color': '#FF0000'}},
+ {'fill': {'color': '#CC0000'}},
+ {'fill': {'color': '#990000'}},
+ ],
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_points04.py b/xlsxwriter/test/comparison/test_chart_points04.py
new file mode 100644
index 0000000..7b92c23
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_points04.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_points04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with point formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [48542464, 46807296]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'points': [
+ {'fill': {'color': 'red'}},
+ {'fill': {'color': 'yellow'}}
+ ],
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'points': [None, None, {'fill': {'color': 'yellow'}}],
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_points05.py b/xlsxwriter/test/comparison/test_chart_points05.py
new file mode 100644
index 0000000..fe1db01
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_points05.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_points05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with point formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [45471616, 46804992]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'marker': {'type': 'automatic'},
+ 'points': [{'fill': {'color': 'red'}}],
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'marker': {'type': 'automatic'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_points06.py b/xlsxwriter/test/comparison/test_chart_points06.py
new file mode 100644
index 0000000..ca3ce27
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_points06.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_points06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with point formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [71050368, 71051904]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'marker': {'type': 'automatic'},
+ 'points': [{'fill': {'color': 'red'}}],
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'marker': {'type': 'automatic'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_radar01.py b/xlsxwriter/test/comparison/test_chart_radar01.py
new file mode 100644
index 0000000..3018c1f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_radar01.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_radar01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'radar'})
+
+ chart.axis_ids = [56801152, 56802688]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_radar02.py b/xlsxwriter/test/comparison/test_chart_radar02.py
new file mode 100644
index 0000000..c536fe1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_radar02.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_radar02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'radar',
+ 'subtype': 'with_markers'
+ })
+
+ chart.axis_ids = [48543616, 48545152]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_radar03.py b/xlsxwriter/test/comparison/test_chart_radar03.py
new file mode 100644
index 0000000..ef3656c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_radar03.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_radar03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'radar',
+ 'subtype': 'filled'
+ })
+
+ chart.axis_ids = [56802304, 56845440]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter01.py b/xlsxwriter/test/comparison/test_chart_scatter01.py
new file mode 100644
index 0000000..49d134a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter01.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [40262272, 40260352]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5'})
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter02.py b/xlsxwriter/test/comparison/test_chart_scatter02.py
new file mode 100644
index 0000000..d0459dd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter02.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter', 'subtype': 'straight_with_markers'})
+
+ chart.axis_ids = [54514816, 45705856]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter03.py b/xlsxwriter/test/comparison/test_chart_scatter03.py
new file mode 100644
index 0000000..9cca067
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter03.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter', 'subtype': 'straight'})
+
+ chart.axis_ids = [54010624, 45705856]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5'})
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter04.py b/xlsxwriter/test/comparison/test_chart_scatter04.py
new file mode 100644
index 0000000..2d241fb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter04.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter',
+ 'subtype': 'smooth_with_markers'})
+
+ chart.axis_ids = [54011008, 45706240]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5'})
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter05.py b/xlsxwriter/test/comparison/test_chart_scatter05.py
new file mode 100644
index 0000000..c7ca9b9
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter05.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter',
+ 'subtype': 'smooth'})
+
+ chart.axis_ids = [54011776, 54010240]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter06.py b/xlsxwriter/test/comparison/test_chart_scatter06.py
new file mode 100644
index 0000000..9d0d930
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter06.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [57708544, 44297600]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.set_x_axis({'minor_unit': 1, 'major_unit': 3})
+ chart.set_y_axis({'minor_unit': 2, 'major_unit': 4})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter07.py b/xlsxwriter/test/comparison/test_chart_scatter07.py
new file mode 100644
index 0000000..34247f1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter07.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/workbook.xml': ['<fileVersion', '<calcPr']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [63597952, 63616128]
+ chart.axis2_ids = [63617664, 63619456]
+
+ data = [
+ [27, 33, 44, 12, 1],
+ [6, 8, 6, 4, 2],
+ [20, 10, 30, 50, 40],
+ [0, 27, 23, 30, 40],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+ worksheet.write_column('D1', data[3])
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({'categories': '=Sheet1!$C$1:$C$5',
+ 'values': '=Sheet1!$D$1:$D$5',
+ 'y2_axis': 1,
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter09.py b/xlsxwriter/test/comparison/test_chart_scatter09.py
new file mode 100644
index 0000000..7fd5b9f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter09.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({
+ 'type': 'scatter',
+ 'subtype': 'smooth_with_markers'
+ })
+
+ chart.axis_ids = [45953024, 45954944]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'smooth': False,
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter10.py b/xlsxwriter/test/comparison/test_chart_scatter10.py
new file mode 100644
index 0000000..7b168fe
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter10.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({
+ 'type': 'scatter',
+ 'subtype': 'smooth'
+ })
+
+ chart.axis_ids = [84754816, 84756352]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'smooth': False,
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter11.py b/xlsxwriter/test/comparison/test_chart_scatter11.py
new file mode 100644
index 0000000..476833f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter11.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({
+ 'type': 'scatter',
+ 'subtype': 'straight_with_markers'
+ })
+
+ chart.axis_ids = [47439232, 47670400]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'smooth': True,
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter12.py b/xlsxwriter/test/comparison/test_chart_scatter12.py
new file mode 100644
index 0000000..f9136e2
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter12.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter',
+ 'subtype': 'straight'})
+
+ chart.axis_ids = [69472640, 69216896]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'marker': {'type': 'automatic'},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter13.py b/xlsxwriter/test/comparison/test_chart_scatter13.py
new file mode 100644
index 0000000..b9238cd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter13.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter',
+ 'subtype': 'straight_with_markers'})
+
+ chart.axis_ids = [69472640, 69216896]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'marker': {'type': 'none'},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter14.py b/xlsxwriter/test/comparison/test_chart_scatter14.py
new file mode 100644
index 0000000..fc4ed9a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter14.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter14.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter',
+ 'subtype': 'straight'})
+
+ chart.axis_ids = [69216512, 69214976]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ 'marker': {'type': 'star', 'size': 5},
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ 'marker': {'type': 'plus', 'size': 5},
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_scatter15.py b/xlsxwriter/test/comparison/test_chart_scatter15.py
new file mode 100644
index 0000000..676a53d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_scatter15.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_scatter15.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [58843520, 58845440]
+
+ data = [
+ ['X', 1, 3],
+ ['Y', 10, 30],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$2:$A$3',
+ 'values': '=Sheet1!$B$2:$B$3',
+ })
+
+ chart.set_x_axis({'name': '=Sheet1!$A$1',
+ 'name_font': {'italic': 1, 'baseline': -1}})
+
+ chart.set_y_axis({'name': '=Sheet1!$B$1'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_size01.py b/xlsxwriter/test/comparison/test_chart_size01.py
new file mode 100644
index 0000000..100ef42
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_size01.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_size01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter chartarea properties."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [61355904, 61365248]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_size({'width': 512, 'height': 320})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_size02.py b/xlsxwriter/test/comparison/test_chart_size02.py
new file mode 100644
index 0000000..6de15dd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_size02.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_size01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_2_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter chartarea properties."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [61355904, 61365248]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_size({'x_scale': 1.066666666, 'y_scale': 1.11111111})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_size03.py b/xlsxwriter/test/comparison/test_chart_size03.py
new file mode 100644
index 0000000..c3311f0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_size03.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_size01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_3_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter chartarea properties."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [61355904, 61365248]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart, {'x_scale': 1.066666666,
+ 'y_scale': 1.11111111})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_size04.py b/xlsxwriter/test/comparison/test_chart_size04.py
new file mode 100644
index 0000000..fb511cb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_size04.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_size04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter chartarea properties."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [73773440, 73774976]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_size({'x_offset': 8, 'y_offset': 9})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_size05.py b/xlsxwriter/test/comparison/test_chart_size05.py
new file mode 100644
index 0000000..aed3830
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_size05.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_size04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_5_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter chartarea properties."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [73773440, 73774976]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart, {'x_offset': 8, 'y_offset': 9})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_sparse01.py b/xlsxwriter/test/comparison/test_chart_sparse01.py
new file mode 100644
index 0000000..2d0becb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_sparse01.py
@@ -0,0 +1,64 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_sparse01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with series datamissing."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [46202880, 46204416]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, None, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$6',
+ 'values': '=Sheet1!$C$1:$C$6',
+ })
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_stock01.py b/xlsxwriter/test/comparison/test_chart_stock01.py
new file mode 100644
index 0000000..48e1e87
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_stock01.py
@@ -0,0 +1,72 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_stock01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'stock'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [40522880, 40524416]
+
+ data = [
+ [39083, 39084, 39085, 39086, 39087],
+ [27.2, 25.03, 19.05, 20.34, 18.5],
+ [23.49, 19.55, 15.12, 17.84, 16.34],
+ [25.45, 23.05, 17.32, 20.45, 17.34],
+ ]
+
+ for row in range(5):
+ worksheet.write(row, 0, data[0][row], date_format)
+ worksheet.write(row, 1, data[1][row])
+ worksheet.write(row, 2, data[2][row])
+ worksheet.write(row, 3, data[3][row])
+
+ worksheet.set_column('A:D', 11)
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$D$1:$D$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_stock02.py b/xlsxwriter/test/comparison/test_chart_stock02.py
new file mode 100644
index 0000000..0f114f3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_stock02.py
@@ -0,0 +1,76 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_stock02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode'],
+ 'xl/workbook.xml': ['<fileVersion', '<calcPr'],
+ 'xl/worksheets/sheet1.xml': ['<sheetView', '<selection activeCell', '</sheetView']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'stock'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [63596416, 63597952]
+ chart.axis2_ids = [51206784, 51204480]
+
+ data = [
+ [39083, 39084, 39085, 39086, 39087],
+ [27.2, 25.03, 19.05, 20.34, 18.5],
+ [23.49, 19.55, 15.12, 17.84, 16.34],
+ [25.45, 23.05, 17.32, 20.45, 17.34],
+ ]
+
+ for row in range(5):
+ worksheet.write(row, 0, data[0][row], date_format)
+ worksheet.write(row, 1, data[1][row])
+ worksheet.write(row, 2, data[2][row])
+ worksheet.write(row, 3, data[3][row])
+
+ worksheet.set_column('A:D', 11)
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.add_series({'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$D$1:$D$5',
+ 'y2_axis': 1,
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_str01.py b/xlsxwriter/test/comparison/test_chart_str01.py
new file mode 100644
index 0000000..126f896
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_str01.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_str01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file when the chart datacontains strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [40501632, 40514688]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 'Foo', 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ worksheet.write('A6', 'Foo')
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_str02.py b/xlsxwriter/test/comparison/test_chart_str02.py
new file mode 100644
index 0000000..3545824
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_str02.py
@@ -0,0 +1,64 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_str02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file when the chart datacontains strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'scatter'})
+
+ chart.axis_ids = [41671680, 41669376]
+
+ data = [
+ [1, 2, 'Foo', 4, 5],
+ [2, 4, 'Bar', 8, 10],
+ [3, 6, 'Baz', 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_table01.py b/xlsxwriter/test/comparison/test_chart_table01.py
new file mode 100644
index 0000000..532eea3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_table01.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_table01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter chart axis table properties."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [61355520, 61357056]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_table()
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_table02.py b/xlsxwriter/test/comparison/test_chart_table02.py
new file mode 100644
index 0000000..9ee5e6d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_table02.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_table02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test XlsxWriter chart axis table properties."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [61354368, 61355904]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_table({
+ 'vertical': False,
+ 'horizontal': False,
+ 'outline': False,
+ 'show_keys': True
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_table03.py b/xlsxwriter/test/comparison/test_chart_table03.py
new file mode 100644
index 0000000..0cd03b0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_table03.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_table03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<a:pPr']}
+
+ def test_create_file(self):
+ """Test XlsxWriter chart axis table properties."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [108636032, 108643840]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_table({
+ 'vertical': False,
+ 'horizontal': False,
+ 'outline': False,
+ 'show_keys': True,
+ 'font': {'bold': True, 'italic': True, 'color': 'red', 'baseline': -1}
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_title01.py b/xlsxwriter/test/comparison/test_chart_title01.py
new file mode 100644
index 0000000..d72d75f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_title01.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_title01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with default title."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [46165376, 54462720]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5',
+ 'name': 'Foo'})
+
+ chart.set_title({'none': True})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_title02.py b/xlsxwriter/test/comparison/test_chart_title02.py
new file mode 100644
index 0000000..9d638f0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_title02.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_title02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with default title."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [73655040, 73656576]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+
+ chart.set_title({'name': 'Title!'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_up_down_bars01.py b/xlsxwriter/test/comparison/test_chart_up_down_bars01.py
new file mode 100644
index 0000000..2536a47
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_up_down_bars01.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_up_down_bars01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with up-down bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [46808448, 49289856]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.set_up_down_bars()
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_up_down_bars02.py b/xlsxwriter/test/comparison/test_chart_up_down_bars02.py
new file mode 100644
index 0000000..9b48425
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_up_down_bars02.py
@@ -0,0 +1,78 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_up_down_bars02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with up-down bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ chart.axis_ids = [49019136, 49222016]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 5, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.set_up_down_bars({
+ 'up': {
+ 'fill': {'color': 'red'},
+ 'line': {'color': 'yellow'}
+ },
+ 'down': {
+ 'fill': {'color': '#00B050'},
+ 'border': {
+ 'color': '#00B0F0',
+ 'dash_type': 'square_dot'
+ },
+ },
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chart_up_down_bars03.py b/xlsxwriter/test/comparison/test_chart_up_down_bars03.py
new file mode 100644
index 0000000..4e55cf5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chart_up_down_bars03.py
@@ -0,0 +1,77 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chart_up_down_bars03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/charts/chart1.xml': ['<c:formatCode']}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with up-down bars."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'stock'})
+ date_format = workbook.add_format({'num_format': 14})
+
+ chart.axis_ids = [45472768, 49017216]
+
+ data = [
+ [39083, 39084, 39085, 39086, 39087],
+ [27.2, 25.03, 19.05, 20.34, 18.5],
+ [23.49, 19.55, 15.12, 17.84, 16.34],
+ [25.45, 23.05, 17.32, 20.45, 17.34],
+ ]
+
+ for row in range(5):
+ worksheet.write(row, 0, data[0][row], date_format)
+ worksheet.write(row, 1, data[1][row])
+ worksheet.write(row, 2, data[2][row])
+ worksheet.write(row, 3, data[3][row])
+
+ worksheet.set_column('A:D', 11)
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$B$1:$B$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$C$1:$C$5',
+ })
+
+ chart.add_series({
+ 'categories': '=Sheet1!$A$1:$A$5',
+ 'values': '=Sheet1!$D$1:$D$5',
+ })
+
+ chart.set_up_down_bars()
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chartsheet01.py b/xlsxwriter/test/comparison/test_chartsheet01.py
new file mode 100644
index 0000000..9151238
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chartsheet01.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chartsheet01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the worksheet properties of an XlsxWriter chartsheet file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chartsheet = workbook.add_chartsheet()
+
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [79858304, 79860096]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chartsheet.set_chart(chart)
+ chartsheet.activate()
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chartsheet02.py b/xlsxwriter/test/comparison/test_chartsheet02.py
new file mode 100644
index 0000000..d6b11e5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chartsheet02.py
@@ -0,0 +1,64 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chartsheet02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the worksheet properties of an XlsxWriter chartsheet file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ chartsheet = workbook.add_chartsheet()
+ worksheet2 = workbook.add_worksheet()
+
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [79858304, 79860096]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet1.write_column('A1', data[0])
+ worksheet1.write_column('B1', data[1])
+ worksheet1.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chartsheet.set_chart(chart)
+ chartsheet.activate()
+ worksheet2.select()
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chartsheet03.py b/xlsxwriter/test/comparison/test_chartsheet03.py
new file mode 100644
index 0000000..e56bd6e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chartsheet03.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chartsheet03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the worksheet properties of an XlsxWriter chartsheet file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chartsheet = workbook.add_chartsheet()
+
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [43794816, 43796352]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chartsheet.set_chart(chart)
+ chartsheet.hide()
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chartsheet04.py b/xlsxwriter/test/comparison/test_chartsheet04.py
new file mode 100644
index 0000000..864a281
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chartsheet04.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chartsheet04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the worksheet properties of an XlsxWriter chartsheet file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chartsheet = workbook.add_chartsheet()
+
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [43913216, 43914752]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chartsheet.protect()
+ chartsheet.set_chart(chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chartsheet05.py b/xlsxwriter/test/comparison/test_chartsheet05.py
new file mode 100644
index 0000000..967a17b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chartsheet05.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chartsheet05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the worksheet properties of an XlsxWriter chartsheet file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chartsheet = workbook.add_chartsheet()
+
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [43695104, 43787008]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chartsheet.set_zoom(75)
+ chartsheet.set_chart(chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chartsheet06.py b/xlsxwriter/test/comparison/test_chartsheet06.py
new file mode 100644
index 0000000..5b6da28
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chartsheet06.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chartsheet06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the worksheet properties of an XlsxWriter chartsheet file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chartsheet = workbook.add_chartsheet()
+
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [43778432, 43780352]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chartsheet.set_tab_color('red')
+ chartsheet.set_chart(chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chartsheet07.py b/xlsxwriter/test/comparison/test_chartsheet07.py
new file mode 100644
index 0000000..3f5d11a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chartsheet07.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chartsheet07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/chartsheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/chartsheets/sheet1.xml': ['<pageSetup', '<drawing']}
+
+ def test_create_file(self):
+ """Test the worksheet properties of an XlsxWriter chartsheet file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chartsheet = workbook.add_chartsheet()
+
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [43911040, 46363392]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chartsheet.set_paper(9)
+ chartsheet.set_portrait()
+ chartsheet.set_chart(chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chartsheet08.py b/xlsxwriter/test/comparison/test_chartsheet08.py
new file mode 100644
index 0000000..f30d266
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chartsheet08.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chartsheet08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/chartsheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/chartsheets/sheet1.xml': ['<pageSetup', '<drawing']}
+
+ def test_create_file(self):
+ """Test the worksheet properties of an XlsxWriter chartsheet file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chartsheet = workbook.add_chartsheet()
+
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [46320256, 46335872]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chartsheet.set_margins(left='0.70866141732283472',
+ right='0.70866141732283472',
+ top='0.74803149606299213',
+ bottom='0.74803149606299213')
+ chartsheet.set_header('Page &P', '0.51181102362204722')
+ chartsheet.set_footer('&A', '0.51181102362204722')
+ chartsheet.set_chart(chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_chartsheet09.py b/xlsxwriter/test/comparison/test_chartsheet09.py
new file mode 100644
index 0000000..e721929
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_chartsheet09.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'chartsheet09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the worksheet properties of an XlsxWriter chartsheet filewith series format properties."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chartsheet = workbook.add_chartsheet()
+
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [49044480, 49055232]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({
+ 'values': '=Sheet1!$A$1:$A$5',
+ 'border': {'color': 'yellow'},
+ 'fill': {'color': 'red'},
+ })
+
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chartsheet.set_chart(chart)
+ chartsheet.activate()
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_comment01.py b/xlsxwriter/test/comparison/test_comment01.py
new file mode 100644
index 0000000..7d3042b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_comment01.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'comment01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with comments."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write_comment('B2', 'Some text')
+
+ # Set the author to match the target XLSX file.
+ worksheet.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_comment02.py b/xlsxwriter/test/comparison/test_comment02.py
new file mode 100644
index 0000000..a35f291
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_comment02.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'comment02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with comments."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write_comment('B2', 'Some text')
+ worksheet.write_comment('D17', 'More text')
+
+ # Set the author to match the target XLSX file.
+ worksheet.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_comment03.py b/xlsxwriter/test/comparison/test_comment03.py
new file mode 100644
index 0000000..d21de86
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_comment03.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'comment03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with comments."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write_comment('A1', 'Some text')
+ worksheet.write_comment('XFD1048576', 'Some text')
+
+ worksheet.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_comment04.py b/xlsxwriter/test/comparison/test_comment04.py
new file mode 100644
index 0000000..e15ba9e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_comment04.py
@@ -0,0 +1,50 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'comment04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with comments."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet()
+
+ worksheet1.write('A1', 'Foo')
+ worksheet1.write_comment('B2', 'Some text')
+
+ worksheet3.write('A1', 'Bar')
+ worksheet3.write_comment('C7', 'More text')
+
+ worksheet1.set_comments_author('John')
+ worksheet3.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_comment05.py b/xlsxwriter/test/comparison/test_comment05.py
new file mode 100644
index 0000000..3630982
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_comment05.py
@@ -0,0 +1,55 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'comment05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ # It takes about 50 times longer to run this test with this file
+ # included. Only turn it on for pre-release testing.
+ self.ignore_files = ['xl/drawings/vmlDrawing1.vml']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a simple XlsxWriter file with comments.
+ Test the VML data and shape ids for blocks of comments > 1024.
+ """
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet()
+
+ for row in range(0, 127 + 1):
+ for col in range(0, 15 + 1):
+ worksheet1.write_comment(row, col, 'Some text')
+
+ worksheet3.write_comment('A1', 'More text')
+
+ worksheet1.set_comments_author('John')
+ worksheet3.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_comment06.py b/xlsxwriter/test/comparison/test_comment06.py
new file mode 100644
index 0000000..f0373dd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_comment06.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'comment06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with comments."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_comment('A1', 'Some text')
+ worksheet.write_comment('A2', 'Some text')
+ worksheet.write_comment('A3', 'Some text', {'visible': True})
+ worksheet.write_comment('A4', 'Some text')
+ worksheet.write_comment('A5', 'Some text')
+
+ worksheet.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_comment07.py b/xlsxwriter/test/comparison/test_comment07.py
new file mode 100644
index 0000000..3a0365b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_comment07.py
@@ -0,0 +1,49 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'comment07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with comments."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_comment('A1', 'Some text')
+ worksheet.write_comment('A2', 'Some text')
+ worksheet.write_comment('A3', 'Some text')
+ worksheet.write_comment('A4', 'Some text')
+ worksheet.write_comment('A5', 'Some text')
+
+ worksheet.show_comments()
+
+ worksheet.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_comment08.py b/xlsxwriter/test/comparison/test_comment08.py
new file mode 100644
index 0000000..330d552
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_comment08.py
@@ -0,0 +1,49 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'comment08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with comments."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_comment('A1', 'Some text')
+ worksheet.write_comment('A2', 'Some text')
+ worksheet.write_comment('A3', 'Some text', {'visible': False})
+ worksheet.write_comment('A4', 'Some text', {'visible': True})
+ worksheet.write_comment('A5', 'Some text')
+
+ worksheet.show_comments()
+
+ worksheet.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_comment09.py b/xlsxwriter/test/comparison/test_comment09.py
new file mode 100644
index 0000000..5b2b540
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_comment09.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'comment09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with comments."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_comment('A1', 'Some text', {'author': 'John'})
+ worksheet.write_comment('A2', 'Some text', {'author': 'Perl'})
+ worksheet.write_comment('A3', 'Some text')
+
+ worksheet.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_comment10.py b/xlsxwriter/test/comparison/test_comment10.py
new file mode 100644
index 0000000..e9bbaf6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_comment10.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'comment10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with comments."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write_comment('B2', 'Some text', {'color': '#98fe97'})
+
+ worksheet.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_comment12.py b/xlsxwriter/test/comparison/test_comment12.py
new file mode 100644
index 0000000..58fd8e3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_comment12.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'comment12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with comments."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_row(0, 21)
+ worksheet.set_column('B:B', 10)
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write_comment('A1', 'Some text')
+
+ worksheet.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_cond_format01.py b/xlsxwriter/test/comparison/test_cond_format01.py
new file mode 100644
index 0000000..e1c0abd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_cond_format01.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'cond_format01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with conditional formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ cell_format = workbook.add_format({
+ 'color': '#9C0006',
+ 'bg_color': '#FFC7CE',
+ 'font_condense': 1,
+ 'font_extend': 1
+ })
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1:A1',
+ {'type': 'cell',
+ 'format': cell_format,
+ 'criteria': 'greater than',
+ 'value': 5
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_cond_format02.py b/xlsxwriter/test/comparison/test_cond_format02.py
new file mode 100644
index 0000000..a506915
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_cond_format02.py
@@ -0,0 +1,53 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'cond_format02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with conditional formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ cell_format = None # Test the null format.
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1',
+ {'type': 'cell',
+ 'format': cell_format,
+ 'criteria': '<',
+ 'value': 5
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_cond_format03.py b/xlsxwriter/test/comparison/test_cond_format03.py
new file mode 100644
index 0000000..43496f7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_cond_format03.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'cond_format03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with conditional formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format1 = workbook.add_format({'font_strikeout': 1, 'dxf_index': 1})
+ format2 = workbook.add_format({'underline': 1, 'dxf_index': 0})
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1',
+ {'type': 'cell',
+ 'format': format1,
+ 'criteria': 'between',
+ 'minimum': 2,
+ 'maximum': 6,
+ })
+
+ worksheet.conditional_format('A1',
+ {'type': 'cell',
+ 'format': format2,
+ 'criteria': 'greater than',
+ 'value': 1,
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_cond_format04.py b/xlsxwriter/test/comparison/test_cond_format04.py
new file mode 100644
index 0000000..9bd7db3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_cond_format04.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'cond_format04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with conditional formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format1 = workbook.add_format({'num_format': 2, 'dxf_index': 1})
+ format2 = workbook.add_format({'num_format': '0.000', 'dxf_index': 0})
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1',
+ {'type': 'cell',
+ 'format': format1,
+ 'criteria': '>',
+ 'value': 2,
+ })
+
+ worksheet.conditional_format('A2',
+ {'type': 'cell',
+ 'format': format2,
+ 'criteria': '<',
+ 'value': 8,
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_cond_format05.py b/xlsxwriter/test/comparison/test_cond_format05.py
new file mode 100644
index 0000000..50e1ae9
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_cond_format05.py
@@ -0,0 +1,53 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'cond_format05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with conditional formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format1 = workbook.add_format({'border': 1})
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1',
+ {'type': 'cell',
+ 'format': format1,
+ 'criteria': '==',
+ 'value': 7,
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_cond_format06.py b/xlsxwriter/test/comparison/test_cond_format06.py
new file mode 100644
index 0000000..bc19acb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_cond_format06.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'cond_format06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with conditional formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format1 = workbook.add_format({
+ 'pattern': 15,
+ 'fg_color': '#FF0000',
+ 'bg_color': '#FFFF00'
+ })
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1',
+ {'type': 'cell',
+ 'format': format1,
+ 'criteria': '>',
+ 'value': 7,
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_cond_format07.py b/xlsxwriter/test/comparison/test_cond_format07.py
new file mode 100644
index 0000000..86ee74d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_cond_format07.py
@@ -0,0 +1,73 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'cond_format07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with conditional formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format1 = workbook.add_format({'bg_color': '#FF0000', 'dxf_index': 1})
+ format2 = workbook.add_format({'bg_color': '#92D050', 'dxf_index': 0})
+
+ data = [
+ [90, 80, 50, 10, 20, 90, 40, 90, 30, 40],
+ [20, 10, 90, 100, 30, 60, 70, 60, 50, 90],
+ [10, 50, 60, 50, 20, 50, 80, 30, 40, 60],
+ [10, 90, 20, 40, 10, 40, 50, 70, 90, 50],
+ [70, 100, 10, 90, 10, 10, 20, 100, 100, 40],
+ [20, 60, 10, 100, 30, 10, 20, 60, 100, 10],
+ [10, 60, 10, 80, 100, 80, 30, 30, 70, 40],
+ [30, 90, 60, 10, 10, 100, 40, 40, 30, 40],
+ [80, 90, 10, 20, 20, 50, 80, 20, 60, 90],
+ [60, 80, 30, 30, 10, 50, 80, 60, 50, 30],
+ ]
+
+ for row, row_data in enumerate(data):
+ worksheet.write_row(row, 0, row_data)
+ row += 1
+
+ worksheet.conditional_format('A1:J10',
+ {'type': 'cell',
+ 'format': format1,
+ 'criteria': '>=',
+ 'value': 50,
+ })
+
+ worksheet.conditional_format('A1:J10',
+ {'type': 'cell',
+ 'format': format2,
+ 'criteria': '<',
+ 'value': 50,
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_cond_format08.py b/xlsxwriter/test/comparison/test_cond_format08.py
new file mode 100644
index 0000000..a2cc823
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_cond_format08.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'cond_format08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with conditional formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format1 = workbook.add_format({
+ 'color': '#9C6500',
+ 'bg_color': '#FFEB9C',
+ 'font_condense': 1,
+ 'font_extend': 1
+ })
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1',
+ {'type': 'cell',
+ 'format': format1,
+ 'criteria': 'greater than',
+ 'value': 5
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_cond_format09.py b/xlsxwriter/test/comparison/test_cond_format09.py
new file mode 100644
index 0000000..2aa3980
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_cond_format09.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'cond_format08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_9_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with conditional formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format = workbook.add_format({
+ 'color': '#9C6500',
+ 'bg_color': '#FFEB9C',
+ 'font_condense': 1,
+ 'font_extend': 1
+ })
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1',
+ {'type': 'cell',
+ 'format': format,
+ 'criteria': 'greater than',
+ 'value': 5
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_cond_format10.py b/xlsxwriter/test/comparison/test_cond_format10.py
new file mode 100644
index 0000000..7f6823a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_cond_format10.py
@@ -0,0 +1,55 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'cond_format10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with conditional formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format1 = workbook.add_format({'bold': 1, 'italic': 1})
+
+ worksheet.write('A1', 'Hello', format1)
+
+ worksheet.write('B3', 10)
+ worksheet.write('B4', 20)
+ worksheet.write('B5', 30)
+ worksheet.write('B6', 40)
+
+ worksheet.conditional_format('B3:B6',
+ {'type': 'cell',
+ 'format': format1,
+ 'criteria': 'greater than',
+ 'value': 20
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_cond_format11.py b/xlsxwriter/test/comparison/test_cond_format11.py
new file mode 100644
index 0000000..e484a2c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_cond_format11.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'cond_format11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with conditional formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format1 = workbook.add_format({'bg_color': '#FFFF00',
+ 'fg_color': '#FF0000',
+ 'pattern': 1})
+
+ worksheet.write('A1', 'Hello', format1)
+
+ worksheet.write('B3', 10)
+ worksheet.write('B4', 20)
+ worksheet.write('B5', 30)
+ worksheet.write('B6', 40)
+
+ worksheet.conditional_format('B3:B6',
+ {'type': 'cell',
+ 'format': format1,
+ 'criteria': 'greater than',
+ 'value': 20
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_cond_format12.py b/xlsxwriter/test/comparison/test_cond_format12.py
new file mode 100644
index 0000000..34afd1f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_cond_format12.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'cond_format12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with conditional formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format1 = workbook.add_format({'bg_color': '#FFFF00',
+ 'fg_color': '#FF0000',
+ 'pattern': 12})
+
+ worksheet.write('A1', 'Hello', format1)
+
+ worksheet.write('B3', 10)
+ worksheet.write('B4', 20)
+ worksheet.write('B5', 30)
+ worksheet.write('B6', 40)
+
+ worksheet.conditional_format('B3:B6',
+ {'type': 'cell',
+ 'format': format1,
+ 'criteria': 'greater than',
+ 'value': 20
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_cond_format13.py b/xlsxwriter/test/comparison/test_cond_format13.py
new file mode 100644
index 0000000..6ea658d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_cond_format13.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'cond_format04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with conditional formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format1 = workbook.add_format({'num_format': 2, 'dxf_index': 1})
+ format2 = workbook.add_format({'num_format': '0.000', 'dxf_index': 0})
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ options = {
+ 'type': 'cell',
+ 'format': format1,
+ 'criteria': '>',
+ 'value': 2,
+ }
+
+ worksheet.conditional_format('A1', options)
+
+ options['criteria'] = '<'
+ options['value'] = 8
+ options['format'] = format2
+
+ worksheet.conditional_format('A2', options)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_data_validation01.py b/xlsxwriter/test/comparison/test_data_validation01.py
new file mode 100644
index 0000000..6b194bb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_data_validation01.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'data_validation01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a XlsxWriter file with data validation."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.data_validation(
+ 'C2', {'validate': 'list',
+ 'value': ['Foo', 'Bar', 'Baz'],
+ }
+ )
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_data_validation02.py b/xlsxwriter/test/comparison/test_data_validation02.py
new file mode 100644
index 0000000..390eba2
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_data_validation02.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'data_validation02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a XlsxWriter file with data validation."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.data_validation(
+ 'C2', {'validate': 'list',
+ 'value': ['Foo', 'Bar', 'Baz'],
+ 'input_title': 'This is the input title',
+ 'input_message': 'This is the input message',
+ }
+ )
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_data_validation03.py b/xlsxwriter/test/comparison/test_data_validation03.py
new file mode 100644
index 0000000..f472aac
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_data_validation03.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'data_validation03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file data validation."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.data_validation(
+ 'C2', {'validate': 'list',
+ 'value': ['Foo', 'Bar', 'Baz'],
+ 'input_title': 'This is the input title',
+ 'input_message': 'This is the input message',
+ }
+ )
+
+ # Examples of the maximum input.
+ input_title = 'This is the longest input title1'
+ input_message = 'This is the longest input message ' + ('a' * 221)
+ values = [
+ "Foobar", "Foobas", "Foobat", "Foobau", "Foobav", "Foobaw",
+ "Foobax", "Foobay", "Foobaz", "Foobba", "Foobbb", "Foobbc",
+ "Foobbd", "Foobbe", "Foobbf", "Foobbg", "Foobbh", "Foobbi",
+ "Foobbj", "Foobbk", "Foobbl", "Foobbm", "Foobbn", "Foobbo",
+ "Foobbp", "Foobbq", "Foobbr", "Foobbs", "Foobbt", "Foobbu",
+ "Foobbv", "Foobbw", "Foobbx", "Foobby", "Foobbz", "Foobca",
+ "End"
+ ]
+
+ worksheet.data_validation(
+ 'D6', {'validate': 'list',
+ 'value': values,
+ 'input_title': input_title,
+ 'input_message': input_message,
+ }
+ )
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_data_validation04.py b/xlsxwriter/test/comparison/test_data_validation04.py
new file mode 100644
index 0000000..e209954
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_data_validation04.py
@@ -0,0 +1,72 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'data_validation02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_4' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file data validation."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.data_validation(
+ 'C2', {'validate': 'list',
+ 'value': ['Foo', 'Bar', 'Baz'],
+ 'input_title': 'This is the input title',
+ 'input_message': 'This is the input message',
+ }
+ )
+
+ # The following should be rejected becuase the input title is too long.
+ input_title = 'This is the longest input title12'
+ input_message = 'This is the longest input message ' + ('a' * 221)
+ values = [
+ "Foobar", "Foobas", "Foobat", "Foobau", "Foobav", "Foobaw",
+ "Foobax", "Foobay", "Foobaz", "Foobba", "Foobbb", "Foobbc",
+ "Foobbd", "Foobbe", "Foobbf", "Foobbg", "Foobbh", "Foobbi",
+ "Foobbj", "Foobbk", "Foobbl", "Foobbm", "Foobbn", "Foobbo",
+ "Foobbp", "Foobbq", "Foobbr", "Foobbs", "Foobbt", "Foobbu",
+ "Foobbv", "Foobbw", "Foobbx", "Foobby", "Foobbz", "Foobca",
+ "End"
+ ]
+
+ # Ignore the warnings raised by data_validation().
+ import warnings
+ warnings.filterwarnings('ignore')
+
+ worksheet.data_validation(
+ 'D6', {'validate': 'list',
+ 'value': values,
+ 'input_title': input_title,
+ 'input_message': input_message,
+ }
+ )
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_data_validation05.py b/xlsxwriter/test/comparison/test_data_validation05.py
new file mode 100644
index 0000000..fa158a1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_data_validation05.py
@@ -0,0 +1,72 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'data_validation02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_5' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file data validation."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.data_validation(
+ 'C2', {'validate': 'list',
+ 'value': ['Foo', 'Bar', 'Baz'],
+ 'input_title': 'This is the input title',
+ 'input_message': 'This is the input message',
+ }
+ )
+
+ # The following should be rejected because the input message is too long.
+ input_title = 'This is the longest input title1'
+ input_message = 'This is the longest input message ' + ('a' * 222)
+ values = [
+ "Foobar", "Foobas", "Foobat", "Foobau", "Foobav", "Foobaw",
+ "Foobax", "Foobay", "Foobaz", "Foobba", "Foobbb", "Foobbc",
+ "Foobbd", "Foobbe", "Foobbf", "Foobbg", "Foobbh", "Foobbi",
+ "Foobbj", "Foobbk", "Foobbl", "Foobbm", "Foobbn", "Foobbo",
+ "Foobbp", "Foobbq", "Foobbr", "Foobbs", "Foobbt", "Foobbu",
+ "Foobbv", "Foobbw", "Foobbx", "Foobby", "Foobbz", "Foobca",
+ "End"
+ ]
+
+ # Ignore the warnings raised by data_validation().
+ import warnings
+ warnings.filterwarnings('ignore')
+
+ worksheet.data_validation(
+ 'D6', {'validate': 'list',
+ 'value': values,
+ 'input_title': input_title,
+ 'input_message': input_message,
+ }
+ )
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_data_validation06.py b/xlsxwriter/test/comparison/test_data_validation06.py
new file mode 100644
index 0000000..8160dab
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_data_validation06.py
@@ -0,0 +1,72 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'data_validation02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_5' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file data validation."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.data_validation(
+ 'C2', {'validate': 'list',
+ 'value': ['Foo', 'Bar', 'Baz'],
+ 'input_title': 'This is the input title',
+ 'input_message': 'This is the input message',
+ }
+ )
+
+ # The following should be rejected because the list items are too long.
+ input_title = 'This is the longest input title1'
+ input_message = 'This is the longest input message ' + ('a' * 221)
+ values = [
+ "Foobar", "Foobas", "Foobat", "Foobau", "Foobav", "Foobaw",
+ "Foobax", "Foobay", "Foobaz", "Foobba", "Foobbb", "Foobbc",
+ "Foobbd", "Foobbe", "Foobbf", "Foobbg", "Foobbh", "Foobbi",
+ "Foobbj", "Foobbk", "Foobbl", "Foobbm", "Foobbn", "Foobbo",
+ "Foobbp", "Foobbq", "Foobbr", "Foobbs", "Foobbt", "Foobbu",
+ "Foobbv", "Foobbw", "Foobbx", "Foobby", "Foobbz", "Foobca",
+ "End1"
+ ]
+
+ # Ignore the warnings raised by data_validation().
+ import warnings
+ warnings.filterwarnings('ignore')
+
+ worksheet.data_validation(
+ 'D6', {'validate': 'list',
+ 'value': values,
+ 'input_title': input_title,
+ 'input_message': input_message,
+ }
+ )
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_data_validation07.py b/xlsxwriter/test/comparison/test_data_validation07.py
new file mode 100644
index 0000000..ba7d999
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_data_validation07.py
@@ -0,0 +1,46 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'data_validation07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a XlsxWriter file with data validation."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.data_validation(
+ 'C2', {'validate': 'list',
+ 'value': ['coffee', 'café'],
+ }
+ )
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_data_validation08.py b/xlsxwriter/test/comparison/test_data_validation08.py
new file mode 100644
index 0000000..1787da7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_data_validation08.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'data_validation08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a XlsxWriter file with data validation."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.data_validation(
+ 'C2', {'validate': 'any',
+ 'input_title': 'This is the input title',
+ 'input_message': 'This is the input message',
+ }
+ )
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_date_1904_01.py b/xlsxwriter/test/comparison/test_date_1904_01.py
new file mode 100644
index 0000000..40f379d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_date_1904_01.py
@@ -0,0 +1,50 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from datetime import date
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'date_1904_01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a XlsxWriter file with date times in 1900 and1904 epochs."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ format1 = workbook.add_format({'num_format': 14})
+
+ worksheet.set_column('A:A', 12)
+
+ worksheet.write_datetime('A1', date(1900, 1, 1), format1)
+ worksheet.write_datetime('A2', date(1902, 9, 26), format1)
+ worksheet.write_datetime('A3', date(1913, 9, 8), format1)
+ worksheet.write_datetime('A4', date(1927, 5, 18), format1)
+ worksheet.write_datetime('A5', date(2173, 10, 14), format1)
+ worksheet.write_datetime('A6', date(4637, 11, 26), format1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_date_1904_02.py b/xlsxwriter/test/comparison/test_date_1904_02.py
new file mode 100644
index 0000000..1c88c93
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_date_1904_02.py
@@ -0,0 +1,50 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from datetime import date
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'date_1904_02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a XlsxWriter file with date times in 1900 and1904 epochs."""
+
+ workbook = Workbook(self.got_filename, {'date_1904': True})
+
+ worksheet = workbook.add_worksheet()
+ format1 = workbook.add_format({'num_format': 14})
+
+ worksheet.set_column('A:A', 12)
+
+ worksheet.write_datetime('A1', date(1904, 1, 1), format1)
+ worksheet.write_datetime('A2', date(1906, 9, 27), format1)
+ worksheet.write_datetime('A3', date(1917, 9, 9), format1)
+ worksheet.write_datetime('A4', date(1931, 5, 19), format1)
+ worksheet.write_datetime('A5', date(2177, 10, 15), format1)
+ worksheet.write_datetime('A6', date(4641, 11, 27), format1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_date_examples01.py b/xlsxwriter/test/comparison/test_date_examples01.py
new file mode 100644
index 0000000..97432ce
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_date_examples01.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'date_examples01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Example spreadsheet used in the tutorial."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ # Widen column A for extra visibility.
+ worksheet.set_column('A:A', 30)
+
+ # A number to convert to a date.
+ number = 41333.5
+
+ # Write it as a number without formatting.
+ worksheet.write('A1', number) # 41333.5
+
+ format2 = workbook.add_format({'num_format': 'dd/mm/yy'})
+ worksheet.write('A2', number, format2) # 28/02/13
+
+ format3 = workbook.add_format({'num_format': 'mm/dd/yy'})
+ worksheet.write('A3', number, format3) # 02/28/13
+
+ format4 = workbook.add_format({'num_format': 'd\\-m\\-yyyy'})
+ worksheet.write('A4', number, format4) # 28-2-2013
+
+ format5 = workbook.add_format({'num_format': 'dd/mm/yy\\ hh:mm'})
+ worksheet.write('A5', number, format5) # 28/02/13 12:00
+
+ format6 = workbook.add_format({'num_format': 'd\\ mmm\\ yyyy'})
+ worksheet.write('A6', number, format6) # 28 Feb 2013
+
+ format7 = workbook.add_format({'num_format': 'mmm\\ d\\ yyyy\\ hh:mm\\ AM/PM'})
+ worksheet.write('A7', number, format7) # Feb 28 2008 12:00 PM
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_default_date_format01.py b/xlsxwriter/test/comparison/test_default_date_format01.py
new file mode 100644
index 0000000..af2eb6b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_default_date_format01.py
@@ -0,0 +1,116 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from datetime import datetime
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'default_date_format01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file_user_date_format(self):
+ """Test write_datetime with explicit date format."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 12)
+
+ format1 = workbook.add_format({'num_format': 'yyyy\\-mm\\-dd'})
+
+ date1 = datetime.strptime('2013-07-25', "%Y-%m-%d")
+
+ worksheet.write_datetime(0, 0, date1, format1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_default_date_format(self):
+ """Test write_datetime with default date format."""
+
+ workbook = Workbook(self.got_filename, {'default_date_format': 'yyyy\\-mm\\-dd'})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 12)
+
+ date1 = datetime.strptime('2013-07-25', "%Y-%m-%d")
+
+ worksheet.write_datetime(0, 0, date1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_default_date_format_write(self):
+ """Test write_datetime with default date format."""
+
+ workbook = Workbook(self.got_filename, {'default_date_format': 'yyyy\\-mm\\-dd'})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 12)
+
+ date1 = datetime.strptime('2013-07-25', "%Y-%m-%d")
+
+ worksheet.write('A1', date1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_default_date_format_write_row(self):
+ """Test write_row with default date format."""
+
+ workbook = Workbook(self.got_filename, {'default_date_format': 'yyyy\\-mm\\-dd'})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 12)
+
+ date1 = datetime.strptime('2013-07-25', "%Y-%m-%d")
+
+ worksheet.write_row('A1', [date1])
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_default_date_format_write_column(self):
+ """Test write_column with default date format."""
+
+ workbook = Workbook(self.got_filename, {'default_date_format': 'yyyy\\-mm\\-dd'})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 12)
+
+ date1 = datetime.strptime('2013-07-25', "%Y-%m-%d")
+
+ worksheet.write_column(0, 0, [date1])
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_default_date_format02.py b/xlsxwriter/test/comparison/test_default_date_format02.py
new file mode 100644
index 0000000..5eab932
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_default_date_format02.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from datetime import datetime
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'default_date_format02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file_user_date_format(self):
+ """Test write_datetime with explicit date format."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 12)
+
+ format1 = workbook.add_format({'num_format': 'dd\\ mm\\ yy'})
+
+ date1 = datetime.strptime('2013-07-25', "%Y-%m-%d")
+
+ worksheet.write_datetime(0, 0, date1, format1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_default_date_format(self):
+ """Test write_datetime with default date format."""
+
+ workbook = Workbook(self.got_filename, {'default_date_format': 'dd\\ mm\\ yy'})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 12)
+
+ date1 = datetime.strptime('2013-07-25', "%Y-%m-%d")
+
+ worksheet.write_datetime(0, 0, date1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_default_format01.py b/xlsxwriter/test/comparison/test_default_format01.py
new file mode 100644
index 0000000..629f0d3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_default_format01.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'default_format01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename,
+ {'default_format_properties': {'font_size': 10}})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_default_row(12.75)
+
+ # For testing.
+ worksheet.original_row_height = 12.75
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_default_row01.py b/xlsxwriter/test/comparison/test_default_row01.py
new file mode 100644
index 0000000..3deb423
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_default_row01.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'default_row01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_default_row(24)
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write('A10', 'Bar')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_default_row02.py b/xlsxwriter/test/comparison/test_default_row02.py
new file mode 100644
index 0000000..676cc26
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_default_row02.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'default_row02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_default_row(hide_unused_rows=True)
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write('A10', 'Bar')
+
+ for row in range(1, 8 + 1):
+ worksheet.set_row(row, 15)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_default_row03.py b/xlsxwriter/test/comparison/test_default_row03.py
new file mode 100644
index 0000000..91296d1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_default_row03.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'default_row03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_default_row(24, 1)
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write('A10', 'Bar')
+
+ for row in range(1, 8 + 1):
+ worksheet.set_row(row, 24)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_default_row04.py b/xlsxwriter/test/comparison/test_default_row04.py
new file mode 100644
index 0000000..5cb5696
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_default_row04.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'default_row04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_default_row(24)
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write('A10', 'Bar')
+
+ worksheet.write_comment('C4', 'Hello', {'y_offset': 22})
+
+ worksheet.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_default_row05.py b/xlsxwriter/test/comparison/test_default_row05.py
new file mode 100644
index 0000000..ea99edf
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_default_row05.py
@@ -0,0 +1,51 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'default_row05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_default_row(24, 1)
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write('A10', 'Bar')
+ worksheet.write('A20', 'Baz')
+
+ for row in range(1, 8 + 1):
+ worksheet.set_row(row, 24)
+
+ for row in range(10, 19 + 1):
+ worksheet.set_row(row, 24)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_defined_name01.py b/xlsxwriter/test/comparison/test_defined_name01.py
new file mode 100644
index 0000000..219fc13
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_defined_name01.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'defined_name01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with defined names."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet('Sheet 3')
+
+ worksheet1.print_area('A1:E6')
+ worksheet1.autofilter('F1:G1')
+ worksheet1.write('G1', 'Filter')
+ worksheet1.write('F1', 'Auto')
+ worksheet1.fit_to_pages(2, 2)
+
+ workbook.define_name("'Sheet 3'!Bar", "='Sheet 3'!$A$1")
+ workbook.define_name("Abc", "=Sheet1!$A$1")
+ workbook.define_name("Baz", "=0.98")
+ workbook.define_name("Sheet1!Bar", "=Sheet1!$A$1")
+ workbook.define_name("Sheet2!Bar", "=Sheet2!$A$1")
+ workbook.define_name("Sheet2!aaa", "=Sheet2!$A$1")
+ workbook.define_name("_Egg", "=Sheet1!$A$1")
+ workbook.define_name("_Fog", "=Sheet1!$A$1")
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_defined_name02.py b/xlsxwriter/test/comparison/test_defined_name02.py
new file mode 100644
index 0000000..dc336d0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_defined_name02.py
@@ -0,0 +1,41 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'defined_name02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with defined names."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet('sheet One')
+
+ workbook.define_name("Sales", "='sheet One'!$G$1:$H$10")
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_defined_name03.py b/xlsxwriter/test/comparison/test_defined_name03.py
new file mode 100644
index 0000000..216d960
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_defined_name03.py
@@ -0,0 +1,41 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'defined_name03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with defined names."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet('sheet One')
+
+ workbook.define_name("Sales", "='sheet One'!G1:H10")
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_defined_name04.py b/xlsxwriter/test/comparison/test_defined_name04.py
new file mode 100644
index 0000000..d67483e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_defined_name04.py
@@ -0,0 +1,71 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'defined_name04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with defined names."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ # Test for valid Excel defined names.
+ workbook.define_name('\\__', '=Sheet1!$A$1')
+ workbook.define_name('a3f6', '=Sheet1!$A$2')
+ workbook.define_name('afoo.bar', '=Sheet1!$A$3')
+ workbook.define_name('étude', '=Sheet1!$A$4')
+ workbook.define_name('eésumé', '=Sheet1!$A$5')
+ workbook.define_name('b', '=Sheet1!$A$6')
+
+ # The following aren't valid Excel names and shouldn't be written to
+ # the output file. We ignore the warnings raised in define_name() and
+ # instead check that the output file only contains the valid names.
+ import warnings
+ warnings.filterwarnings('ignore')
+
+ workbook.define_name('.abc', '=Sheet1!$B$1')
+ workbook.define_name('GFG$', '=Sheet1!$B$1')
+ workbook.define_name('A1', '=Sheet1!$B$1')
+ workbook.define_name('XFD1048576', '=Sheet1!$B$1')
+ workbook.define_name('1A', '=Sheet1!$B$1')
+ workbook.define_name('A A', '=Sheet1!$B$1')
+ workbook.define_name('c', '=Sheet1!$B$1')
+ workbook.define_name('r', '=Sheet1!$B$1')
+ workbook.define_name('C', '=Sheet1!$B$1')
+ workbook.define_name('R', '=Sheet1!$B$1')
+ workbook.define_name('R1', '=Sheet1!$B$1')
+ workbook.define_name('C1', '=Sheet1!$B$1')
+ workbook.define_name('R1C1', '=Sheet1!$B$1')
+ workbook.define_name('R13C99', '=Sheet1!$B$1')
+
+ warnings.resetwarnings()
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_escapes01.py b/xlsxwriter/test/comparison/test_escapes01.py
new file mode 100644
index 0000000..7f7d0fb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_escapes01.py
@@ -0,0 +1,90 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'escapes01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml',
+ '[Content_Types].xml',
+ 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test creation of a file with strings that require XML escaping."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet('5&4')
+
+ worksheet.write_formula(0, 0, '=IF(1>2,0,1)', None, 1)
+ worksheet.write_formula(1, 0, """=CONCATENATE("'","<>&")""", None, "'<>&")
+ worksheet.write_formula(2, 0, '=1&"b"', None, '1b')
+ worksheet.write_formula(3, 0, """="'\"""", None, "'")
+ worksheet.write_formula(4, 0, '=""""', None, '"')
+ worksheet.write_formula(5, 0, '="&" & "&"', None, '&&')
+
+ worksheet.write_string(7, 0, '"&<>')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_write(self):
+ """Test formulas with write() method."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet('5&4')
+
+ worksheet.write(0, 0, '=IF(1>2,0,1)', None, 1)
+ worksheet.write(1, 0, """=CONCATENATE("'","<>&")""", None, "'<>&")
+ worksheet.write(2, 0, '=1&"b"', None, '1b')
+ worksheet.write(3, 0, """="'\"""", None, "'")
+ worksheet.write(4, 0, '=""""', None, '"')
+ worksheet.write(5, 0, '="&" & "&"', None, '&&')
+
+ worksheet.write_string(7, 0, '"&<>')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_A1(self):
+ """Test formulas with A1 notation."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet('5&4')
+
+ worksheet.write_formula('A1', '=IF(1>2,0,1)', None, 1)
+ worksheet.write_formula('A2', """=CONCATENATE("'","<>&")""", None, "'<>&")
+ worksheet.write_formula('A3', '=1&"b"', None, '1b')
+ worksheet.write_formula('A4', """="'\"""", None, "'")
+ worksheet.write_formula('A5', '=""""', None, '"')
+ worksheet.write_formula('A6', '="&" & "&"', None, '&&')
+
+ worksheet.write_string(7, 0, '"&<>')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_escapes02.py b/xlsxwriter/test/comparison/test_escapes02.py
new file mode 100644
index 0000000..8ac2f62
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_escapes02.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'escapes02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with comments.Check encoding of comments."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', '"<>\'&')
+ worksheet.write_comment('B2', """<>&"'""")
+
+ worksheet.set_comments_author("""I am '"<>&""")
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_escapes03.py b/xlsxwriter/test/comparison/test_escapes03.py
new file mode 100644
index 0000000..094ad24
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_escapes03.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'escapes03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file.Check encoding of rich strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+ worksheet.write_rich_string('A3', 'a', bold, """b"<>'c""", 'defg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_escapes04.py b/xlsxwriter/test/comparison/test_escapes04.py
new file mode 100644
index 0000000..dc3cba1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_escapes04.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'escapes04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file.Check encoding of url strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'http://www.perl.com/?a=1&b=2')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_escapes05.py b/xlsxwriter/test/comparison/test_escapes05.py
new file mode 100644
index 0000000..a75346a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_escapes05.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'escapes05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file. Check encoding of url strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet1 = workbook.add_worksheet("Start")
+ worksheet2 = workbook.add_worksheet("A & B")
+
+ worksheet1.write_url('A1', "internal:'A & B'!A1", None, 'Jump to A & B')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_escapes06.py b/xlsxwriter/test/comparison/test_escapes06.py
new file mode 100644
index 0000000..affbc67
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_escapes06.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'escapes06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file a num format thatrequire XML escaping."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ num_format = workbook.add_format({'num_format': '[Red]0.0%\\ "a"'})
+
+ worksheet.set_column('A:A', 14)
+
+ worksheet.write('A1', 123, num_format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_escapes07.py b/xlsxwriter/test/comparison/test_escapes07.py
new file mode 100644
index 0000000..8739c5d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_escapes07.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'escapes07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file.Check encoding of url strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', """http://example.com/!"$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~""")
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_escapes08.py b/xlsxwriter/test/comparison/test_escapes08.py
new file mode 100644
index 0000000..7810b07
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_escapes08.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'escapes08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file. Check encoding of url strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'http://example.com/%5b0%5d', None, 'http://example.com/[0]')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_excel2003_style01.py b/xlsxwriter/test/comparison/test_excel2003_style01.py
new file mode 100644
index 0000000..4b1bf5f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_excel2003_style01.py
@@ -0,0 +1,38 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'excel2003_style01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'excel2003_style': True})
+ workbook.add_worksheet()
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_excel2003_style02.py b/xlsxwriter/test/comparison/test_excel2003_style02.py
new file mode 100644
index 0000000..41dda85
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_excel2003_style02.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'excel2003_style02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'excel2003_style': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_paper(9)
+
+ bold = workbook.add_format({'bold': 1})
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write('A2', 'Bar', bold)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_excel2003_style03.py b/xlsxwriter/test/comparison/test_excel2003_style03.py
new file mode 100644
index 0000000..756a374
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_excel2003_style03.py
@@ -0,0 +1,51 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'excel2003_style03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'excel2003_style': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_paper(9)
+
+ worksheet.set_header('Page &P')
+ worksheet.set_footer('&A')
+
+ bold = workbook.add_format({'bold': 1})
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write('A2', 'Bar', bold)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_excel2003_style04.py b/xlsxwriter/test/comparison/test_excel2003_style04.py
new file mode 100644
index 0000000..db14e8f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_excel2003_style04.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'excel2003_style04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'excel2003_style': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Foo')
+ worksheet.set_row(0, 21)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_excel2003_style05.py b/xlsxwriter/test/comparison/test_excel2003_style05.py
new file mode 100644
index 0000000..9f33241
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_excel2003_style05.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'excel2003_style05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/drawings/drawing1.xml':
+ ['<xdr:cNvPr', '<a:picLocks',
+ '<a:srcRect/>', '<xdr:spPr',
+ '<a:noFill/>']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'excel2003_style': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('B3', self.image_dir + 'red.jpg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_excel2003_style06.py b/xlsxwriter/test/comparison/test_excel2003_style06.py
new file mode 100644
index 0000000..7a6f717
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_excel2003_style06.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'excel2003_style06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/drawings/drawing1.xml':
+ ['<xdr:cNvPr', '<a:picLocks',
+ '<a:srcRect/>', '<xdr:spPr',
+ '<a:noFill/>']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'excel2003_style': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('B3', self.image_dir + 'red.jpg',
+ {'x_offset': 4, 'y_offset': 3})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_excel2003_style07.py b/xlsxwriter/test/comparison/test_excel2003_style07.py
new file mode 100644
index 0000000..ed1560b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_excel2003_style07.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'excel2003_style07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/drawings/drawing1.xml':
+ ['<xdr:cNvPr', '<a:picLocks',
+ '<a:srcRect/>', '<xdr:spPr',
+ '<a:noFill/>']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'excel2003_style': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('B3', self.image_dir + 'yellow.jpg',
+ {'x_offset': 4, 'y_offset': 3})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_excel2003_style08.py b/xlsxwriter/test/comparison/test_excel2003_style08.py
new file mode 100644
index 0000000..f2d356a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_excel2003_style08.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'excel2003_style08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'excel2003_style': True})
+
+ worksheet = workbook.add_worksheet()
+
+ courier = workbook.add_format({'font_name': 'Courier',
+ 'font_size': 8,
+ 'font_family': 3})
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write('A2', 'Bar', courier)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_firstsheet01.py b/xlsxwriter/test/comparison/test_firstsheet01.py
new file mode 100644
index 0000000..324b8ad
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_firstsheet01.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'firstsheet01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet()
+ worksheet4 = workbook.add_worksheet()
+ worksheet5 = workbook.add_worksheet()
+ worksheet6 = workbook.add_worksheet()
+ worksheet7 = workbook.add_worksheet()
+ worksheet8 = workbook.add_worksheet()
+ worksheet9 = workbook.add_worksheet()
+ worksheet10 = workbook.add_worksheet()
+ worksheet11 = workbook.add_worksheet()
+ worksheet12 = workbook.add_worksheet()
+ worksheet13 = workbook.add_worksheet()
+ worksheet14 = workbook.add_worksheet()
+ worksheet15 = workbook.add_worksheet()
+ worksheet16 = workbook.add_worksheet()
+ worksheet17 = workbook.add_worksheet()
+ worksheet18 = workbook.add_worksheet()
+ worksheet19 = workbook.add_worksheet()
+ worksheet20 = workbook.add_worksheet()
+
+ worksheet8.set_first_sheet()
+ worksheet20.activate()
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_fit_to_pages01.py b/xlsxwriter/test/comparison/test_fit_to_pages01.py
new file mode 100644
index 0000000..3774480
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_fit_to_pages01.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'fit_to_pages01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with fit to print."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.fit_to_pages(1, 1)
+ worksheet.set_paper(9)
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_fit_to_pages02.py b/xlsxwriter/test/comparison/test_fit_to_pages02.py
new file mode 100644
index 0000000..d2b03ef
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_fit_to_pages02.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'fit_to_pages02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with fit to print."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.fit_to_pages(2, 1)
+ worksheet.set_paper(9)
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_fit_to_pages03.py b/xlsxwriter/test/comparison/test_fit_to_pages03.py
new file mode 100644
index 0000000..da165d3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_fit_to_pages03.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'fit_to_pages03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with fit to print."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.fit_to_pages(1, 2)
+ worksheet.set_paper(9)
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_fit_to_pages04.py b/xlsxwriter/test/comparison/test_fit_to_pages04.py
new file mode 100644
index 0000000..1538fbb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_fit_to_pages04.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'fit_to_pages04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with fit to print."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.fit_to_pages(3, 2)
+ worksheet.set_paper(9)
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_fit_to_pages05.py b/xlsxwriter/test/comparison/test_fit_to_pages05.py
new file mode 100644
index 0000000..9c1a006
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_fit_to_pages05.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'fit_to_pages05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with fit to print."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.fit_to_pages(1, 0)
+ worksheet.set_paper(9)
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_format01.py b/xlsxwriter/test/comparison/test_format01.py
new file mode 100644
index 0000000..e7ed1b3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_format01.py
@@ -0,0 +1,53 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'format01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with unused formats."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet('Data Sheet')
+ worksheet3 = workbook.add_worksheet()
+
+ unused1 = workbook.add_format({'bold': 1})
+ bold = workbook.add_format({'bold': 1})
+ unused2 = workbook.add_format({'bold': 1})
+ unused3 = workbook.add_format({'italic': 1})
+
+ worksheet1.write('A1', 'Foo')
+ worksheet1.write('A2', 123)
+
+ worksheet3.write('B2', 'Foo')
+ worksheet3.write('B3', 'Bar', bold)
+ worksheet3.write('C4', 234)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_format11.py b/xlsxwriter/test/comparison/test_format11.py
new file mode 100644
index 0000000..dfca760
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_format11.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'format11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test a vertical and horizontal centered format."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ centered = workbook.add_format({
+ 'align': 'center',
+ 'valign': 'vcenter'
+ })
+
+ worksheet.write('B2', 'Foo', centered)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_format12.py b/xlsxwriter/test/comparison/test_format12.py
new file mode 100644
index 0000000..0ae8617
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_format12.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'format12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test a vertical and horizontal centered format."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ top_left_bottom = workbook.add_format({
+ 'left': 1,
+ 'top': 1,
+ 'bottom': 1,
+ })
+
+ top_bottom = workbook.add_format({
+ 'top': 1,
+ 'bottom': 1,
+ })
+
+ top_left = workbook.add_format({
+ 'left': 1,
+ 'top': 1,
+ })
+
+ worksheet.write('B2', 'test', top_left_bottom)
+ worksheet.write('D2', 'test', top_left)
+ worksheet.write('F2', 'test', top_bottom)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_format13.py b/xlsxwriter/test/comparison/test_format13.py
new file mode 100644
index 0000000..b0fcb6c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_format13.py
@@ -0,0 +1,49 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'format13.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_row(0, 21)
+
+ font_format = workbook.add_format()
+
+ font_format.set_font('B Nazanin')
+ font_format.set_font_family(0)
+ font_format.set_font_charset(178)
+
+ worksheet.write('A1', 'Foo', font_format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_format14.py b/xlsxwriter/test/comparison/test_format14.py
new file mode 100644
index 0000000..30edcf0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_format14.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'format14.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the center across format."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ center = workbook.add_format()
+
+ center.set_center_across()
+
+ worksheet.write('A1', 'foo', center)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_2(self):
+ """Test the center across format."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ center = workbook.add_format({"center_across": True})
+
+ worksheet.write('A1', 'foo', center)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_formula_results01.py b/xlsxwriter/test/comparison/test_formula_results01.py
new file mode 100644
index 0000000..5cc5b2e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_formula_results01.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'formula_results01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml',
+ '[Content_Types].xml',
+ 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with formula errors."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_formula('A1', '1+1', None, 2)
+ worksheet.write_formula('A2', '"Foo"', None, 'Foo')
+ worksheet.write_formula('A3', 'IF(B3,FALSE,TRUE)', None, True)
+ worksheet.write_formula('A4', 'IF(B4,TRUE,FALSE)', None, False)
+ worksheet.write_formula('A5', '#DIV/0!', None, '#DIV/0!')
+ worksheet.write_formula('A6', '#N/A', None, '#N/A')
+ worksheet.write_formula('A7', '#NAME?', None, '#NAME?')
+ worksheet.write_formula('A8', '#NULL!', None, '#NULL!')
+ worksheet.write_formula('A9', '#NUM!', None, '#NUM!')
+ worksheet.write_formula('A10', '#REF!', None, '#REF!')
+ worksheet.write_formula('A11', '#VALUE!', None, '#VALUE!')
+ worksheet.write_formula('A12', '1/0', None, '#DIV/0!')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header01.py b/xlsxwriter/test/comparison/test_header01.py
new file mode 100644
index 0000000..9e71f9f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header01.py
@@ -0,0 +1,41 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_header('&L&P', {'scale_with_doc': False})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header02.py b/xlsxwriter/test/comparison/test_header02.py
new file mode 100644
index 0000000..d5b1588
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header02.py
@@ -0,0 +1,41 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_header('&L&P', {'align_with_margins': False})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header03.py b/xlsxwriter/test/comparison/test_header03.py
new file mode 100644
index 0000000..80f0aeb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header03.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_footer('&L&P', {'scale_with_doc': False,
+ 'align_with_margins': False})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image01.py b/xlsxwriter/test/comparison/test_header_image01.py
new file mode 100644
index 0000000..609aa5f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image01.py
@@ -0,0 +1,96 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+from ...compatibility import BytesIO
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_header('&L&G',
+ {'image_left': self.image_dir + 'red.jpg'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_header('&L&G',
+ {'image_left': self.image_dir + 'red.jpg'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_from_bytesio(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ image_file = open(self.image_dir + 'red.jpg', 'rb')
+ image_data = BytesIO(image_file.read())
+ image_file.close()
+
+ worksheet.set_header('&L&G',
+ {'image_left': 'red.jpg',
+ 'image_data_left': image_data})
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_from_bytesio_in_memory(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet = workbook.add_worksheet()
+
+ image_file = open(self.image_dir + 'red.jpg', 'rb')
+ image_data = BytesIO(image_file.read())
+ image_file.close()
+
+ worksheet.set_header('&L&G',
+ {'image_left': 'red.jpg',
+ 'image_data_left': image_data})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image02.py b/xlsxwriter/test/comparison/test_header_image02.py
new file mode 100644
index 0000000..de83b0c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image02.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_header('&L&G&C&G',
+ {'image_left': self.image_dir + 'red.jpg',
+ 'image_center': self.image_dir + 'blue.jpg'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image03.py b/xlsxwriter/test/comparison/test_header_image03.py
new file mode 100644
index 0000000..0a7d913
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image03.py
@@ -0,0 +1,94 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+from ...compatibility import BytesIO
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_header('&L&G&C&G&R&G',
+ {'image_left': self.image_dir + 'red.jpg',
+ 'image_center': self.image_dir + 'blue.jpg',
+ 'image_right': self.image_dir + 'yellow.jpg'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_with_picture(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_header('&L&[Picture]&C&G&R&[Picture]',
+ {'image_left': self.image_dir + 'red.jpg',
+ 'image_center': self.image_dir + 'blue.jpg',
+ 'image_right': self.image_dir + 'yellow.jpg'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_from_bytesio(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ image_file_left = open(self.image_dir + 'red.jpg', 'rb')
+ image_data_left = BytesIO(image_file_left.read())
+ image_file_left.close()
+
+ image_file_center = open(self.image_dir + 'blue.jpg', 'rb')
+ image_data_center = BytesIO(image_file_center.read())
+ image_file_center.close()
+
+ image_file_right = open(self.image_dir + 'yellow.jpg', 'rb')
+ image_data_right = BytesIO(image_file_right.read())
+ image_file_right.close()
+
+ worksheet.set_header('&L&G&C&G&R&G',
+ {'image_left': 'red.jpg',
+ 'image_center': 'blue.jpg',
+ 'image_right': 'yellow.jpg',
+ 'image_data_left': image_data_left,
+ 'image_data_center': image_data_center,
+ 'image_data_right': image_data_right,
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image04.py b/xlsxwriter/test/comparison/test_header_image04.py
new file mode 100644
index 0000000..d579f66
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image04.py
@@ -0,0 +1,94 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+from ...compatibility import BytesIO
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_footer('&L&G&C&G&R&G',
+ {'image_left': self.image_dir + 'red.jpg',
+ 'image_center': self.image_dir + 'blue.jpg',
+ 'image_right': self.image_dir + 'yellow.jpg'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_with_picture(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_footer('&L&[Picture]&C&G&R&[Picture]',
+ {'image_left': self.image_dir + 'red.jpg',
+ 'image_center': self.image_dir + 'blue.jpg',
+ 'image_right': self.image_dir + 'yellow.jpg'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_from_bytesio(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ image_file_left = open(self.image_dir + 'red.jpg', 'rb')
+ image_data_left = BytesIO(image_file_left.read())
+ image_file_left.close()
+
+ image_file_center = open(self.image_dir + 'blue.jpg', 'rb')
+ image_data_center = BytesIO(image_file_center.read())
+ image_file_center.close()
+
+ image_file_right = open(self.image_dir + 'yellow.jpg', 'rb')
+ image_data_right = BytesIO(image_file_right.read())
+ image_file_right.close()
+
+ worksheet.set_footer('&L&G&C&G&R&G',
+ {'image_left': 'red.jpg',
+ 'image_center': 'blue.jpg',
+ 'image_right': 'yellow.jpg',
+ 'image_data_left': image_data_left,
+ 'image_data_center': image_data_center,
+ 'image_data_right': image_data_right,
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image05.py b/xlsxwriter/test/comparison/test_header_image05.py
new file mode 100644
index 0000000..615ed24
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image05.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_header('&L&G',
+ {'image_left': self.image_dir + 'red.jpg'})
+
+ worksheet.set_footer('&L&G',
+ {'image_left': self.image_dir + 'blue.jpg'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image06.py b/xlsxwriter/test/comparison/test_header_image06.py
new file mode 100644
index 0000000..2321d13
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image06.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup'],
+ 'xl/worksheets/sheet2.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+
+ worksheet1.set_header('&L&G',
+ {'image_left': self.image_dir + 'red.jpg'})
+
+ worksheet2.set_header('&L&G',
+ {'image_left': self.image_dir + 'blue.jpg'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image07.py b/xlsxwriter/test/comparison/test_header_image07.py
new file mode 100644
index 0000000..7e159b8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image07.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('B3', self.image_dir + 'red.jpg')
+
+ worksheet.set_header('&L&G',
+ {'image_left': self.image_dir + 'blue.jpg'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image08.py b/xlsxwriter/test/comparison/test_header_image08.py
new file mode 100644
index 0000000..b2ab67e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image08.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write_comment('B2', 'Some text')
+
+ worksheet.set_comments_author('John')
+
+ worksheet.set_header('&L&G',
+ {'image_left': self.image_dir + 'red.jpg'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image09.py b/xlsxwriter/test/comparison/test_header_image09.py
new file mode 100644
index 0000000..a406298
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image09.py
@@ -0,0 +1,50 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup'],
+ 'xl/worksheets/sheet2.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+
+ worksheet1.write('A1', 'Foo')
+ worksheet1.write_comment('B2', 'Some text')
+
+ worksheet1.set_comments_author('John')
+
+ worksheet2.set_header('&L&G',
+ {'image_left': self.image_dir + 'red.jpg'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image10.py b/xlsxwriter/test/comparison/test_header_image10.py
new file mode 100644
index 0000000..e0d4fb1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image10.py
@@ -0,0 +1,50 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup'],
+ 'xl/worksheets/sheet2.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+
+ worksheet1.set_header('&L&G',
+ {'image_left': self.image_dir + 'red.jpg'})
+
+ worksheet2.write('A1', 'Foo')
+ worksheet2.write_comment('B2', 'Some text')
+
+ worksheet2.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image11.py b/xlsxwriter/test/comparison/test_header_image11.py
new file mode 100644
index 0000000..e1e4717
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image11.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.image_dir = test_dir + 'images/'
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_header('&L&G',
+ {'image_left': self.image_dir + 'black_300.jpg'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image12.py b/xlsxwriter/test/comparison/test_header_image12.py
new file mode 100644
index 0000000..b827893
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image12.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.image_dir = test_dir + 'images/'
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_header('&L&G',
+ {'image_left': self.image_dir + 'black_300e.png'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image13.py b/xlsxwriter/test/comparison/test_header_image13.py
new file mode 100644
index 0000000..6e94fa4
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image13.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image13.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_header('&L&G&C&G&R&G',
+ {'image_left': self.image_dir + 'black_72.jpg',
+ 'image_center': self.image_dir + 'black_150.jpg',
+ 'image_right': self.image_dir + 'black_300.jpg',
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_header_image14.py b/xlsxwriter/test/comparison/test_header_image14.py
new file mode 100644
index 0000000..417b553
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_header_image14.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'header_image14.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_header('&L&G&C&G&R&G',
+ {'image_left': self.image_dir + 'black_72e.png',
+ 'image_center': self.image_dir + 'black_150e.png',
+ 'image_right': self.image_dir + 'black_300e.png',
+ })
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hide01.py b/xlsxwriter/test/comparison/test_hide01.py
new file mode 100644
index 0000000..55626cd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hide01.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hide01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet()
+
+ worksheet2.hide()
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink01.py b/xlsxwriter/test/comparison/test_hyperlink01.py
new file mode 100644
index 0000000..ac970fd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink01.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks"""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'http://www.perl.org/')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_write(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks with write()"""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'http://www.perl.org/')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink02.py b/xlsxwriter/test/comparison/test_hyperlink02.py
new file mode 100644
index 0000000..6aa8121
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink02.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'http://www.perl.org/')
+ worksheet.write_url('D4', 'http://www.perl.org/')
+ worksheet.write_url('A8', 'http://www.perl.org/')
+ worksheet.write_url('B6', 'http://www.cpan.org/')
+ worksheet.write_url('F12', 'http://www.cpan.org/')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink03.py b/xlsxwriter/test/comparison/test_hyperlink03.py
new file mode 100644
index 0000000..901a180
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink03.py
@@ -0,0 +1,53 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+
+ worksheet1.write_url('A1', 'http://www.perl.org/')
+ worksheet1.write_url('D4', 'http://www.perl.org/')
+ worksheet1.write_url('A8', 'http://www.perl.org/')
+ worksheet1.write_url('B6', 'http://www.cpan.org/')
+ worksheet1.write_url('F12', 'http://www.cpan.org/')
+
+ worksheet2.write_url('C2', 'http://www.google.com/')
+ worksheet2.write_url('C5', 'http://www.cpan.org/')
+ worksheet2.write_url('C7', 'http://www.perl.org/')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink04.py b/xlsxwriter/test/comparison/test_hyperlink04.py
new file mode 100644
index 0000000..f402816
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink04.py
@@ -0,0 +1,76 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet('Data Sheet')
+
+ worksheet1.write_url('A1', "internal:Sheet2!A1")
+ worksheet1.write_url('A3', "internal:Sheet2!A1:A5")
+ worksheet1.write_url('A5', "internal:'Data Sheet'!D5", None, 'Some text')
+ worksheet1.write_url('E12', "internal:Sheet1!J1")
+ worksheet1.write_url('G17', "internal:Sheet2!A1", None, 'Some text')
+ worksheet1.write_url('A18', "internal:Sheet2!A1", None, None, 'Tool Tip 1')
+ worksheet1.write_url('A20', "internal:Sheet2!A1", None, 'More text', 'Tool Tip 2')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_write(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks with write()"""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet('Data Sheet')
+
+ worksheet1.write('A1', "internal:Sheet2!A1")
+ worksheet1.write('A3', "internal:Sheet2!A1:A5")
+ worksheet1.write('A5', "internal:'Data Sheet'!D5", None, 'Some text')
+ worksheet1.write('E12', "internal:Sheet1!J1")
+ worksheet1.write('G17', "internal:Sheet2!A1", None, 'Some text')
+ worksheet1.write('A18', "internal:Sheet2!A1", None, None, 'Tool Tip 1')
+ worksheet1.write('A20', "internal:Sheet2!A1", None, 'More text', 'Tool Tip 2')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink05.py b/xlsxwriter/test/comparison/test_hyperlink05.py
new file mode 100644
index 0000000..75e0270
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink05.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'http://www.perl.org/')
+ worksheet.write_url('A3', 'http://www.perl.org/', None, 'Perl home')
+ worksheet.write_url('A5', 'http://www.perl.org/', None, 'Perl home', 'Tool Tip')
+ worksheet.write_url('A7', 'http://www.cpan.org/', None, 'CPAN', 'Download')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink06.py b/xlsxwriter/test/comparison/test_hyperlink06.py
new file mode 100644
index 0000000..40c5847
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink06.py
@@ -0,0 +1,64 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', r'external:C:\Temp\foo.xlsx')
+ worksheet.write_url('A3', r'external:C:\Temp\foo.xlsx#Sheet1!A1')
+ worksheet.write_url('A5', r'external:C:\Temp\foo.xlsx#Sheet1!A1', None, 'External', 'Tip')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_write(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks with write()"""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', r'external:C:\Temp\foo.xlsx')
+ worksheet.write('A3', r'external:C:\Temp\foo.xlsx#Sheet1!A1')
+ worksheet.write('A5', r'external:C:\Temp\foo.xlsx#Sheet1!A1', None, 'External', 'Tip')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink07.py b/xlsxwriter/test/comparison/test_hyperlink07.py
new file mode 100644
index 0000000..caa1761
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink07.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', r'external:\\VBOXSVR\share\foo.xlsx', None, r'J:\foo.xlsx')
+ worksheet.write_url('A3', r'external:foo.xlsx')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink08.py b/xlsxwriter/test/comparison/test_hyperlink08.py
new file mode 100644
index 0000000..e5e5e71
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink08.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'external://VBOXSVR/share/foo.xlsx', None, 'J:/foo.xlsx')
+ worksheet.write_url('A3', 'external:foo.xlsx')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink09.py b/xlsxwriter/test/comparison/test_hyperlink09.py
new file mode 100644
index 0000000..6a874cf
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink09.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', r'external:..\foo.xlsx')
+ worksheet.write_url('A3', r'external:..\foo.xlsx#Sheet1!A1')
+ worksheet.write_url('A5', r'external:\\VBOXSVR\share\foo.xlsx#Sheet1!B2', None, r'J:\foo.xlsx#Sheet1!B2')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink10.py b/xlsxwriter/test/comparison/test_hyperlink10.py
new file mode 100644
index 0000000..bbb7302
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink10.py
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks. This example has link formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ format = workbook.add_format({'color': 'red', 'underline': 1})
+
+ worksheet.write_url('A1', 'http://www.perl.org/', format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_write(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks. This example has link formatting and uses write()"""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ format = workbook.add_format({'color': 'red', 'underline': 1})
+
+ worksheet.write('A1', 'http://www.perl.org/', format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink11.py b/xlsxwriter/test/comparison/test_hyperlink11.py
new file mode 100644
index 0000000..5051601
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink11.py
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_link_format_explicit(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks. This example has link formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ url_format = workbook.add_format({'color': 'blue', 'underline': 1})
+
+ worksheet.write_url('A1', 'http://www.perl.org/', url_format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_link_format_implicit(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks. This example has link formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'http://www.perl.org/')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_link_format_none(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks. This example has link formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'http://www.perl.org/', None)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink12.py b/xlsxwriter/test/comparison/test_hyperlink12.py
new file mode 100644
index 0000000..b7d780e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink12.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks. This example has link formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ format = workbook.add_format({'color': 'blue', 'underline': 1})
+
+ worksheet.write_url('A1', 'mailto:jmcnamara at cpan.org', format)
+
+ worksheet.write_url('A3', 'ftp://perl.org/', format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_write(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks. This example has link formatting and uses write()"""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ format = workbook.add_format({'color': 'blue', 'underline': 1})
+
+ worksheet.write('A1', 'mailto:jmcnamara at cpan.org', format)
+
+ worksheet.write('A3', 'ftp://perl.org/', format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink13.py b/xlsxwriter/test/comparison/test_hyperlink13.py
new file mode 100644
index 0000000..686354f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink13.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink13.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks. This example has writes a url in a range."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ format = workbook.add_format({'align': 'center'})
+
+ worksheet.merge_range('C4:E5', '', format)
+ worksheet.write_url('C4', 'http://www.perl.org/', format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink14.py b/xlsxwriter/test/comparison/test_hyperlink14.py
new file mode 100644
index 0000000..c2a2749
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink14.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink14.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks. This example has writes a url in a range."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ format = workbook.add_format({'align': 'center'})
+
+ worksheet.merge_range('C4:E5', '', format,)
+ worksheet.write_url('C4', 'http://www.perl.org/', format, 'Perl Home')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink15.py b/xlsxwriter/test/comparison/test_hyperlink15.py
new file mode 100644
index 0000000..5357be2
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink15.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink15.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks.This example doesn't have any link formatting and tests the relationshiplinkage code."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('B2', 'external:subdir/blank.xlsx')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink16.py b/xlsxwriter/test/comparison/test_hyperlink16.py
new file mode 100644
index 0000000..236c845
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink16.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink16.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks.This example doesn't have any link formatting and tests the relationshiplinkage code."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('B2', 'external:./subdir/blank.xlsx')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink17.py b/xlsxwriter/test/comparison/test_hyperlink17.py
new file mode 100644
index 0000000..f18a04f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink17.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink17.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks.This example doesn't have any link formatting and tests the relationshiplinkage code."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'http://google.com/some link')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink18.py b/xlsxwriter/test/comparison/test_hyperlink18.py
new file mode 100644
index 0000000..0fe89a7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink18.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink18.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks.This example doesn't have any link formatting and tests the relationshiplinkage code."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'http://google.com/00000000001111111111222222222233333333334444444444555555555566666666666777777777778888888888999999999990000000000111111111122222222223333333333444444444455555555556666666666677777777777888888888899999999999000000000011111111112222222222x')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink19.py b/xlsxwriter/test/comparison/test_hyperlink19.py
new file mode 100644
index 0000000..d0892ae
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink19.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+from ...sharedstrings import SharedStringTable
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink19.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml', '[Content_Types].xml', 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'http://www.perl.com/')
+
+ # Maintain the link but overwrite string with a formula.
+ worksheet.write_formula('A1', '=1+1', None, 2)
+
+ # Reset the SST for testing.
+ workbook.str_table = SharedStringTable()
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink20.py b/xlsxwriter/test/comparison/test_hyperlink20.py
new file mode 100644
index 0000000..59582a9
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink20.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink20.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_hyperlink_formating_explicit(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks. This example has link formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Simulate custom colour for testing.
+ workbook.custom_colors = ['FF0000FF']
+
+ worksheet = workbook.add_worksheet()
+ format1 = workbook.add_format({'color': 'blue', 'underline': 1})
+ format2 = workbook.add_format({'color': 'red', 'underline': 1})
+
+ worksheet.write_url('A1', 'http://www.python.org/1', format1)
+ worksheet.write_url('A2', 'http://www.python.org/2', format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_hyperlink_formating_implicit(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks. This example has link formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Simulate custom colour for testing.
+ workbook.custom_colors = ['FF0000FF']
+
+ worksheet = workbook.add_worksheet()
+ format1 = None
+ format2 = workbook.add_format({'color': 'red', 'underline': 1})
+
+ worksheet.write_url('A1', 'http://www.python.org/1', format1)
+ worksheet.write_url('A2', 'http://www.python.org/2', format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink21.py b/xlsxwriter/test/comparison/test_hyperlink21.py
new file mode 100644
index 0000000..c36de3c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink21.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink21.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'external:C:\Temp\Test 1')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink22.py b/xlsxwriter/test/comparison/test_hyperlink22.py
new file mode 100644
index 0000000..4a9ea4f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink22.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink22.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', r'external:\\Vboxsvr\share\foo bar.xlsx')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink23.py b/xlsxwriter/test/comparison/test_hyperlink23.py
new file mode 100644
index 0000000..6ec23c0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink23.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink23.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'https://en.wikipedia.org/wiki/Microsoft_Excel#Data_storage_and_communication', None, 'Display text')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink24.py b/xlsxwriter/test/comparison/test_hyperlink24.py
new file mode 100644
index 0000000..6cdf9c3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink24.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink24.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'http://www.example.com/some_long_url_that_is_255_characters_long_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_z#some_long_location_that_is_255_characters_long_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_a [...]
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink25.py b/xlsxwriter/test/comparison/test_hyperlink25.py
new file mode 100644
index 0000000..36e822c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink25.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink25.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'http://www.google.com/#foo#bar')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink26.py b/xlsxwriter/test/comparison/test_hyperlink26.py
new file mode 100644
index 0000000..93b9a0e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink26.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink26.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', 'http://www.google.com/foo#bar#baz')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_hyperlink27.py b/xlsxwriter/test/comparison/test_hyperlink27.py
new file mode 100644
index 0000000..f8dffd0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_hyperlink27.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'hyperlink27.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with hyperlinks."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_url('A1', r"external:\\Vboxsvr\share\foo bar.xlsx#'Some Sheet'!A1")
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image01.py b/xlsxwriter/test/comparison/test_image01.py
new file mode 100644
index 0000000..c96dac7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image01.py
@@ -0,0 +1,55 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('E9', self.image_dir + 'red.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('E9', self.image_dir + 'red.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image02.py b/xlsxwriter/test/comparison/test_image02.py
new file mode 100644
index 0000000..6163d23
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image02.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('D7', self.image_dir + 'yellow.png', {'x_offset': 1, 'y_offset': 2})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image03.py b/xlsxwriter/test/comparison/test_image03.py
new file mode 100644
index 0000000..e5bc0fe
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image03.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('E9', self.image_dir + 'red.jpg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image04.py b/xlsxwriter/test/comparison/test_image04.py
new file mode 100644
index 0000000..6693588
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image04.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('E9', self.image_dir + 'red.bmp')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image05.py b/xlsxwriter/test/comparison/test_image05.py
new file mode 100644
index 0000000..3b7eae1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image05.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('A1', self.image_dir + 'blue.png')
+ worksheet.insert_image('B3', self.image_dir + 'red.jpg')
+ worksheet.insert_image('D5', self.image_dir + 'yellow.jpg')
+ worksheet.insert_image('F9', self.image_dir + 'grey.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image07.py b/xlsxwriter/test/comparison/test_image07.py
new file mode 100644
index 0000000..9bf8580
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image07.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+
+ worksheet1.insert_image('E9', self.image_dir + 'red.png')
+ worksheet2.insert_image('E9', self.image_dir + 'yellow.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image08.py b/xlsxwriter/test/comparison/test_image08.py
new file mode 100644
index 0000000..66ba1ff
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image08.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('B3', self.image_dir + 'grey.png', {'x_scale': 0.5, 'y_scale': 0.5})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image09.py b/xlsxwriter/test/comparison/test_image09.py
new file mode 100644
index 0000000..745a9d8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image09.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('E9', self.image_dir + 'red_64x20.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image10.py b/xlsxwriter/test/comparison/test_image10.py
new file mode 100644
index 0000000..e872cca
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image10.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('C2', self.image_dir + 'logo.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image11.py b/xlsxwriter/test/comparison/test_image11.py
new file mode 100644
index 0000000..e2e17f4
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image11.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('C2', self.image_dir + 'logo.png', {'x_offset': 8, 'y_offset': 5})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image12.py b/xlsxwriter/test/comparison/test_image12.py
new file mode 100644
index 0000000..c2746c5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image12.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_row(1, 75)
+ worksheet.set_column('C:C', 32)
+
+ worksheet.insert_image('C2', self.image_dir + 'logo.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image13.py b/xlsxwriter/test/comparison/test_image13.py
new file mode 100644
index 0000000..bb40b76
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image13.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image13.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_row(1, 75)
+ worksheet.set_column('C:C', 32)
+
+ worksheet.insert_image('C2', self.image_dir + 'logo.png', {'x_offset': 8, 'y_offset': 5})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image14.py b/xlsxwriter/test/comparison/test_image14.py
new file mode 100644
index 0000000..a8a3999
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image14.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image14.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_row(1, 4.5)
+ worksheet.set_row(2, 35.25)
+ worksheet.set_column('C:E', 3.29)
+ worksheet.set_column('F:F', 10.71)
+
+ worksheet.insert_image('C2', self.image_dir + 'logo.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image15.py b/xlsxwriter/test/comparison/test_image15.py
new file mode 100644
index 0000000..4e084f6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image15.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image15.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_row(1, 4.5)
+ worksheet.set_row(2, 35.25)
+ worksheet.set_column('C:E', 3.29)
+ worksheet.set_column('F:F', 10.71)
+
+ worksheet.insert_image('C2', self.image_dir + 'logo.png', {'x_offset': 13, 'y_offset': 2})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image16.py b/xlsxwriter/test/comparison/test_image16.py
new file mode 100644
index 0000000..b72abdd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image16.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image16.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('C2', self.image_dir + 'issue32.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image17.py b/xlsxwriter/test/comparison/test_image17.py
new file mode 100644
index 0000000..9fdb258
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image17.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image17.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_row(1, 96)
+ worksheet.set_column('C:C', 18)
+
+ worksheet.insert_image('C2', self.image_dir + 'issue32.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image18.py b/xlsxwriter/test/comparison/test_image18.py
new file mode 100644
index 0000000..6de0d79
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image18.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image18.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_row(1, 96)
+ worksheet.set_column('C:C', 18)
+
+ worksheet.insert_image('C2', self.image_dir + 'issue32.png', {'x_offset': 5, 'y_offset': 5})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image19.py b/xlsxwriter/test/comparison/test_image19.py
new file mode 100644
index 0000000..1cf2e8a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image19.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image19.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('C2', self.image_dir + 'train.jpg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image20.py b/xlsxwriter/test/comparison/test_image20.py
new file mode 100644
index 0000000..9aa363c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image20.py
@@ -0,0 +1,55 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image20.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a simple XlsxWriter file with multiple images.
+ """
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ # Add second worksheet for internal link
+ workbook.add_worksheet()
+
+ # External link
+ options = {'url': 'https://www.github.com'}
+ worksheet.insert_image('C2', self.image_dir + 'train.jpg', options)
+
+ options = {'url': 'external:./subdir/blank.xlsx'}
+ worksheet.insert_image('C30', self.image_dir + 'train.jpg', options)
+
+ # Internal link
+ options = {'url': 'internal:Sheet2!A1'}
+ worksheet.insert_image('C60', self.image_dir + 'train.jpg', options)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image21.py b/xlsxwriter/test/comparison/test_image21.py
new file mode 100644
index 0000000..2cdb27b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image21.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image21.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write(2000, 0, "Here")
+ worksheet.insert_image(2000, 1, self.image_dir + 'logo.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image22.py b/xlsxwriter/test/comparison/test_image22.py
new file mode 100644
index 0000000..bc9b618
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image22.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image22.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('B2', self.image_dir + 'black_300.jpg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image23.py b/xlsxwriter/test/comparison/test_image23.py
new file mode 100644
index 0000000..862d5ce
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image23.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image23.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('B2', self.image_dir + 'black_72.jpg')
+ worksheet.insert_image('B8', self.image_dir + 'black_96.jpg')
+ worksheet.insert_image('B13', self.image_dir + 'black_150.jpg')
+ worksheet.insert_image('B17', self.image_dir + 'black_300.jpg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image24.py b/xlsxwriter/test/comparison/test_image24.py
new file mode 100644
index 0000000..00e0a60
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image24.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image24.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('B2', self.image_dir + 'black_300.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image25.py b/xlsxwriter/test/comparison/test_image25.py
new file mode 100644
index 0000000..ad061b0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image25.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image25.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('B2', self.image_dir + 'black_150.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image26.py b/xlsxwriter/test/comparison/test_image26.py
new file mode 100644
index 0000000..f79ed01
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image26.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image26.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('B2', self.image_dir + 'black_72.png')
+ worksheet.insert_image('B8', self.image_dir + 'black_96.png')
+ worksheet.insert_image('B13', self.image_dir + 'black_150.png')
+ worksheet.insert_image('B17', self.image_dir + 'black_300.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image27.py b/xlsxwriter/test/comparison/test_image27.py
new file mode 100644
index 0000000..4ae6f5a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image27.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image27.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('B2', self.image_dir + 'mylogo.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image28.py b/xlsxwriter/test/comparison/test_image28.py
new file mode 100644
index 0000000..83d03ca
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image28.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image28.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image(0, 6, self.image_dir + 'red_208.png', {'x_offset': 46, 'y_offset': 1})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image29.py b/xlsxwriter/test/comparison/test_image29.py
new file mode 100644
index 0000000..e786105
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image29.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image29.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image(0, 10, self.image_dir + 'red_208.png', {'x_offset': -210, 'y_offset': 1})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image30.py b/xlsxwriter/test/comparison/test_image30.py
new file mode 100644
index 0000000..d860f9a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image30.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image30.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('E9', self.image_dir + 'red.png', {'x_offset': -2, 'y_offset': -1})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image31.py b/xlsxwriter/test/comparison/test_image31.py
new file mode 100644
index 0000000..76538e6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image31.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image31.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('D:D', 3.86)
+ worksheet.set_row(7, 7.5)
+
+ worksheet.insert_image('E9', self.image_dir + 'red.png', {'x_offset': -2, 'y_offset': -1})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image32.py b/xlsxwriter/test/comparison/test_image32.py
new file mode 100644
index 0000000..8e36c10
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image32.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image32.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('B2', self.image_dir + 'red.png', {'x_offset': -100, 'y_offset': -100})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image33.py b/xlsxwriter/test/comparison/test_image33.py
new file mode 100644
index 0000000..731e671
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image33.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image33.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('D:D', 3.86)
+ worksheet.set_column('E:E', 1.43)
+ worksheet.set_row(7, 7.5)
+ worksheet.set_row(8, 9.75)
+
+ worksheet.insert_image('E9', self.image_dir + 'red.png', {'x_offset': -2, 'y_offset': -1})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image34.py b/xlsxwriter/test/comparison/test_image34.py
new file mode 100644
index 0000000..163ed1a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image34.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image34.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('E9', self.image_dir + 'red_readonly.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image35.py b/xlsxwriter/test/comparison/test_image35.py
new file mode 100644
index 0000000..228b475
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image35.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image35.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('E9', self.image_dir + 'zero_dpi.jpg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image_anchor01.py b/xlsxwriter/test/comparison/test_image_anchor01.py
new file mode 100644
index 0000000..8ee0132
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image_anchor01.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image_anchor01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image(
+ 'E9', self.image_dir + 'red.png', {'positioning': 3})
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image(
+ 'E9', self.image_dir + 'red.png', {'positioning': 3})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image_anchor02.py b/xlsxwriter/test/comparison/test_image_anchor02.py
new file mode 100644
index 0000000..9aecb81
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image_anchor02.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image_anchor02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image(
+ 'E9', self.image_dir + 'red.png', {'positioning': 2})
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image(
+ 'E9', self.image_dir + 'red.png', {'positioning': 2})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image_anchor03.py b/xlsxwriter/test/comparison/test_image_anchor03.py
new file mode 100644
index 0000000..42a5889
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image_anchor03.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image_anchor03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image(
+ 'E9', self.image_dir + 'red.png', {'positioning': 1})
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image(
+ 'E9', self.image_dir + 'red.png', {'positioning': 1})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image_anchor04.py b/xlsxwriter/test/comparison/test_image_anchor04.py
new file mode 100644
index 0000000..2c522cd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image_anchor04.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image_anchor04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image(
+ 'D7', self.image_dir + 'yellow.png',
+ {'x_offset': 1, 'y_offset': 2, 'positioning': 3})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image_anchor05.py b/xlsxwriter/test/comparison/test_image_anchor05.py
new file mode 100644
index 0000000..38174bc
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image_anchor05.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image_anchor05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image(
+ 'D7', self.image_dir + 'yellow.png',
+ {'x_offset': 1, 'y_offset': 2, 'positioning': 2})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image_anchor06.py b/xlsxwriter/test/comparison/test_image_anchor06.py
new file mode 100644
index 0000000..32d5e34
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image_anchor06.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image_anchor06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image(
+ 'D7', self.image_dir + 'yellow.png',
+ {'x_offset': 1, 'y_offset': 2, 'positioning': 1})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image_anchor07.py b/xlsxwriter/test/comparison/test_image_anchor07.py
new file mode 100644
index 0000000..d674f7a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image_anchor07.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image_anchor07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_image('A1', self.image_dir + 'blue.png')
+ worksheet.insert_image(
+ 'B3', self.image_dir + 'red.jpg', {'positioning': 3})
+ worksheet.insert_image(
+ 'D5', self.image_dir + 'yellow.jpg', {'positioning': 2})
+ worksheet.insert_image(
+ 'F9', self.image_dir + 'grey.png', {'positioning': 1})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image_bytes01.py b/xlsxwriter/test/comparison/test_image_bytes01.py
new file mode 100644
index 0000000..7f971d3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image_bytes01.py
@@ -0,0 +1,64 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+from ...compatibility import BytesIO
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_bytes_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ image_file = open(self.image_dir + 'red.png', 'rb')
+ image_data = BytesIO(image_file.read())
+ image_file.close()
+
+ worksheet.insert_image('E9', 'red.png', {'image_data': image_data})
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet = workbook.add_worksheet()
+
+ image_file = open(self.image_dir + 'red.png', 'rb')
+ image_data = BytesIO(image_file.read())
+ image_file.close()
+
+ worksheet.insert_image('E9', 'red.png', {'image_data': image_data})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image_bytes02.py b/xlsxwriter/test/comparison/test_image_bytes02.py
new file mode 100644
index 0000000..a3543b0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image_bytes02.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+from ...compatibility import BytesIO
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_bytes_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ image_file = open(self.image_dir + 'yellow.png', 'rb')
+ image_data = BytesIO(image_file.read())
+ image_file.close()
+
+ worksheet.insert_image('D7', 'yellow.png', {'x_offset': 1, 'y_offset': 2, 'image_data': image_data})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_image_bytes03.py b/xlsxwriter/test/comparison/test_image_bytes03.py
new file mode 100644
index 0000000..02af2bb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_image_bytes03.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+from ...compatibility import BytesIO
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'image03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_bytes_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with image(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ image_file = open(self.image_dir + 'red.jpg', 'rb')
+ image_data = BytesIO(image_file.read())
+ image_file.close()
+
+ worksheet.insert_image('E9', 'red.jpg', {'image_data': image_data})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_landscape01.py b/xlsxwriter/test/comparison/test_landscape01.py
new file mode 100644
index 0000000..919607b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_landscape01.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'landscape01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write(0, 0, 'Foo')
+ worksheet.set_landscape()
+ worksheet.set_paper(9)
+
+ worksheet.vertical_dpi = 200
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_macro01.py b/xlsxwriter/test/comparison/test_macro01.py
new file mode 100644
index 0000000..5c4cdf0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_macro01.py
@@ -0,0 +1,98 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+from ...compatibility import BytesIO
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'macro01.xlsm'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.vba_dir = test_dir + 'xlsx_files/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ workbook.add_vba_project(self.vba_dir + 'vbaProject01.bin')
+
+ worksheet.write('A1', 123)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet = workbook.add_worksheet()
+
+ workbook.add_vba_project(self.vba_dir + 'vbaProject01.bin')
+
+ worksheet.write('A1', 123)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_bytes_io(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ vba_file = open(self.vba_dir + 'vbaProject01.bin', 'rb')
+ vba_data = BytesIO(vba_file.read())
+ vba_file.close()
+
+ workbook.add_vba_project(vba_data, True)
+
+ worksheet.write('A1', 123)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_bytes_io_in_memory(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet = workbook.add_worksheet()
+
+ vba_file = open(self.vba_dir + 'vbaProject01.bin', 'rb')
+ vba_data = BytesIO(vba_file.read())
+ vba_file.close()
+
+ workbook.add_vba_project(vba_data, True)
+
+ worksheet.write('A1', 123)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_merge_cells01.py b/xlsxwriter/test/comparison/test_merge_cells01.py
new file mode 100644
index 0000000..ff9d1c4
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_merge_cells01.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'merge_cells01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ format = workbook.add_format({'align': 'center'})
+
+ worksheet.set_selection('A4')
+
+ worksheet.merge_range('A1:A2', 'col1', format)
+ worksheet.merge_range('B1:B2', 'col2', format)
+ worksheet.merge_range('C1:C2', 'col3', format)
+ worksheet.merge_range('D1:D2', 'col4', format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_merge_range01.py b/xlsxwriter/test/comparison/test_merge_range01.py
new file mode 100644
index 0000000..b1629f1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_merge_range01.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'merge_range01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format = workbook.add_format({'align': 'center'})
+
+ worksheet.merge_range(1, 1, 1, 3, 'Foo', format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_merge_range02.py b/xlsxwriter/test/comparison/test_merge_range02.py
new file mode 100644
index 0000000..a63bddb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_merge_range02.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'merge_range02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format = workbook.add_format({'align': 'center'})
+
+ worksheet.merge_range(1, 1, 5, 3, 'Foo', format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_merge_range03.py b/xlsxwriter/test/comparison/test_merge_range03.py
new file mode 100644
index 0000000..f6f8a1b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_merge_range03.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'merge_range03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format = workbook.add_format({'align': 'center'})
+
+ worksheet.merge_range(1, 1, 1, 2, 'Foo', format)
+ worksheet.merge_range(1, 3, 1, 4, 'Foo', format)
+ worksheet.merge_range(1, 5, 1, 6, 'Foo', format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_merge_range04.py b/xlsxwriter/test/comparison/test_merge_range04.py
new file mode 100644
index 0000000..f2361a1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_merge_range04.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'merge_range04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format = workbook.add_format({'align': 'center', 'bold': 1})
+
+ worksheet.merge_range(1, 1, 1, 3, 'Foo', format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_merge_range05.py b/xlsxwriter/test/comparison/test_merge_range05.py
new file mode 100644
index 0000000..4998ef6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_merge_range05.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'merge_range05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format = workbook.add_format({'align': 'center'})
+
+ worksheet.merge_range(1, 1, 1, 3, 123, format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_optimize01.py b/xlsxwriter/test/comparison/test_optimize01.py
new file mode 100644
index 0000000..3b96875
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_optimize01.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'optimize01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'constant_memory': True,
+ 'strings_to_numbers': True,
+ 'in_memory': False})
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Hello')
+ worksheet.write('A2', '123')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_optimize02.py b/xlsxwriter/test/comparison/test_optimize02.py
new file mode 100644
index 0000000..19a7bc3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_optimize02.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'optimize02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'constant_memory': True, 'in_memory': False})
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Hello')
+ worksheet.write('A2', 123)
+
+ worksheet.write('G1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_optimize03.py b/xlsxwriter/test/comparison/test_optimize03.py
new file mode 100644
index 0000000..79f29b4
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_optimize03.py
@@ -0,0 +1,91 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'optimize03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'constant_memory': True, 'in_memory': False})
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+
+ worksheet.set_column('A:A', 36, bold)
+ worksheet.set_column('B:B', 20)
+ worksheet.set_row(0, 40)
+
+ heading_format = workbook.add_format({
+ 'bold': 1,
+ 'font_color': 'blue',
+ 'font_size': 16,
+ 'align': 'centre_across',
+ 'valign': 'vcenter',
+ })
+
+ heading_format.text_h_align = 6
+
+ hyperlink_format = workbook.add_format({
+ 'font_color': 'blue',
+ 'underline': 1,
+ })
+
+ headings = ['Features of Excel::Writer::XLSX', '']
+ worksheet.write_row('A1', headings, heading_format)
+
+ text_format = workbook.add_format({
+ 'bold': 1,
+ 'italic': 1,
+ 'font_color': 'red',
+ 'font_size': 18,
+ 'font': 'Lucida Calligraphy'
+ })
+
+ worksheet.write('A2', "Text")
+ worksheet.write('B2', "Hello Excel")
+ worksheet.write('A3', "Formatted text")
+ worksheet.write('B3', "Hello Excel", text_format)
+
+ num1_format = workbook.add_format({'num_format': '$#,##0.00'})
+ num2_format = workbook.add_format({'num_format': ' d mmmm yyy'})
+
+ worksheet.write('A5', "Numbers")
+ worksheet.write('B5', 1234.56)
+ worksheet.write('A6', "Formatted numbers")
+ worksheet.write('B6', 1234.56, num1_format)
+ worksheet.write('A7', "Formatted numbers")
+ worksheet.write('B7', 37257, num2_format)
+
+ worksheet.write('A8', 'Formulas and functions, "=SIN(PI()/4)"')
+ worksheet.write('B8', '=SIN(PI()/4)')
+
+ worksheet.write('A9', "Hyperlinks")
+ worksheet.write('B9', 'http://www.perl.com/', hyperlink_format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_optimize04.py b/xlsxwriter/test/comparison/test_optimize04.py
new file mode 100644
index 0000000..81575e8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_optimize04.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'optimize04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'constant_memory': True, 'in_memory': False})
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+ worksheet.write_rich_string('A3', 'a', bold, 'bc', 'defg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_optimize05.py b/xlsxwriter/test/comparison/test_optimize05.py
new file mode 100644
index 0000000..53ae6dc
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_optimize05.py
@@ -0,0 +1,50 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'optimize05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'constant_memory': True, 'in_memory': False})
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+ worksheet.write_rich_string('A3', 'a', bold, 'bc', 'defg')
+ worksheet.write_rich_string('B4', 'abc', italic, 'de', 'fg')
+ worksheet.write_rich_string('C5', 'a', bold, 'bc', 'defg')
+ worksheet.write_rich_string('D6', 'abc', italic, 'de', 'fg')
+ worksheet.write_rich_string('E7', 'a', bold, 'bcdef', 'g')
+ worksheet.write_rich_string('F8', italic, 'abcd', 'efg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_optimize06.py b/xlsxwriter/test/comparison/test_optimize06.py
new file mode 100644
index 0000000..67b7182
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_optimize06.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'optimize06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'constant_memory': True, 'in_memory': False})
+ worksheet = workbook.add_worksheet()
+
+ # Test that control characters and any other single byte characters are
+ # handled correctly by the SharedStrings module. We skip chr 34 = " in
+ # this test since it isn't encoded by Excel as ".
+ ordinals = list(range(0, 34))
+ ordinals.extend(range(35, 128))
+
+ for i in ordinals:
+ worksheet.write_string(i, 0, chr(i))
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_optimize07.py b/xlsxwriter/test/comparison/test_optimize07.py
new file mode 100644
index 0000000..77465ba
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_optimize07.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'optimize07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'constant_memory': True, 'in_memory': False})
+ worksheet = workbook.add_worksheet()
+
+ strings = [
+ "_",
+ "_x",
+ "_x0",
+ "_x00",
+ "_x000",
+ "_x0000",
+ "_x0000_",
+ "_x005F_",
+ "_x000G_",
+ "_X0000_",
+ "_x000a_",
+ "_x000A_",
+ "_x0000__x0000_",
+ "__x0000__",
+ ]
+
+ worksheet.write_column(0, 0, strings)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_optimize08.py b/xlsxwriter/test/comparison/test_optimize08.py
new file mode 100644
index 0000000..fda0204
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_optimize08.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'optimize08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'constant_memory': True, 'in_memory': False})
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+
+ worksheet.write_rich_string('A3', ' a', bold, 'bc', 'defg ')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_optimize09.py b/xlsxwriter/test/comparison/test_optimize09.py
new file mode 100644
index 0000000..55de2dc
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_optimize09.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+import sys
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'optimize09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'constant_memory': True, 'in_memory': False})
+ worksheet = workbook.add_worksheet()
+
+ smiley = "\u263a"
+
+ if sys.version_info[0] == 2:
+ smiley = unichr(9786)
+
+ worksheet.write('A1', smiley)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_optimize10.py b/xlsxwriter/test/comparison/test_optimize10.py
new file mode 100644
index 0000000..1f98a5b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_optimize10.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+import codecs
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'optimize10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+ self.txt_filename = test_dir + 'xlsx_files/' + 'unicode_polish_utf8.txt'
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test example file converting Unicode text."""
+
+ # Open the input file with the correct encoding.
+ textfile = codecs.open(self.txt_filename, 'r', 'utf-8')
+
+ # Create an new Excel file and convert the text data.
+ workbook = Workbook(self.got_filename, {'constant_memory': True, 'in_memory': False})
+ worksheet = workbook.add_worksheet()
+
+ # Widen the first column to make the text clearer.
+ worksheet.set_column('A:A', 50)
+
+ # Start from the first cell.
+ row = 0
+ col = 0
+
+ # Read the text file and write it to the worksheet.
+ for line in textfile:
+ # Ignore the comments in the sample file.
+ if line.startswith('#'):
+ continue
+
+ # Write any other lines to the worksheet.
+ worksheet.write(row, col, line.rstrip("\n"))
+ row += 1
+
+ workbook.close()
+ textfile.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_optimize11.py b/xlsxwriter/test/comparison/test_optimize11.py
new file mode 100644
index 0000000..5138136
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_optimize11.py
@@ -0,0 +1,81 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'optimize11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file_no_close(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'constant_memory': True, 'in_memory': False})
+
+ for i in range(1, 10):
+ worksheet = workbook.add_worksheet()
+ worksheet.write('A1', 'Hello 1')
+ worksheet.write('A2', 'Hello 2')
+ worksheet.write('A4', 'Hello 3')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_with_close(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'constant_memory': True, 'in_memory': False})
+
+ for i in range(1, 10):
+ worksheet = workbook.add_worksheet()
+ worksheet.write('A1', 'Hello 1')
+ worksheet.write('A2', 'Hello 2')
+ worksheet.write('A4', 'Hello 3')
+ worksheet._opt_close()
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_with_reopen(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename, {'constant_memory': True, 'in_memory': False})
+
+ for i in range(1, 10):
+ worksheet = workbook.add_worksheet()
+ worksheet.write('A1', 'Hello 1')
+ worksheet._opt_close()
+ worksheet._opt_reopen()
+ worksheet.write('A2', 'Hello 2')
+ worksheet._opt_close()
+ worksheet._opt_reopen()
+ worksheet.write('A4', 'Hello 3')
+ worksheet._opt_close()
+ worksheet._opt_reopen()
+ worksheet._opt_close()
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_outline01.py b/xlsxwriter/test/comparison/test_outline01.py
new file mode 100644
index 0000000..be3f9f3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_outline01.py
@@ -0,0 +1,87 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'outline01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml', '[Content_Types].xml', 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a outlines in a XlsxWriter file. These tests are
+ based on the outline programs in the examples directory.
+ """
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet('Outlined Rows')
+
+ bold = workbook.add_format({'bold': 1})
+
+ worksheet1.set_row(1, None, None, {'level': 2})
+ worksheet1.set_row(2, None, None, {'level': 2})
+ worksheet1.set_row(3, None, None, {'level': 2})
+ worksheet1.set_row(4, None, None, {'level': 2})
+ worksheet1.set_row(5, None, None, {'level': 1})
+
+ worksheet1.set_row(6, None, None, {'level': 2})
+ worksheet1.set_row(7, None, None, {'level': 2})
+ worksheet1.set_row(8, None, None, {'level': 2})
+ worksheet1.set_row(9, None, None, {'level': 2})
+ worksheet1.set_row(10, None, None, {'level': 1})
+
+ worksheet1.set_column('A:A', 20)
+
+ worksheet1.write('A1', 'Region', bold)
+ worksheet1.write('A2', 'North')
+ worksheet1.write('A3', 'North')
+ worksheet1.write('A4', 'North')
+ worksheet1.write('A5', 'North')
+ worksheet1.write('A6', 'North Total', bold)
+
+ worksheet1.write('B1', 'Sales', bold)
+ worksheet1.write('B2', 1000)
+ worksheet1.write('B3', 1200)
+ worksheet1.write('B4', 900)
+ worksheet1.write('B5', 1200)
+ worksheet1.write('B6', '=SUBTOTAL(9,B2:B5)', bold, 4300)
+
+ worksheet1.write('A7', 'South')
+ worksheet1.write('A8', 'South')
+ worksheet1.write('A9', 'South')
+ worksheet1.write('A10', 'South')
+ worksheet1.write('A11', 'South Total', bold)
+
+ worksheet1.write('B7', 400)
+ worksheet1.write('B8', 600)
+ worksheet1.write('B9', 500)
+ worksheet1.write('B10', 600)
+ worksheet1.write('B11', '=SUBTOTAL(9,B7:B10)', bold, 2100)
+
+ worksheet1.write('A12', 'Grand Total', bold)
+ worksheet1.write('B12', '=SUBTOTAL(9,B2:B10)', bold, 6400)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_outline02.py b/xlsxwriter/test/comparison/test_outline02.py
new file mode 100644
index 0000000..d5243b5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_outline02.py
@@ -0,0 +1,90 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'outline02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml', '[Content_Types].xml', 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a outlines in a XlsxWriter file. These tests are
+ based on the outline programs in the examples directory.
+ """
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet2 = workbook.add_worksheet('Collapsed Rows')
+
+ bold = workbook.add_format({'bold': 1})
+
+ worksheet2.set_row(1, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(2, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(3, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(4, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(5, None, None, {'level': 1, 'hidden': True})
+
+ worksheet2.set_row(6, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(7, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(8, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(9, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(10, None, None, {'level': 1, 'hidden': True})
+ worksheet2.set_row(11, None, None, {'collapsed': True})
+
+ worksheet2.set_column('A:A', 20)
+
+ worksheet2.set_selection('A14')
+
+ worksheet2.write('A1', 'Region', bold)
+ worksheet2.write('A2', 'North')
+ worksheet2.write('A3', 'North')
+ worksheet2.write('A4', 'North')
+ worksheet2.write('A5', 'North')
+ worksheet2.write('A6', 'North Total', bold)
+
+ worksheet2.write('B1', 'Sales', bold)
+ worksheet2.write('B2', 1000)
+ worksheet2.write('B3', 1200)
+ worksheet2.write('B4', 900)
+ worksheet2.write('B5', 1200)
+ worksheet2.write('B6', '=SUBTOTAL(9,B2:B5)', bold, 4300)
+
+ worksheet2.write('A7', 'South')
+ worksheet2.write('A8', 'South')
+ worksheet2.write('A9', 'South')
+ worksheet2.write('A10', 'South')
+ worksheet2.write('A11', 'South Total', bold)
+
+ worksheet2.write('B7', 400)
+ worksheet2.write('B8', 600)
+ worksheet2.write('B9', 500)
+ worksheet2.write('B10', 600)
+ worksheet2.write('B11', '=SUBTOTAL(9,B7:B10)', bold, 2100)
+
+ worksheet2.write('A12', 'Grand Total', bold)
+ worksheet2.write('B12', '=SUBTOTAL(9,B2:B10)', bold, 6400)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_outline03.py b/xlsxwriter/test/comparison/test_outline03.py
new file mode 100644
index 0000000..802a09f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_outline03.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'outline03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml', '[Content_Types].xml', 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a outlines in a XlsxWriter file. These tests are
+ based on the outline programs in the examples directory.
+ """
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet3 = workbook.add_worksheet('Outline Columns')
+
+ bold = workbook.add_format({'bold': 1})
+
+ data = [
+ ['Month', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Total'],
+ ['North', 50, 20, 15, 25, 65, 80],
+ ['South', 10, 20, 30, 50, 50, 50],
+ ['East', 45, 75, 50, 15, 75, 100],
+ ['West', 15, 15, 55, 35, 20, 50]]
+
+ worksheet3.set_row(0, None, bold)
+
+ worksheet3.set_column('A:A', 10, bold)
+ worksheet3.set_column('B:G', 6, None, {'level': 1})
+ worksheet3.set_column('H:H', 10)
+
+ for row, data_row in enumerate(data):
+ worksheet3.write_row(row, 0, data_row)
+
+ worksheet3.write('H2', '=SUM(B2:G2)', None, 255)
+ worksheet3.write('H3', '=SUM(B3:G3)', None, 210)
+ worksheet3.write('H4', '=SUM(B4:G4)', None, 360)
+ worksheet3.write('H5', '=SUM(B5:G5)', None, 190)
+ worksheet3.write('H6', '=SUM(H2:H5)', bold, 1015)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_outline04.py b/xlsxwriter/test/comparison/test_outline04.py
new file mode 100644
index 0000000..80f12e5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_outline04.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'outline04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml', '[Content_Types].xml', 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a outlines in a XlsxWriter file. These tests are
+ based on the outline programs in the examples directory.
+ """
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet4 = workbook.add_worksheet('Outline levels')
+
+ levels = [
+ "Level 1", "Level 2", "Level 3", "Level 4", "Level 5", "Level 6",
+ "Level 7", "Level 6", "Level 5", "Level 4", "Level 3", "Level 2",
+ "Level 1"]
+
+ worksheet4.write_column('A1', levels)
+
+ worksheet4.set_row(0, None, None, {'level': 1})
+ worksheet4.set_row(1, None, None, {'level': 2})
+ worksheet4.set_row(2, None, None, {'level': 3})
+ worksheet4.set_row(3, None, None, {'level': 4})
+ worksheet4.set_row(4, None, None, {'level': 5})
+ worksheet4.set_row(5, None, None, {'level': 6})
+ worksheet4.set_row(6, None, None, {'level': 7})
+ worksheet4.set_row(7, None, None, {'level': 6})
+ worksheet4.set_row(8, None, None, {'level': 5})
+ worksheet4.set_row(9, None, None, {'level': 4})
+ worksheet4.set_row(10, None, None, {'level': 3})
+ worksheet4.set_row(11, None, None, {'level': 2})
+ worksheet4.set_row(12, None, None, {'level': 1})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_outline05.py b/xlsxwriter/test/comparison/test_outline05.py
new file mode 100644
index 0000000..abf6c41
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_outline05.py
@@ -0,0 +1,90 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'outline05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml', '[Content_Types].xml', 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a outlines in a XlsxWriter file. These tests are
+ based on the outline programs in the examples directory.
+ """
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet2 = workbook.add_worksheet('Collapsed Rows')
+
+ bold = workbook.add_format({'bold': 1})
+
+ worksheet2.set_row(1, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(2, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(3, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(4, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(5, None, None, {'level': 1, 'hidden': True, 'collapsed': True})
+
+ worksheet2.set_row(6, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(7, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(8, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(9, None, None, {'level': 2, 'hidden': True})
+ worksheet2.set_row(10, None, None, {'level': 1, 'hidden': True, 'collapsed': True})
+
+ worksheet2.set_row(11, None, None, {'collapsed': True})
+
+ worksheet2.set_column('A:A', 20)
+ worksheet2.set_selection('A14')
+
+ worksheet2.write('A1', 'Region', bold)
+ worksheet2.write('A2', 'North')
+ worksheet2.write('A3', 'North')
+ worksheet2.write('A4', 'North')
+ worksheet2.write('A5', 'North')
+ worksheet2.write('A6', 'North Total', bold)
+
+ worksheet2.write('B1', 'Sales', bold)
+ worksheet2.write('B2', 1000)
+ worksheet2.write('B3', 1200)
+ worksheet2.write('B4', 900)
+ worksheet2.write('B5', 1200)
+ worksheet2.write('B6', '=SUBTOTAL(9,B2:B5)', bold, 4300)
+
+ worksheet2.write('A7', 'South')
+ worksheet2.write('A8', 'South')
+ worksheet2.write('A9', 'South')
+ worksheet2.write('A10', 'South')
+ worksheet2.write('A11', 'South Total', bold)
+
+ worksheet2.write('B7', 400)
+ worksheet2.write('B8', 600)
+ worksheet2.write('B9', 500)
+ worksheet2.write('B10', 600)
+ worksheet2.write('B11', '=SUBTOTAL(9,B7:B10)', bold, 2100)
+
+ worksheet2.write('A12', 'Grand Total', bold)
+ worksheet2.write('B12', '=SUBTOTAL(9,B2:B10)', bold, 6400)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_outline06.py b/xlsxwriter/test/comparison/test_outline06.py
new file mode 100644
index 0000000..30b3266
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_outline06.py
@@ -0,0 +1,89 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'outline06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml', '[Content_Types].xml', 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """
+ Test the creation of a outlines in a XlsxWriter file. These tests are
+ based on the outline programs in the examples directory.
+ """
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet('Outlined Rows')
+
+ worksheet1.outline_settings(0, 0, 0, 1)
+
+ bold = workbook.add_format({'bold': 1})
+
+ worksheet1.set_row(1, None, None, {'level': 2})
+ worksheet1.set_row(2, None, None, {'level': 2})
+ worksheet1.set_row(3, None, None, {'level': 2})
+ worksheet1.set_row(4, None, None, {'level': 2})
+ worksheet1.set_row(5, None, None, {'level': 1})
+
+ worksheet1.set_row(6, None, None, {'level': 2})
+ worksheet1.set_row(7, None, None, {'level': 2})
+ worksheet1.set_row(8, None, None, {'level': 2})
+ worksheet1.set_row(9, None, None, {'level': 2})
+ worksheet1.set_row(10, None, None, {'level': 1})
+
+ worksheet1.set_column('A:A', 20)
+
+ worksheet1.write('A1', 'Region', bold)
+ worksheet1.write('A2', 'North')
+ worksheet1.write('A3', 'North')
+ worksheet1.write('A4', 'North')
+ worksheet1.write('A5', 'North')
+ worksheet1.write('A6', 'North Total', bold)
+
+ worksheet1.write('B1', 'Sales', bold)
+ worksheet1.write('B2', 1000)
+ worksheet1.write('B3', 1200)
+ worksheet1.write('B4', 900)
+ worksheet1.write('B5', 1200)
+ worksheet1.write('B6', '=SUBTOTAL(9,B2:B5)', bold, 4300)
+
+ worksheet1.write('A7', 'South')
+ worksheet1.write('A8', 'South')
+ worksheet1.write('A9', 'South')
+ worksheet1.write('A10', 'South')
+ worksheet1.write('A11', 'South Total', bold)
+
+ worksheet1.write('B7', 400)
+ worksheet1.write('B8', 600)
+ worksheet1.write('B9', 500)
+ worksheet1.write('B10', 600)
+ worksheet1.write('B11', '=SUBTOTAL(9,B7:B10)', bold, 2100)
+
+ worksheet1.write('A12', 'Grand Total', bold)
+ worksheet1.write('B12', '=SUBTOTAL(9,B2:B10)', bold, 6400)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_page_breaks01.py b/xlsxwriter/test/comparison/test_page_breaks01.py
new file mode 100644
index 0000000..b872752
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_page_breaks01.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'page_breaks01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with page breaks."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_h_pagebreaks([1])
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_page_breaks02.py b/xlsxwriter/test/comparison/test_page_breaks02.py
new file mode 100644
index 0000000..783926c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_page_breaks02.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'page_breaks02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with page breaks."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_h_pagebreaks([15, 7, 3, 0])
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_page_breaks03.py b/xlsxwriter/test/comparison/test_page_breaks03.py
new file mode 100644
index 0000000..48dc8b5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_page_breaks03.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'page_breaks03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with page breaks."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_h_pagebreaks(range(0, 1025))
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_page_breaks04.py b/xlsxwriter/test/comparison/test_page_breaks04.py
new file mode 100644
index 0000000..44faa5a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_page_breaks04.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'page_breaks04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with page breaks."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_v_pagebreaks([1])
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_page_breaks05.py b/xlsxwriter/test/comparison/test_page_breaks05.py
new file mode 100644
index 0000000..5e6d2c0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_page_breaks05.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'page_breaks05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with page breaks."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_v_pagebreaks([8, 3, 1, 0])
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_page_breaks06.py b/xlsxwriter/test/comparison/test_page_breaks06.py
new file mode 100644
index 0000000..ed85973
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_page_breaks06.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'page_breaks06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with page breaks."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_h_pagebreaks([1, 5, 13, 8, 8, 8])
+ worksheet.set_v_pagebreaks([8, 3, 1, 0])
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_page_view01.py b/xlsxwriter/test/comparison/test_page_view01.py
new file mode 100644
index 0000000..1c50b54
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_page_view01.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'page_view01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with print options."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_page_view()
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_panes01.py b/xlsxwriter/test/comparison/test_panes01.py
new file mode 100644
index 0000000..2fe1cb5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_panes01.py
@@ -0,0 +1,79 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'panes01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test an XlsxWriter file with panes.."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet01 = workbook.add_worksheet()
+ worksheet02 = workbook.add_worksheet()
+ worksheet03 = workbook.add_worksheet()
+ worksheet04 = workbook.add_worksheet()
+ worksheet05 = workbook.add_worksheet()
+ worksheet06 = workbook.add_worksheet()
+ worksheet07 = workbook.add_worksheet()
+ worksheet08 = workbook.add_worksheet()
+ worksheet09 = workbook.add_worksheet()
+ worksheet10 = workbook.add_worksheet()
+ worksheet11 = workbook.add_worksheet()
+ worksheet12 = workbook.add_worksheet()
+ worksheet13 = workbook.add_worksheet()
+
+ worksheet01.write('A1', 'Foo')
+ worksheet02.write('A1', 'Foo')
+ worksheet03.write('A1', 'Foo')
+ worksheet04.write('A1', 'Foo')
+ worksheet05.write('A1', 'Foo')
+ worksheet06.write('A1', 'Foo')
+ worksheet07.write('A1', 'Foo')
+ worksheet08.write('A1', 'Foo')
+ worksheet09.write('A1', 'Foo')
+ worksheet10.write('A1', 'Foo')
+ worksheet11.write('A1', 'Foo')
+ worksheet12.write('A1', 'Foo')
+ worksheet13.write('A1', 'Foo')
+
+ worksheet01.freeze_panes('A2')
+ worksheet02.freeze_panes('A3')
+ worksheet03.freeze_panes('B1')
+ worksheet04.freeze_panes('C1')
+ worksheet05.freeze_panes('B2')
+ worksheet06.freeze_panes('G4')
+ worksheet07.freeze_panes(3, 6, 3, 6, 1)
+ worksheet08.split_panes(15, 0)
+ worksheet09.split_panes(30, 0)
+ worksheet10.split_panes(0, 8.46)
+ worksheet11.split_panes(0, 17.57)
+ worksheet12.split_panes(15, 8.46)
+ worksheet13.split_panes(45, 54.14)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_across01.py b/xlsxwriter/test/comparison/test_print_across01.py
new file mode 100644
index 0000000..64f315a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_across01.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_across01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with fit across."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.print_across()
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_area01.py b/xlsxwriter/test/comparison/test_print_area01.py
new file mode 100644
index 0000000..470143f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_area01.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_area01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with a print area."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.print_area('A1:A1')
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_single_cell(self):
+ """Test the creation of a simple XlsxWriter file with a print area."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.print_area('A1')
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_area02.py b/xlsxwriter/test/comparison/test_print_area02.py
new file mode 100644
index 0000000..67ad788
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_area02.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_area02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with a print area."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.print_area('A1:G1')
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_area03.py b/xlsxwriter/test/comparison/test_print_area03.py
new file mode 100644
index 0000000..af02552
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_area03.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_area03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with a print area."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.print_area('A1:XFD1')
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_area04.py b/xlsxwriter/test/comparison/test_print_area04.py
new file mode 100644
index 0000000..3ff1b52
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_area04.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_area04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with a print area."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.print_area('A1:A8')
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_area05.py b/xlsxwriter/test/comparison/test_print_area05.py
new file mode 100644
index 0000000..dacaf3b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_area05.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_area05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with a print area."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.print_area('A1:A1048576')
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_area06.py b/xlsxwriter/test/comparison/test_print_area06.py
new file mode 100644
index 0000000..6e8895f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_area06.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_area06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with a print area."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.print_area('A1:F9')
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_area07.py b/xlsxwriter/test/comparison/test_print_area07.py
new file mode 100644
index 0000000..c4e589f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_area07.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_area07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with a print area."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.print_area('A1:XFD1048576')
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_options01.py b/xlsxwriter/test/comparison/test_print_options01.py
new file mode 100644
index 0000000..88acbc2
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_options01.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_options01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with print options."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.hide_gridlines(0)
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_options02.py b/xlsxwriter/test/comparison/test_print_options02.py
new file mode 100644
index 0000000..5b8461f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_options02.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_options02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with print options."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.center_horizontally()
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_options03.py b/xlsxwriter/test/comparison/test_print_options03.py
new file mode 100644
index 0000000..372906f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_options03.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_options03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with print options."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.center_vertically()
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_options04.py b/xlsxwriter/test/comparison/test_print_options04.py
new file mode 100644
index 0000000..9f64063
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_options04.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_options04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with print options."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.print_row_col_headers()
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_options05.py b/xlsxwriter/test/comparison/test_print_options05.py
new file mode 100644
index 0000000..03a4d20
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_options05.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_options05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with print options."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.hide_gridlines(0)
+ worksheet.center_horizontally()
+ worksheet.center_vertically()
+ worksheet.print_row_col_headers()
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_print_options06.py b/xlsxwriter/test/comparison/test_print_options06.py
new file mode 100644
index 0000000..5a85ec3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_print_options06.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'print_options06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with a print areaand a repeat rows"""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.print_area('A1:G20')
+ worksheet.repeat_rows(0)
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_properties01.py b/xlsxwriter/test/comparison/test_properties01.py
new file mode 100644
index 0000000..378d605
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_properties01.py
@@ -0,0 +1,53 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'properties01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ workbook.set_properties({
+ 'title': 'This is an example spreadsheet',
+ 'subject': 'With document properties',
+ 'author': 'Someone',
+ 'manager': 'Dr. Heinz Doofenshmirtz',
+ 'company': 'of Wolves',
+ 'category': 'Example spreadsheets',
+ 'keywords': 'Sample, Example, Properties',
+ 'comments': 'Created with Perl and Excel::Writer::XLSX',
+ 'status': 'Quo'})
+
+ worksheet.set_column('A:A', 70)
+ worksheet.write('A1', "Select 'Office Button -> Prepare -> Properties' to see the file properties.")
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_properties02.py b/xlsxwriter/test/comparison/test_properties02.py
new file mode 100644
index 0000000..e3e30ce
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_properties02.py
@@ -0,0 +1,41 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'properties02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ workbook.set_properties({'hyperlink_base': 'C:\\'})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_properties03.py b/xlsxwriter/test/comparison/test_properties03.py
new file mode 100644
index 0000000..85b3252
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_properties03.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'properties03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ workbook.set_custom_property('Checked by', 'Adam')
+
+ worksheet.set_column('A:A', 70)
+ worksheet.write('A1', "Select 'Office Button -> Prepare -> Properties' to see the file properties.")
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_properties04.py b/xlsxwriter/test/comparison/test_properties04.py
new file mode 100644
index 0000000..3676871
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_properties04.py
@@ -0,0 +1,87 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+from datetime import datetime
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'properties04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file_explicit(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ long_string = 'This is a long string. ' * 11
+ long_string += 'AA'
+
+ date = datetime.strptime('2016-12-12 23:00:00', '%Y-%m-%d %H:%M:%S')
+
+ # This test uses explicit property types.
+ workbook.set_custom_property('Checked by', 'Adam', 'text')
+ workbook.set_custom_property('Date completed', date, 'date')
+ workbook.set_custom_property('Document number', 12345, 'number_int')
+ workbook.set_custom_property('Reference', 1.2345, 'number')
+ workbook.set_custom_property('Source', True, 'bool')
+ workbook.set_custom_property('Status', False, 'bool')
+ workbook.set_custom_property('Department', long_string, 'text')
+ workbook.set_custom_property('Group', 1.23456789012, 'number')
+
+ worksheet.set_column('A:A', 70)
+ worksheet.write('A1', "Select 'Office Button -> Prepare -> Properties' to see the file properties.")
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_implicit(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ long_string = 'This is a long string. ' * 11
+ long_string += 'AA'
+
+ date = datetime.strptime('2016-12-12 23:00:00', '%Y-%m-%d %H:%M:%S')
+
+ # This test uses implicit property types.
+ workbook.set_custom_property('Checked by', 'Adam')
+ workbook.set_custom_property('Date completed', date)
+ workbook.set_custom_property('Document number', 12345)
+ workbook.set_custom_property('Reference', 1.2345)
+ workbook.set_custom_property('Source', True)
+ workbook.set_custom_property('Status', False)
+ workbook.set_custom_property('Department', long_string)
+ workbook.set_custom_property('Group', 1.23456789012)
+
+ worksheet.set_column('A:A', 70)
+ worksheet.write('A1', "Select 'Office Button -> Prepare -> Properties' to see the file properties.")
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_properties05.py b/xlsxwriter/test/comparison/test_properties05.py
new file mode 100644
index 0000000..396460f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_properties05.py
@@ -0,0 +1,45 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'properties05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ workbook.set_custom_property('Location', 'Café')
+
+ worksheet.set_column('A:A', 70)
+ worksheet.write('A1', "Select 'Office Button -> Prepare -> Properties' to see the file properties.")
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_protect01.py b/xlsxwriter/test/comparison/test_protect01.py
new file mode 100644
index 0000000..9b13fc9
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_protect01.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'protect01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the a simple XlsxWriter file with worksheet protection."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ unlocked = workbook.add_format({'locked': 0, 'hidden': 0})
+ hidden = workbook.add_format({'locked': 0, 'hidden': 1})
+
+ worksheet.write('A1', 1)
+ worksheet.write('A2', 2, unlocked)
+ worksheet.write('A3', 3, hidden)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_protect02.py b/xlsxwriter/test/comparison/test_protect02.py
new file mode 100644
index 0000000..2148d88
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_protect02.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'protect02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the a simple XlsxWriter file with worksheet protection."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ unlocked = workbook.add_format({'locked': 0, 'hidden': 0})
+ hidden = workbook.add_format({'locked': 0, 'hidden': 1})
+
+ worksheet.protect()
+
+ worksheet.write('A1', 1)
+ worksheet.write('A2', 2, unlocked)
+ worksheet.write('A3', 3, hidden)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_protect03.py b/xlsxwriter/test/comparison/test_protect03.py
new file mode 100644
index 0000000..4ca0a69
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_protect03.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'protect03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the a simple XlsxWriter file with worksheet protection."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ unlocked = workbook.add_format({'locked': 0, 'hidden': 0})
+ hidden = workbook.add_format({'locked': 0, 'hidden': 1})
+
+ worksheet.protect('password')
+
+ worksheet.write('A1', 1)
+ worksheet.write('A2', 2, unlocked)
+ worksheet.write('A3', 3, hidden)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_quote_name01.py b/xlsxwriter/test/comparison/test_quote_name01.py
new file mode 100644
index 0000000..194d625
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_quote_name01.py
@@ -0,0 +1,71 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'quote_name01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ # Test quoted/non-quoted sheet names.
+ worksheet = workbook.add_worksheet('Sheet 1')
+ chart = workbook.add_chart({'type': 'pie'})
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': ["'Sheet 1'", 0, 0, 4, 0]})
+ worksheet.insert_chart('E6', chart, {'x_offset': 26, 'y_offset': 17})
+
+ sheetnames = (
+ 'Sheet 2', "Sheet!3", 'Sheet"4',
+ 'Sheet#5', 'Sheet$6', 'Sheet%7', 'Sheet&8'
+ )
+
+ for sheetname in sheetnames:
+ worksheet = workbook.add_worksheet(sheetname)
+ chart = workbook.add_chart({'type': 'pie'})
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': [sheetname, 0, 0, 4, 0]})
+ worksheet.insert_chart('E6', chart, {'x_offset': 26, 'y_offset': 17})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_quote_name02.py b/xlsxwriter/test/comparison/test_quote_name02.py
new file mode 100644
index 0000000..3a2e227
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_quote_name02.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'quote_name02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ # Test quoted/non-quoted sheet names.
+ sheetnames = (
+ "Sheet'1", "S'heet'2", 'Sheet(3', 'Sheet)4',
+ 'Sheet+5', 'Sheet,6', 'Sheet-7', 'Sheet;8'
+ )
+
+ for sheetname in sheetnames:
+
+ worksheet = workbook.add_worksheet(sheetname)
+ chart = workbook.add_chart({'type': 'pie'})
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': [sheetname, 0, 0, 4, 0]})
+ worksheet.insert_chart('E6', chart, {'x_offset': 26, 'y_offset': 17})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_quote_name03.py b/xlsxwriter/test/comparison/test_quote_name03.py
new file mode 100644
index 0000000..1576712
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_quote_name03.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'quote_name03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ sheetnames = (
+ 'Sheet<1', 'Sheet>2', 'Sheet=3', 'Sheet at 4',
+ 'Sheet^5', 'Sheet`6', 'Sheet7', 'Sheet~8'
+ )
+
+ for sheetname in sheetnames:
+
+ worksheet = workbook.add_worksheet(sheetname)
+ chart = workbook.add_chart({'type': 'pie'})
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': [sheetname, 0, 0, 4, 0]})
+ worksheet.insert_chart('E6', chart, {'x_offset': 26, 'y_offset': 17})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_quote_name04.py b/xlsxwriter/test/comparison/test_quote_name04.py
new file mode 100644
index 0000000..c208143
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_quote_name04.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'quote_name04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+
+ ]
+
+ worksheet = workbook.add_worksheet('Sheet 1')
+ chart = workbook.add_chart({'type': 'pie'})
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': ['Sheet 1', 0, 0, 4, 0],
+ 'name': ['Sheet 1', 0, 0]})
+
+ chart.set_title({'name': 'Foo'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_remove_timezone.py b/xlsxwriter/test/comparison/test_remove_timezone.py
new file mode 100644
index 0000000..912b402
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_remove_timezone.py
@@ -0,0 +1,95 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from datetime import date
+from datetime import datetime
+from datetime import time
+from datetime import timedelta
+from datetime import tzinfo
+from ...workbook import Workbook
+
+
+# Simple class to add timezone to dates for testing.
+class GMT(tzinfo):
+
+ def utcoffset(self, dt):
+ return timedelta(hours=1)
+
+ def dst(self, dt):
+ return timedelta(0)
+
+ def tzname(self, dt):
+ return "Europe"
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'remove_timezone01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_remove_timezone_none(self):
+ """Test write_datetime without timezones."""
+
+ workbook = Workbook(self.got_filename, {'remove_timezone': False})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 20)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+ format3 = workbook.add_format({'num_format': 22})
+
+ date1 = time(12, 0, 0)
+ date2 = date(2016, 9, 23)
+ date3 = datetime.strptime('2016-09-12 12:00', "%Y-%m-%d %H:%M")
+
+ worksheet.write_datetime(0, 0, date1, format1)
+ worksheet.write_datetime(1, 0, date2, format2)
+ worksheet.write_datetime(2, 0, date3, format3)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_remove_timezone_gmt(self):
+ """Test write_datetime with timezones."""
+
+ workbook = Workbook(self.got_filename, {'remove_timezone': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 20)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+ format3 = workbook.add_format({'num_format': 22})
+
+ date1 = time(12, 0, 0, tzinfo=GMT())
+ date2 = date(2016, 9, 23)
+ date3 = datetime.strptime('2016-09-12 12:00', "%Y-%m-%d %H:%M")
+
+ date3 = date3.replace(tzinfo=GMT())
+
+ worksheet.write_datetime(1, 0, date2, format2)
+ worksheet.write_datetime(2, 0, date3, format3)
+
+ workbook.close()
diff --git a/xlsxwriter/test/comparison/test_repeat01.py b/xlsxwriter/test/comparison/test_repeat01.py
new file mode 100644
index 0000000..cb4605c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_repeat01.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'repeat01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with repeat rows."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.repeat_rows(0)
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_repeat02.py b/xlsxwriter/test/comparison/test_repeat02.py
new file mode 100644
index 0000000..15ad1bc
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_repeat02.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'repeat02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with repeat cols."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.repeat_columns('A:A')
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_repeat03.py b/xlsxwriter/test/comparison/test_repeat03.py
new file mode 100644
index 0000000..af19538
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_repeat03.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'repeat03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with repeat rows and cols."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.repeat_rows(0)
+ worksheet.repeat_columns('A:A')
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_repeat04.py b/xlsxwriter/test/comparison/test_repeat04.py
new file mode 100644
index 0000000..beb9951
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_repeat04.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'repeat04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with repeat rowswhen the sheet name contains a space."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet('Sheet 1')
+
+ worksheet.repeat_rows(0)
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_repeat05.py b/xlsxwriter/test/comparison/test_repeat05.py
new file mode 100644
index 0000000..aa1772d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_repeat05.py
@@ -0,0 +1,52 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'repeat05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/printerSettings/printerSettings2.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels',
+ 'xl/worksheets/_rels/sheet3.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins', '<pageSetup'],
+ 'xl/worksheets/sheet3.xml': ['<pageMargins', '<pageSetup']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with repeat rows andcols on more than one worksheet."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet()
+
+ worksheet1.repeat_rows(0)
+ worksheet3.repeat_rows(2, 3)
+ worksheet3.repeat_columns('B:F')
+
+ worksheet1.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_rich_string01.py b/xlsxwriter/test/comparison/test_rich_string01.py
new file mode 100644
index 0000000..b478863
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_rich_string01.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'rich_string01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+ worksheet.write_rich_string('A3', 'a', bold, 'bc', 'defg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_rich_string02.py b/xlsxwriter/test/comparison/test_rich_string02.py
new file mode 100644
index 0000000..89ff63f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_rich_string02.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'rich_string02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+ worksheet.write_rich_string('A3', 'abcd', italic, 'ef', 'g')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_rich_string03.py b/xlsxwriter/test/comparison/test_rich_string03.py
new file mode 100644
index 0000000..9a889a9
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_rich_string03.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'rich_string03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+ worksheet.write_rich_string('A3', bold, 'abc', 'defg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_rich_string04.py b/xlsxwriter/test/comparison/test_rich_string04.py
new file mode 100644
index 0000000..8c63926
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_rich_string04.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'rich_string04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+ worksheet.write_rich_string('A3', bold, 'abc', italic, 'defg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_rich_string05.py b/xlsxwriter/test/comparison/test_rich_string05.py
new file mode 100644
index 0000000..9f2b459
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_rich_string05.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'rich_string05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('A:A', 30)
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+ worksheet.write_rich_string('A3', 'This is ', bold, 'bold', ' and this is ', italic, 'italic')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_rich_string06.py b/xlsxwriter/test/comparison/test_rich_string06.py
new file mode 100644
index 0000000..3b066c3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_rich_string06.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'rich_string06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ red = workbook.add_format({'color': 'red'})
+
+ worksheet.write('A1', 'Foo', red)
+ worksheet.write('A2', 'Bar')
+ worksheet.write_rich_string('A3', 'ab', red, 'cde', 'fg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_rich_string07.py b/xlsxwriter/test/comparison/test_rich_string07.py
new file mode 100644
index 0000000..cdfc979
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_rich_string07.py
@@ -0,0 +1,51 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'rich_string07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+ worksheet.write_rich_string('A3', 'a', bold, 'bc', 'defg')
+ worksheet.write_rich_string('B4', 'abc', italic, 'de', 'fg')
+ worksheet.write_rich_string('C5', 'a', bold, 'bc', 'defg')
+ worksheet.write_rich_string('D6', 'abc', italic, 'de', 'fg')
+ worksheet.write_rich_string('E7', 'a', bold, 'bcdef', 'g')
+ worksheet.write_rich_string('F8', italic, 'abcd', 'efg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_rich_string08.py b/xlsxwriter/test/comparison/test_rich_string08.py
new file mode 100644
index 0000000..abef74e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_rich_string08.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'rich_string08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+ format = workbook.add_format({'align': 'center'})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+ worksheet.write_rich_string('A3', 'ab', bold, 'cd', 'efg', format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_rich_string09.py b/xlsxwriter/test/comparison/test_rich_string09.py
new file mode 100644
index 0000000..120d015
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_rich_string09.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'rich_string09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+ worksheet.write_rich_string('A3', 'a', bold, 'bc', 'defg')
+
+ worksheet.write_rich_string('A3', 'a', bold, bold, 'bc', 'defg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_rich_string10.py b/xlsxwriter/test/comparison/test_rich_string10.py
new file mode 100644
index 0000000..0f163a2
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_rich_string10.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'rich_string10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+
+ worksheet.write_rich_string('A3', ' a', bold, 'bc', 'defg ')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_rich_string11.py b/xlsxwriter/test/comparison/test_rich_string11.py
new file mode 100644
index 0000000..2b6fac6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_rich_string11.py
@@ -0,0 +1,52 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+import sys
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'rich_string11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ smiley = "\u263a"
+
+ if sys.version_info[0] == 2:
+ smiley = unichr(9786)
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+ worksheet.write_rich_string('A3', 'a', bold, smiley, 'defg')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_rich_string12.py b/xlsxwriter/test/comparison/test_rich_string12.py
new file mode 100644
index 0000000..4a895ac
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_rich_string12.py
@@ -0,0 +1,51 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'rich_string12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('A:A', 30)
+ worksheet.set_row(2, 60)
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+ wrap = workbook.add_format({'text_wrap': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+
+ worksheet.write_rich_string('A3', "This is\n", bold, "bold\n", "and this is\n", italic, 'italic', wrap)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_selection01.py b/xlsxwriter/test/comparison/test_selection01.py
new file mode 100644
index 0000000..c45f833
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_selection01.py
@@ -0,0 +1,41 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'selection01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_selection('B4:C5')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_selection02.py b/xlsxwriter/test/comparison/test_selection02.py
new file mode 100644
index 0000000..586614c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_selection02.py
@@ -0,0 +1,51 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'selection02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+ worksheet3 = workbook.add_worksheet()
+ worksheet4 = workbook.add_worksheet()
+ worksheet5 = workbook.add_worksheet()
+ worksheet6 = workbook.add_worksheet()
+
+ worksheet1.set_selection(3, 2, 3, 2)
+ worksheet2.set_selection(3, 2, 6, 6)
+ worksheet3.set_selection(6, 6, 3, 2)
+ worksheet4.set_selection('C4')
+ worksheet5.set_selection('C4:G7')
+ worksheet6.set_selection('G7:C4')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_set_column01.py b/xlsxwriter/test/comparison/test_set_column01.py
new file mode 100644
index 0000000..c5bf84a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_set_column01.py
@@ -0,0 +1,72 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'set_column01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column("A:A", 0.08)
+ worksheet.set_column("B:B", 0.17)
+ worksheet.set_column("C:C", 0.25)
+ worksheet.set_column("D:D", 0.33)
+ worksheet.set_column("E:E", 0.42)
+ worksheet.set_column("F:F", 0.5)
+ worksheet.set_column("G:G", 0.58)
+ worksheet.set_column("H:H", 0.67)
+ worksheet.set_column("I:I", 0.75)
+ worksheet.set_column("J:J", 0.83)
+ worksheet.set_column("K:K", 0.92)
+ worksheet.set_column("L:L", 1)
+ worksheet.set_column("M:M", 1.14)
+ worksheet.set_column("N:N", 1.29)
+ worksheet.set_column("O:O", 1.43)
+ worksheet.set_column("P:P", 1.57)
+ worksheet.set_column("Q:Q", 1.71)
+ worksheet.set_column("R:R", 1.86)
+ worksheet.set_column("S:S", 2)
+ worksheet.set_column("T:T", 2.14)
+ worksheet.set_column("U:U", 2.29)
+ worksheet.set_column("V:V", 2.43)
+ worksheet.set_column("W:W", 2.57)
+ worksheet.set_column("X:X", 2.71)
+ worksheet.set_column("Y:Y", 2.86)
+ worksheet.set_column("Z:Z", 3)
+ worksheet.set_column("AB:AB", 8.57)
+ worksheet.set_column("AC:AC", 8.71)
+ worksheet.set_column("AD:AD", 8.86)
+ worksheet.set_column("AE:AE", 9)
+ worksheet.set_column("AF:AF", 9.14)
+ worksheet.set_column("AG:AG", 9.29)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_set_column02.py b/xlsxwriter/test/comparison/test_set_column02.py
new file mode 100644
index 0000000..933e1bc
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_set_column02.py
@@ -0,0 +1,72 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'set_column01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_2_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column("A:A", 0.083333333333333)
+ worksheet.set_column("B:B", 0.166666666666667)
+ worksheet.set_column("C:C", 0.250000000000000)
+ worksheet.set_column("D:D", 0.333333333333333)
+ worksheet.set_column("E:E", 0.416666666666667)
+ worksheet.set_column("F:F", 0.500000000000000)
+ worksheet.set_column("G:G", 0.583333333333333)
+ worksheet.set_column("H:H", 0.666666666666666)
+ worksheet.set_column("I:I", 0.750000000000000)
+ worksheet.set_column("J:J", 0.833333333333333)
+ worksheet.set_column("K:K", 0.916666666666666)
+ worksheet.set_column("L:L", 1.000000000000000)
+ worksheet.set_column("M:M", 1.142857142857140)
+ worksheet.set_column("N:N", 1.285714285714290)
+ worksheet.set_column("O:O", 1.428571428571430)
+ worksheet.set_column("P:P", 1.571428571428570)
+ worksheet.set_column("Q:Q", 1.714285714285710)
+ worksheet.set_column("R:R", 1.857142857142860)
+ worksheet.set_column("S:S", 2.000000000000000)
+ worksheet.set_column("T:T", 2.142857142857140)
+ worksheet.set_column("U:U", 2.285714285714290)
+ worksheet.set_column("V:V", 2.428571428571430)
+ worksheet.set_column("W:W", 2.571428571428570)
+ worksheet.set_column("X:X", 2.714285714285710)
+ worksheet.set_column("Y:Y", 2.857142857142860)
+ worksheet.set_column("Z:Z", 3.000000000000000)
+ worksheet.set_column("AB:AB", 8.571428571428570)
+ worksheet.set_column("AC:AC", 8.711428571428570)
+ worksheet.set_column("AD:AD", 8.857142857142860)
+ worksheet.set_column("AE:AE", 9.000000000000000)
+ worksheet.set_column("AF:AF", 9.142857142857140)
+ worksheet.set_column("AG:AG", 9.285714285714290)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_set_column03.py b/xlsxwriter/test/comparison/test_set_column03.py
new file mode 100644
index 0000000..dbbdbd8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_set_column03.py
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'set_column03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write('A1', 'Foo', italic)
+ worksheet.write('B1', 'Bar', bold)
+ worksheet.write_column('A2', data[0])
+ worksheet.write_column('B2', data[1])
+ worksheet.write_column('C2', data[2])
+
+ worksheet.set_column('F:F', None, bold)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_set_column04.py b/xlsxwriter/test/comparison/test_set_column04.py
new file mode 100644
index 0000000..169b841
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_set_column04.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'set_column04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+ bold_italic = workbook.add_format({'bold': 1, 'italic': 1})
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write('A1', 'Foo', italic)
+ worksheet.write('B1', 'Bar', bold)
+ worksheet.write_column('A2', data[0])
+ worksheet.write_column('B2', data[1])
+ worksheet.write_column('C2', data[2])
+
+ worksheet.set_row(12, None, italic)
+ worksheet.set_column('F:F', None, bold)
+
+ worksheet.write('F13', None, bold_italic)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_set_column05.py b/xlsxwriter/test/comparison/test_set_column05.py
new file mode 100644
index 0000000..00180d0
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_set_column05.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'set_column05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+ bold_italic = workbook.add_format({'bold': 1, 'italic': 1})
+
+ chart.axis_ids = [68311296, 69198208]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write('A1', 'Foo', italic)
+ worksheet.write('B1', 'Bar', bold)
+ worksheet.write_column('A2', data[0])
+ worksheet.write_column('B2', data[1])
+ worksheet.write_column('C2', data[2])
+
+ worksheet.set_row(12, None, italic)
+ worksheet.set_column('F:F', None, bold)
+
+ worksheet.write('F13', None, bold_italic)
+
+ chart.add_series({'values': '=Sheet1!$A$2:$A$6'})
+ chart.add_series({'values': '=Sheet1!$B$2:$B$6'})
+ chart.add_series({'values': '=Sheet1!$C$2:$C$6'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_set_column06.py b/xlsxwriter/test/comparison/test_set_column06.py
new file mode 100644
index 0000000..4a69efb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_set_column06.py
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'set_column06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'line'})
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ chart.axis_ids = [69197824, 69199360]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('B1', 'Bar', italic)
+ worksheet.write_column('A2', data[0])
+ worksheet.write_column('B2', data[1])
+ worksheet.write_column('C2', data[2])
+
+ worksheet.set_row(12, None, None, {'hidden': True})
+ worksheet.set_column('F:F', None, None, {'hidden': True})
+
+ chart.add_series({'values': '=Sheet1!$A$2:$A$6'})
+ chart.add_series({'values': '=Sheet1!$B$2:$B$6'})
+ chart.add_series({'values': '=Sheet1!$C$2:$C$6'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_set_column07.py b/xlsxwriter/test/comparison/test_set_column07.py
new file mode 100644
index 0000000..2fcc4c5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_set_column07.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'set_column07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+ bold_italic = workbook.add_format({'bold': 1, 'italic': 1})
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write('A1', 'Foo', italic)
+ worksheet.write('B1', 'Bar', bold)
+ worksheet.write_column('A2', data[0])
+ worksheet.write_column('B2', data[1])
+ worksheet.write_column('C2', data[2])
+
+ worksheet.set_row(12, None, italic)
+ worksheet.set_column('F:F', None, bold)
+
+ worksheet.write('F13', None, bold_italic)
+
+ worksheet.insert_image('E12', self.image_dir + 'logo.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_set_column08.py b/xlsxwriter/test/comparison/test_set_column08.py
new file mode 100644
index 0000000..7273dec
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_set_column08.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'set_column08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('B1', 'Bar', italic)
+ worksheet.write_column('A2', data[0])
+ worksheet.write_column('B2', data[1])
+ worksheet.write_column('C2', data[2])
+
+ worksheet.set_row(12, None, None, {'hidden': True})
+ worksheet.set_column('F:F', None, None, {'hidden': True})
+
+ worksheet.insert_image('E12', self.image_dir + 'logo.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_set_column09.py b/xlsxwriter/test/comparison/test_set_column09.py
new file mode 100644
index 0000000..fd15c52
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_set_column09.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'set_column09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('A:A', 100)
+ worksheet.set_column('F:H', 8)
+ worksheet.set_column('C:D', 12)
+ worksheet.set_column('A:A', 10)
+ worksheet.set_column('XFD:XFD', 5)
+ worksheet.set_column('ZZ:ZZ', 3)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_set_print_scale01.py b/xlsxwriter/test/comparison/test_set_print_scale01.py
new file mode 100644
index 0000000..2a650ac
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_set_print_scale01.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'set_print_scale01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"'],
+ 'xl/worksheets/sheet1.xml': ['<pageMargins']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with printer settings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_print_scale(110)
+ worksheet.set_paper(9)
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_set_start_page01.py b/xlsxwriter/test/comparison/test_set_start_page01.py
new file mode 100644
index 0000000..7967714
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_set_start_page01.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'set_start_page01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with printer settings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_start_page(1)
+ worksheet.set_paper(9)
+
+ worksheet.vertical_dpi = 200
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_set_start_page02.py b/xlsxwriter/test/comparison/test_set_start_page02.py
new file mode 100644
index 0000000..78ad761
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_set_start_page02.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'set_start_page02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with printer settings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_start_page(2)
+ worksheet.set_paper(9)
+
+ worksheet.vertical_dpi = 200
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_set_start_page03.py b/xlsxwriter/test/comparison/test_set_start_page03.py
new file mode 100644
index 0000000..fe2c758
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_set_start_page03.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'set_start_page03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {'xl/worksheets/sheet1.xml': ['<pageMargins']}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with printer settings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_start_page(101)
+ worksheet.set_paper(9)
+
+ worksheet.vertical_dpi = 200
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_shared_strings01.py b/xlsxwriter/test/comparison/test_shared_strings01.py
new file mode 100644
index 0000000..b5993b7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_shared_strings01.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'shared_strings01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ # Test that control characters and any other single byte characters are
+ # handled correctly by the sharedstrings module. We skip chr 34 = " in
+ # this test since it isn't encoded by Excel as ".
+ chars = list(range(127))
+ del chars[34]
+
+ for char in chars:
+ worksheet.write_string(char, 0, chr(char))
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_shared_strings02.py b/xlsxwriter/test/comparison/test_shared_strings02.py
new file mode 100644
index 0000000..1bd90aa
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_shared_strings02.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'shared_strings02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ strings = [
+ "_",
+ "_x",
+ "_x0",
+ "_x00",
+ "_x000",
+ "_x0000",
+ "_x0000_",
+ "_x005F_",
+ "_x000G_",
+ "_X0000_",
+ "_x000a_",
+ "_x000A_",
+ "_x0000__x0000_",
+ "__x0000__",
+ ]
+
+ worksheet.write_column(0, 0, strings)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_simple01.py b/xlsxwriter/test/comparison/test_simple01.py
new file mode 100644
index 0000000..006c635
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_simple01.py
@@ -0,0 +1,132 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from __future__ import with_statement
+from ..excel_comparsion_test import ExcelComparisonTest
+from datetime import date
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'simple01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple workbook."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_string(0, 0, 'Hello')
+ worksheet.write_number(1, 0, 123)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_A1(self):
+ """Test the creation of a simple workbook with A1 notation."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_string('A1', 'Hello')
+ worksheet.write_number('A2', 123)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_write(self):
+ """Test the creation of a simple workbook using write()."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write(0, 0, 'Hello')
+ worksheet.write(1, 0, 123)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_with_statement(self):
+ """Test the creation of a simple workbook using `with` statement."""
+
+ with Workbook(self.got_filename) as workbook:
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write(0, 0, 'Hello')
+ worksheet.write(1, 0, 123)
+
+ self.assertExcelEqual()
+
+ def test_create_file_write_A1(self):
+ """Test the creation of a simple workbook using write() with A1."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Hello')
+ worksheet.write('A2', 123)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_kwargs(self):
+ """Test the creation of a simple workbook with keyword args."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_string(row=0, col=0, string='Hello')
+ worksheet.write_number(row=1, col=0, number=123)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_write_date_default(self):
+ """Test writing a datetime without a format. Issue #33"""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Hello')
+ worksheet.write('A2', date(1900, 5, 2))
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test the creation of a simple workbook."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_string(0, 0, 'Hello')
+ worksheet.write_number(1, 0, 123)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_simple02.py b/xlsxwriter/test/comparison/test_simple02.py
new file mode 100644
index 0000000..7bd9fa7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_simple02.py
@@ -0,0 +1,94 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'simple02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple workbook."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ workbook.add_worksheet('Data Sheet')
+ worksheet3 = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+
+ worksheet1.write_string(0, 0, 'Foo')
+ worksheet1.write_number(1, 0, 123)
+
+ worksheet3.write_string(1, 1, 'Foo')
+ worksheet3.write_string(2, 1, 'Bar', bold)
+ worksheet3.write_number(3, 2, 234)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_A1(self):
+ """Test the creation of a simple workbook with A1 notation."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet('Data Sheet')
+ worksheet3 = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+
+ worksheet1.write('A1', 'Foo')
+ worksheet1.write('A2', 123)
+
+ worksheet3.write('B2', 'Foo')
+ worksheet3.write('B3', 'Bar', bold)
+ worksheet3.write('C4', 234)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test the creation of a simple workbook."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet1 = workbook.add_worksheet()
+ workbook.add_worksheet('Data Sheet')
+ worksheet3 = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+
+ worksheet1.write_string(0, 0, 'Foo')
+ worksheet1.write_number(1, 0, 123)
+
+ worksheet3.write_string(1, 1, 'Foo')
+ worksheet3.write_string(2, 1, 'Bar', bold)
+ worksheet3.write_number(3, 2, 234)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_simple03.py b/xlsxwriter/test/comparison/test_simple03.py
new file mode 100644
index 0000000..b6637b8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_simple03.py
@@ -0,0 +1,84 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'simple03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test worksheet selection and activation."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet('Data Sheet')
+ worksheet3 = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+
+ worksheet1.write('A1', 'Foo')
+ worksheet1.write('A2', 123)
+
+ worksheet3.write('B2', 'Foo')
+ worksheet3.write('B3', 'Bar', bold)
+ worksheet3.write('C4', 234)
+
+ worksheet2.activate()
+
+ worksheet2.select()
+ worksheet3.select()
+ worksheet3.activate()
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test worksheet selection and activation."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet('Data Sheet')
+ worksheet3 = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+
+ worksheet1.write('A1', 'Foo')
+ worksheet1.write('A2', 123)
+
+ worksheet3.write('B2', 'Foo')
+ worksheet3.write('B3', 'Bar', bold)
+ worksheet3.write('C4', 234)
+
+ worksheet2.activate()
+
+ worksheet2.select()
+ worksheet3.select()
+ worksheet3.activate()
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_simple04.py b/xlsxwriter/test/comparison/test_simple04.py
new file mode 100644
index 0000000..c08e38e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_simple04.py
@@ -0,0 +1,161 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from datetime import datetime, date, time
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'simple04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test dates and times."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 12)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+
+ date1 = datetime.strptime('12:00', "%H:%M")
+ date2 = datetime.strptime('2013-01-27', "%Y-%m-%d")
+
+ worksheet.write_datetime(0, 0, date1, format1)
+ worksheet.write_datetime(1, 0, date2, format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_write(self):
+ """Test dates and times with write() method."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 12)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+
+ date1 = datetime.strptime('12:00', "%H:%M")
+ date2 = datetime.strptime('2013-01-27', "%Y-%m-%d")
+
+ worksheet.write(0, 0, date1, format1)
+ worksheet.write(1, 0, date2, format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_A1(self):
+ """Test dates and times in A1 notation."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('A:A', 12)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+
+ date1 = datetime.strptime('12:00', "%H:%M")
+ date2 = datetime.strptime('2013-01-27', "%Y-%m-%d")
+
+ worksheet.write_datetime('A1', date1, format1)
+ worksheet.write_datetime('A2', date2, format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_date_and_time1(self):
+ """Test dates and times with datetime .date and .time."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('A:A', 12)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+
+ date1 = time(12)
+ date2 = date(2013, 1, 27)
+
+ worksheet.write_datetime('A1', date1, format1)
+ worksheet.write_datetime('A2', date2, format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_date_and_time2(self):
+ """Test dates and times with datetime .date and .time. and write()"""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('A:A', 12)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+
+ date1 = time(12)
+ date2 = date(2013, 1, 27)
+
+ worksheet.write('A1', date1, format1)
+ worksheet.write('A2', date2, format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test dates and times."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 12)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+
+ date1 = datetime.strptime('12:00', "%H:%M")
+ date2 = datetime.strptime('2013-01-27', "%Y-%m-%d")
+
+ worksheet.write_datetime(0, 0, date1, format1)
+ worksheet.write_datetime(1, 0, date2, format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_simple05.py b/xlsxwriter/test/comparison/test_simple05.py
new file mode 100644
index 0000000..cda5cde
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_simple05.py
@@ -0,0 +1,88 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'simple05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test font formatting."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_row(5, 18)
+ worksheet.set_row(6, 18)
+
+ format1 = workbook.add_format({'bold': 1})
+ format2 = workbook.add_format({'italic': 1})
+ format3 = workbook.add_format({'bold': 1, 'italic': 1})
+ format4 = workbook.add_format({'underline': 1})
+ format5 = workbook.add_format({'font_strikeout': 1})
+ format6 = workbook.add_format({'font_script': 1})
+ format7 = workbook.add_format({'font_script': 2})
+
+ worksheet.write_string(0, 0, 'Foo', format1)
+ worksheet.write_string(1, 0, 'Foo', format2)
+ worksheet.write_string(2, 0, 'Foo', format3)
+ worksheet.write_string(3, 0, 'Foo', format4)
+ worksheet.write_string(4, 0, 'Foo', format5)
+ worksheet.write_string(5, 0, 'Foo', format6)
+ worksheet.write_string(6, 0, 'Foo', format7)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test font formatting."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_row(5, 18)
+ worksheet.set_row(6, 18)
+
+ format1 = workbook.add_format({'bold': 1})
+ format2 = workbook.add_format({'italic': 1})
+ format3 = workbook.add_format({'bold': 1, 'italic': 1})
+ format4 = workbook.add_format({'underline': 1})
+ format5 = workbook.add_format({'font_strikeout': 1})
+ format6 = workbook.add_format({'font_script': 1})
+ format7 = workbook.add_format({'font_script': 2})
+
+ worksheet.write_string(0, 0, 'Foo', format1)
+ worksheet.write_string(1, 0, 'Foo', format2)
+ worksheet.write_string(2, 0, 'Foo', format3)
+ worksheet.write_string(3, 0, 'Foo', format4)
+ worksheet.write_string(4, 0, 'Foo', format5)
+ worksheet.write_string(5, 0, 'Foo', format6)
+ worksheet.write_string(6, 0, 'Foo', format7)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_simple06.py b/xlsxwriter/test/comparison/test_simple06.py
new file mode 100644
index 0000000..48c645b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_simple06.py
@@ -0,0 +1,161 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from datetime import datetime, date, time
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'simple04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_6_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test dates and times."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 12)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+
+ date1 = datetime.strptime('12:00', "%H:%M")
+ date2 = datetime.strptime('2013-01-27', "%Y-%m-%d")
+
+ worksheet.write_datetime(0, 0, date1, format1)
+ worksheet.write_datetime(1, 0, date2, format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_write(self):
+ """Test dates and times with write() method."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 12)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+
+ date1 = datetime.strptime('12:00', "%H:%M")
+ date2 = datetime.strptime('2013-01-27', "%Y-%m-%d")
+
+ worksheet.write(0, 0, date1, format1)
+ worksheet.write(1, 0, date2, format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_A1(self):
+ """Test dates and times in A1 notation."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('A:A', 12)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+
+ date1 = datetime.strptime('12:00', "%H:%M")
+ date2 = datetime.strptime('2013-01-27', "%Y-%m-%d")
+
+ worksheet.write_datetime('A1', date1, format1)
+ worksheet.write_datetime('A2', date2, format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_date_and_time1(self):
+ """Test dates and times with datetime .date and .time."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('A:A', 12)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+
+ date1 = time(12)
+ date2 = date(2013, 1, 27)
+
+ worksheet.write_datetime('A1', date1, format1)
+ worksheet.write_datetime('A2', date2, format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_date_and_time2(self):
+ """Test dates and times with datetime .date and .time. and write()"""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('A:A', 12)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+
+ date1 = time(12)
+ date2 = date(2013, 1, 27)
+
+ worksheet.write('A1', date1, format1)
+ worksheet.write('A2', date2, format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test dates and times."""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column(0, 0, 12)
+
+ format1 = workbook.add_format({'num_format': 20})
+ format2 = workbook.add_format({'num_format': 14})
+
+ date1 = datetime.strptime('12:00', "%H:%M")
+ date2 = datetime.strptime('2013-01-27', "%Y-%m-%d")
+
+ worksheet.write_datetime(0, 0, date1, format1)
+ worksheet.write_datetime(1, 0, date2, format2)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_simple07.py b/xlsxwriter/test/comparison/test_simple07.py
new file mode 100644
index 0000000..18e21cd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_simple07.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'simple07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_write_nan(self):
+ """Test write with NAN/INF. Issue #30"""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_string(0, 0, 'Foo')
+ worksheet.write_number(1, 0, 123)
+ worksheet.write_string(2, 0, 'NAN')
+ worksheet.write_string(3, 0, 'nan')
+ worksheet.write_string(4, 0, 'INF')
+ worksheet.write_string(5, 0, 'infinity')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file_in_memory(self):
+ """Test write with NAN/INF. Issue #30"""
+
+ workbook = Workbook(self.got_filename, {'in_memory': True})
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_string(0, 0, 'Foo')
+ worksheet.write_number(1, 0, 123)
+ worksheet.write_string(2, 0, 'NAN')
+ worksheet.write_string(3, 0, 'nan')
+ worksheet.write_string(4, 0, 'INF')
+ worksheet.write_string(5, 0, 'infinity')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_simple08.py b/xlsxwriter/test/comparison/test_simple08.py
new file mode 100644
index 0000000..3bda9f8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_simple08.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'simple08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test '0' number format. GH103."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ format1 = workbook.add_format({'num_format': 1})
+
+ worksheet.write(0, 0, 1.23, format1)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_simple09.py b/xlsxwriter/test/comparison/test_simple09.py
new file mode 100644
index 0000000..c8b8794
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_simple09.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'simple09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ # Test data out of range. These should be ignored.
+ worksheet.write('A0', 'foo')
+ worksheet.write(-1, -1, 'foo')
+ worksheet.write(0, -1, 'foo')
+ worksheet.write(-1, 0, 'foo')
+ worksheet.write(1048576, 0, 'foo')
+ worksheet.write(0, 16384, 'foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_tab_color01.py b/xlsxwriter/test/comparison/test_tab_color01.py
new file mode 100644
index 0000000..fb920e2
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_tab_color01.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'tab_color01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with a coloured tab."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Foo')
+ worksheet.set_tab_color('red')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table01.py b/xlsxwriter/test/comparison/test_table01.py
new file mode 100644
index 0000000..0259eb8
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table01.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('C:F', 10.288)
+
+ worksheet.add_table('C3:F13')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table02.py b/xlsxwriter/test/comparison/test_table02.py
new file mode 100644
index 0000000..1d550fe
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table02.py
@@ -0,0 +1,50 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+
+ worksheet1.set_column('B:J', 10.288)
+ worksheet2.set_column('C:L', 10.288)
+
+ worksheet1.add_table('B3:E11')
+ worksheet1.add_table('G10:J16')
+ worksheet1.add_table('C18:F25')
+
+ worksheet2.add_table('I4:L11')
+ worksheet2.add_table('C16:H23')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table03.py b/xlsxwriter/test/comparison/test_table03.py
new file mode 100644
index 0000000..39313f7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table03.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('C:F', 10.288)
+
+ worksheet.add_table('C3:F13')
+
+ worksheet.write('A1', 'http://perl.com/')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table04.py b/xlsxwriter/test/comparison/test_table04.py
new file mode 100644
index 0000000..85a9161
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table04.py
@@ -0,0 +1,52 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('C:F', 10.288)
+
+ worksheet.add_table('C3:F13')
+
+ worksheet.write('A1', 'http://perl.com/')
+
+ worksheet.set_comments_author('John')
+ worksheet.write_comment('H1', 'Test1')
+ worksheet.write_comment('J1', 'Test2')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table05.py b/xlsxwriter/test/comparison/test_table05.py
new file mode 100644
index 0000000..f7bca4d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table05.py
@@ -0,0 +1,55 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('C:F', 10.288)
+
+ worksheet.add_table('C3:F13')
+
+ worksheet.write('A1', 'http://perl.com/')
+
+ worksheet.set_comments_author('John')
+ worksheet.write_comment('H1', 'Test1')
+ worksheet.write_comment('J1', 'Test2')
+
+ worksheet.insert_image('A4', self.image_dir + 'blue.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table06.py b/xlsxwriter/test/comparison/test_table06.py
new file mode 100644
index 0000000..21d1ae3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table06.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ # Turn off default URL format for testing.
+ workbook.default_url_format = None
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('C:H', 10.288)
+
+ worksheet.add_table('C3:F13')
+ worksheet.add_table('F15:H20')
+ worksheet.add_table('C23:D30')
+
+ worksheet.write('A1', 'http://perl.com/')
+ worksheet.write('C1', 'http://perl.com/')
+
+ worksheet.set_comments_author('John')
+ worksheet.write_comment('H1', 'Test1')
+ worksheet.write_comment('J1', 'Test2')
+
+ worksheet.insert_image('A4', self.image_dir + 'blue.png')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table07.py b/xlsxwriter/test/comparison/test_table07.py
new file mode 100644
index 0000000..ac53eae
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table07.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('C:F', 10.288)
+
+ worksheet.add_table('C3:F13', {'header_row': 0})
+
+ worksheet.write('A1', 'Foo')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table08.py b/xlsxwriter/test/comparison/test_table08.py
new file mode 100644
index 0000000..809f95d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table08.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml', '[Content_Types].xml', 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('C:F', 10.288)
+
+ worksheet.write_string('A1', 'Column1')
+ worksheet.write_string('B1', 'Column2')
+ worksheet.write_string('C1', 'Column3')
+ worksheet.write_string('D1', 'Column4')
+ worksheet.write_string('E1', 'Total')
+
+ worksheet.add_table('C3:F14', {'total_row': 1,
+ 'columns': [{'total_string': 'Total'},
+ {},
+ {},
+ {'total_function': 'count'},
+ ]})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table09.py b/xlsxwriter/test/comparison/test_table09.py
new file mode 100644
index 0000000..27a93eb
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table09.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml', '[Content_Types].xml', 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('B:K', 10.288)
+
+ worksheet.write_string('A1', 'Column1')
+ worksheet.write_string('B1', 'Column2')
+ worksheet.write_string('C1', 'Column3')
+ worksheet.write_string('D1', 'Column4')
+ worksheet.write_string('E1', 'Column5')
+ worksheet.write_string('F1', 'Column6')
+ worksheet.write_string('G1', 'Column7')
+ worksheet.write_string('H1', 'Column8')
+ worksheet.write_string('I1', 'Column9')
+ worksheet.write_string('J1', 'Column10')
+ worksheet.write_string('K1', 'Total')
+
+ data = [0, 0, 0, None, None, 0, 0, 0, 0, 0]
+ worksheet.write_row('B4', data)
+ worksheet.write_row('B5', data)
+
+ worksheet.add_table('B3:K6', {'total_row': 1,
+ 'columns': [{'total_string': 'Total'},
+ {},
+ {'total_function': 'Average'},
+ {'total_function': 'COUNT'},
+ {'total_function': 'count_nums'},
+ {'total_function': 'max'},
+ {'total_function': 'min'},
+ {'total_function': 'sum'},
+ {'total_function': 'std Dev'},
+ {'total_function': 'var'}
+ ]})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table10.py b/xlsxwriter/test/comparison/test_table10.py
new file mode 100644
index 0000000..bc2982a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table10.py
@@ -0,0 +1,71 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml', '[Content_Types].xml', 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ xformat = workbook.add_format({'num_format': 2})
+
+ worksheet.set_column('B:K', 10.288)
+
+ worksheet.write_string('A1', 'Column1')
+ worksheet.write_string('B1', 'Column2')
+ worksheet.write_string('C1', 'Column3')
+ worksheet.write_string('D1', 'Column4')
+ worksheet.write_string('E1', 'Column5')
+ worksheet.write_string('F1', 'Column6')
+ worksheet.write_string('G1', 'Column7')
+ worksheet.write_string('H1', 'Column8')
+ worksheet.write_string('I1', 'Column9')
+ worksheet.write_string('J1', 'Column10')
+ worksheet.write_string('K1', 'Total')
+
+ data = [0, 0, 0, None, None, 0, 0, 0, 0, 0]
+ worksheet.write_row('B4', data)
+ worksheet.write_row('B5', data)
+
+ worksheet.add_table('B3:K6', {'total_row': 1,
+ 'columns': [{'total_string': 'Total'},
+ {},
+ {'total_function': 'Average'},
+ {'total_function': 'COUNT'},
+ {'total_function': 'count_nums'},
+ {'total_function': 'max'},
+ {'total_function': 'min'},
+ {'total_function': 'sum'},
+ {'total_function': 'std Dev'},
+ {'total_function': 'var', 'formula': 'SUM(Table1[[#This Row],[Column1]:[Column3]])', 'format': xformat}
+ ]})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table11.py b/xlsxwriter/test/comparison/test_table11.py
new file mode 100644
index 0000000..856a4c2
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table11.py
@@ -0,0 +1,50 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ data = [
+ ['Foo', 1234, 2000, 4321],
+ ['Bar', 1256, 4000, 4320],
+ ['Baz', 2234, 3000, 4332],
+ ['Bop', 1324, 1000, 4333],
+ ]
+
+ worksheet.set_column('C:F', 10.288)
+
+ worksheet.add_table('C2:F6', {'data': data})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table12.py b/xlsxwriter/test/comparison/test_table12.py
new file mode 100644
index 0000000..4011ab5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table12.py
@@ -0,0 +1,49 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ data = [
+ ['Foo', 1234, 2000],
+ ['Bar', 1256, 4000],
+ ['Baz', 2234, 3000],
+ ]
+
+ worksheet.set_column('C:F', 10.288)
+
+ worksheet.add_table('C2:F6', {'data': data})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table13.py b/xlsxwriter/test/comparison/test_table13.py
new file mode 100644
index 0000000..b953e47
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table13.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table13.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ format1 = workbook.add_format({'num_format': 2, 'dxf_index': 2})
+ format2 = workbook.add_format({'num_format': 2, 'dxf_index': 1})
+ format3 = workbook.add_format({'num_format': 2, 'dxf_index': 0})
+
+ data = [
+ ['Foo', 1234, 2000, 4321],
+ ['Bar', 1256, 4000, 4320],
+ ['Baz', 2234, 3000, 4332],
+ ['Bop', 1324, 1000, 4333],
+ ]
+
+ worksheet.set_column('C:F', 10.288)
+
+ worksheet.add_table('C2:F6', {'data': data,
+ 'columns': [{},
+ {'format': format1},
+ {'format': format2},
+ {'format': format3},
+ ]})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table14.py b/xlsxwriter/test/comparison/test_table14.py
new file mode 100644
index 0000000..49e04c6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table14.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table14.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ format1 = workbook.add_format({'num_format': '0.00;[Red]0.00', 'dxf_index': 2})
+ format2 = workbook.add_format({'num_format': '0.00_ ;\-0.00\ ', 'dxf_index': 1})
+ format3 = workbook.add_format({'num_format': '0.00_ ;[Red]\-0.00\ ', 'dxf_index': 0})
+
+ data = [
+ ['Foo', 1234, 2000, 4321],
+ ['Bar', 1256, 4000, 4320],
+ ['Baz', 2234, 3000, 4332],
+ ['Bop', 1324, 1000, 4333],
+ ]
+
+ worksheet.set_column('C:F', 10.288)
+
+ worksheet.add_table('C2:F6', {'data': data,
+ 'columns': [{},
+ {'format': format1},
+ {'format': format2},
+ {'format': format3},
+ ]})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table15.py b/xlsxwriter/test/comparison/test_table15.py
new file mode 100644
index 0000000..6aeccc7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table15.py
@@ -0,0 +1,50 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table15.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ data = [
+ ['Foo', 1234, 2000, 4321],
+ ['Bar', 1256, 0, 4320],
+ ['Baz', 2234, 3000, 4332],
+ ['Bop', 1324, 1000, 4333],
+ ]
+
+ worksheet.set_column('C:F', 10.288)
+
+ worksheet.add_table('C2:F6', {'data': data})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table16.py b/xlsxwriter/test/comparison/test_table16.py
new file mode 100644
index 0000000..d6de7ed
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table16.py
@@ -0,0 +1,50 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+
+ worksheet1.set_column('B:J', 10.288)
+ worksheet2.set_column('C:L', 10.288)
+
+ worksheet2.add_table('I4:L11')
+ worksheet2.add_table('C16:H23')
+
+ worksheet1.add_table('B3:E11')
+ worksheet1.add_table('G10:J16')
+ worksheet1.add_table('C18:F25')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table17.py b/xlsxwriter/test/comparison/test_table17.py
new file mode 100644
index 0000000..e3ad146
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table17.py
@@ -0,0 +1,75 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table17.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml', '[Content_Types].xml', 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('B:K', 10.288)
+
+ worksheet.write_string('A1', 'Column1')
+ worksheet.write_string('B1', 'Column2')
+ worksheet.write_string('C1', 'Column3')
+ worksheet.write_string('D1', 'Column4')
+ worksheet.write_string('E1', 'Column5')
+ worksheet.write_string('F1', 'Column6')
+ worksheet.write_string('G1', 'Column7')
+ worksheet.write_string('H1', 'Column8')
+ worksheet.write_string('I1', 'Column9')
+ worksheet.write_string('J1', 'Column10')
+ worksheet.write_string('K1', 'Total')
+
+ data = [0, 0, 0, None, None, 0, 0, 0, 0, 0]
+ worksheet.write_row('B4', data)
+ worksheet.write_row('B5', data)
+ worksheet.write('G4', 4)
+ worksheet.write('G5', 5)
+ worksheet.write('I4', 1)
+ worksheet.write('I5', 2)
+
+ worksheet.add_table('B3:K6',
+ {'total_row': 1,
+ 'columns': [{'total_string': 'Total'},
+ {},
+ {'total_function': 'Average'},
+ {'total_function': 'COUNT'},
+ {'total_function': 'count_nums'},
+ {'total_function': 'max', 'total_value': 5},
+ {'total_function': 'min'},
+ {'total_function': 'sum', 'total_value': 3},
+ {'total_function': 'std Dev'},
+ {'total_function': 'var'}
+ ]})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table18.py b/xlsxwriter/test/comparison/test_table18.py
new file mode 100644
index 0000000..0039eae
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table18.py
@@ -0,0 +1,52 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table18.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ text_wrap = workbook.add_format({'text_wrap': 1})
+
+ worksheet.set_column('C:F', 10.288)
+ worksheet.set_row(2, 39)
+
+ worksheet.add_table('C3:F13',
+ {'columns': [{},
+ {},
+ {},
+ {'header': "Column\n4",
+ 'header_format': text_wrap}]})
+
+ worksheet.write('A16', 'hello')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table19.py b/xlsxwriter/test/comparison/test_table19.py
new file mode 100644
index 0000000..98138dd
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table19.py
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table19.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('C:F', 10.288)
+
+ worksheet.add_table('C3:F13',
+ {'columns': [{},
+ {},
+ {},
+ {'header': " Column4 "}]})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table20.py b/xlsxwriter/test/comparison/test_table20.py
new file mode 100644
index 0000000..ac611c4
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table20.py
@@ -0,0 +1,52 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_2' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ # Ignore increased shared string count.
+ self.ignore_files = ['xl/sharedStrings.xml']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.set_column('C:F', 10.288)
+
+ worksheet.add_table('C3:F13')
+
+ # The following should be ignored since it contains duplicate headers.
+ # Ignore the warning.
+ import warnings
+ warnings.filterwarnings('ignore')
+
+ worksheet.add_table('C3:E3',
+ {'columns': [{'header': 'Column1'},
+ {'header': 'Column1'}]})
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table21.py b/xlsxwriter/test/comparison/test_table21.py
new file mode 100644
index 0000000..f75d2a9
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table21.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table21.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Column')
+
+ worksheet.set_column('C:D', 10.288)
+
+ worksheet.add_table('C3:D13',
+ {'columns': [{'header': "Column"}]})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_table22.py b/xlsxwriter/test/comparison/test_table22.py
new file mode 100644
index 0000000..e2712ae
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_table22.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'table22.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with tables."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ data = [
+ ['apple', 'pie'],
+ ['pine', 'tree'],
+ ]
+
+ worksheet.set_column('B:C', 10.288)
+
+ worksheet.add_table('B2:C3', {'data': data, 'header_row': False})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox01.py b/xlsxwriter/test/comparison/test_textbox01.py
new file mode 100644
index 0000000..369ef84
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox01.py
@@ -0,0 +1,41 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox02.py b/xlsxwriter/test/comparison/test_textbox02.py
new file mode 100644
index 0000000..970678b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox02.py
@@ -0,0 +1,43 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text')
+
+ worksheet.insert_textbox('H18', 'Some more text')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox03.py b/xlsxwriter/test/comparison/test_textbox03.py
new file mode 100644
index 0000000..dc66fbe
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox03.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet1 = workbook.add_worksheet()
+ worksheet2 = workbook.add_worksheet()
+
+ worksheet1.insert_textbox('E9', 'This is some text')
+ worksheet1.insert_textbox('H18', 'Some more text')
+
+ worksheet2.insert_textbox('C4', 'Hello')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox04.py b/xlsxwriter/test/comparison/test_textbox04.py
new file mode 100644
index 0000000..269dbd5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox04.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [61365632, 64275584]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ worksheet.insert_textbox('F25', 'This is some text')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox05.py b/xlsxwriter/test/comparison/test_textbox05.py
new file mode 100644
index 0000000..5df062a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox05.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.image_dir = test_dir + 'images/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'column'})
+
+ chart.axis_ids = [71529216, 71530752]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ worksheet.insert_chart('E9', chart)
+
+ worksheet.insert_image('E25', self.image_dir + 'red.png')
+
+ worksheet.insert_textbox('G25', 'This is some text')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox06.py b/xlsxwriter/test/comparison/test_textbox06.py
new file mode 100644
index 0000000..545f84c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox06.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'width': 256, 'height': 100})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox07.py b/xlsxwriter/test/comparison/test_textbox07.py
new file mode 100644
index 0000000..cb8e77c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox07.py
@@ -0,0 +1,41 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text\nand some more text')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox08.py b/xlsxwriter/test/comparison/test_textbox08.py
new file mode 100644
index 0000000..6abfbd1
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox08.py
@@ -0,0 +1,41 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some long text that should wrap around to the next line')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox09.py b/xlsxwriter/test/comparison/test_textbox09.py
new file mode 100644
index 0000000..9eb571a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox09.py
@@ -0,0 +1,41 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This\nis\n\nsome long text')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox10.py b/xlsxwriter/test/comparison/test_textbox10.py
new file mode 100644
index 0000000..3e33047
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox10.py
@@ -0,0 +1,41 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', '')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox11.py b/xlsxwriter/test/comparison/test_textbox11.py
new file mode 100644
index 0000000..b1c5b5f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox11.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'fill': {'color': 'red'}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox12.py b/xlsxwriter/test/comparison/test_textbox12.py
new file mode 100644
index 0000000..f0b6fe7
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox12.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox12.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'fill': {'none': True}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox13.py b/xlsxwriter/test/comparison/test_textbox13.py
new file mode 100644
index 0000000..0073577
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox13.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox13.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'line': {'color': 'red',
+ 'width': 2,
+ 'dash_type': 'round_dot',
+ }})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox14.py b/xlsxwriter/test/comparison/test_textbox14.py
new file mode 100644
index 0000000..f60c7af
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox14.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox14.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'border': {'none': True}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox15.py b/xlsxwriter/test/comparison/test_textbox15.py
new file mode 100644
index 0000000..7c930c3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox15.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox15.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'align': {'horizontal': 'center'}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox16.py b/xlsxwriter/test/comparison/test_textbox16.py
new file mode 100644
index 0000000..cfa656e
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox16.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox16.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'align': {'vertical': 'middle'}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox17.py b/xlsxwriter/test/comparison/test_textbox17.py
new file mode 100644
index 0000000..5d359d3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox17.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox17.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'align': {'vertical': 'bottom'}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox18.py b/xlsxwriter/test/comparison/test_textbox18.py
new file mode 100644
index 0000000..331d9a6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox18.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox18.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text')
+
+ worksheet.insert_textbox('E19', 'This is some text',
+ {'align': {'vertical': 'middle'}})
+
+ worksheet.insert_textbox('E29', 'This is some text',
+ {'align': {'vertical': 'bottom'}})
+
+ worksheet.insert_textbox('E39', 'This is some text',
+ {'align': {'vertical': 'top',
+ 'horizontal': 'center'}})
+
+ worksheet.insert_textbox('E49', 'This is some text',
+ {'align': {'vertical': 'middle',
+ 'horizontal': 'center'}})
+
+ worksheet.insert_textbox('E59', 'This is some text',
+ {'align': {'vertical': 'bottom',
+ 'horizontal': 'center'}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox19.py b/xlsxwriter/test/comparison/test_textbox19.py
new file mode 100644
index 0000000..332158f
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox19.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox19.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'gradient': {'colors': ['#DDEBCF',
+ '#9CB86E',
+ '#156B13']}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox20.py b/xlsxwriter/test/comparison/test_textbox20.py
new file mode 100644
index 0000000..8dd43e6
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox20.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox20.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'font': {'bold': True}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox21.py b/xlsxwriter/test/comparison/test_textbox21.py
new file mode 100644
index 0000000..bd208da
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox21.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox21.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'font': {'bold': True,
+ 'italic': True,
+ 'underline': True,
+ 'size': 12}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox22.py b/xlsxwriter/test/comparison/test_textbox22.py
new file mode 100644
index 0000000..9f6a233
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox22.py
@@ -0,0 +1,44 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox22.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'font': {'name': 'Arial',
+ 'pitch_family': 34,
+ 'charset': 0}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox23.py b/xlsxwriter/test/comparison/test_textbox23.py
new file mode 100644
index 0000000..a9b7f6b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox23.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox23.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'font': {'color': 'red'}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox24.py b/xlsxwriter/test/comparison/test_textbox24.py
new file mode 100644
index 0000000..934885b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox24.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox24.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This\nis\n\nsome long text',
+ {'font': {'color': 'red'}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox25.py b/xlsxwriter/test/comparison/test_textbox25.py
new file mode 100644
index 0000000..ce0f0af
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox25.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox25.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'line': {'width': 3.25}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_textbox26.py b/xlsxwriter/test/comparison/test_textbox26.py
new file mode 100644
index 0000000..fe8ed55
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_textbox26.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'textbox26.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of a simple XlsxWriter file with textbox(s)."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.insert_textbox('E9', 'This is some text',
+ {'font': {'name': 'Arial',
+ 'size': 11.5,
+ 'pitch_family': 34,
+ 'charset': 0}})
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_tutorial01.py b/xlsxwriter/test/comparison/test_tutorial01.py
new file mode 100644
index 0000000..d4461b5
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_tutorial01.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'tutorial01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml',
+ '[Content_Types].xml',
+ 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Example spreadsheet used in the tutorial."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Some data we want to write to the worksheet.
+ expenses = (
+ ['Rent', 1000],
+ ['Gas', 100],
+ ['Food', 300],
+ ['Gym', 50],
+ )
+
+ # Start from the first cell. Rows and columns are zero indexed.
+ row = 0
+ col = 0
+
+ # Iterate over the data and write it out row by row.
+ for item, cost in (expenses):
+ worksheet.write(row, col, item)
+ worksheet.write(row, col + 1, cost)
+ row += 1
+
+ # Write a total using a formula.
+ worksheet.write(row, 0, 'Total')
+ worksheet.write(row, 1, '=SUM(B1:B4)', None, 1450)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_tutorial02.py b/xlsxwriter/test/comparison/test_tutorial02.py
new file mode 100644
index 0000000..4f1446a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_tutorial02.py
@@ -0,0 +1,72 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'tutorial02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml',
+ '[Content_Types].xml',
+ 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Example spreadsheet used in the tutorial 2."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Add a bold format to use to highlight cells.
+ bold = workbook.add_format({'bold': True})
+
+ # Add a number format for cells with money.
+ money_format = workbook.add_format({'num_format': '\\$#,##0'})
+
+ # Write some data headers.
+ worksheet.write('A1', 'Item', bold)
+ worksheet.write('B1', 'Cost', bold)
+
+ # Some data we want to write to the worksheet.
+ expenses = (
+ ['Rent', 1000],
+ ['Gas', 100],
+ ['Food', 300],
+ ['Gym', 50],
+ )
+
+ # Start from the first cell below the headers.
+ row = 1
+ col = 0
+
+ # Iterate over the data and write it out row by row.
+ for item, cost in (expenses):
+ worksheet.write(row, col, item)
+ worksheet.write(row, col + 1, cost, money_format)
+ row += 1
+
+ # Write a total using a formula.
+ worksheet.write(row, 0, 'Total', bold)
+ worksheet.write(row, 1, '=SUM(B2:B5)', money_format, 1450)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_tutorial03.py b/xlsxwriter/test/comparison/test_tutorial03.py
new file mode 100644
index 0000000..c666e61
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_tutorial03.py
@@ -0,0 +1,137 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from datetime import datetime
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'tutorial03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml',
+ '[Content_Types].xml',
+ 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Example spreadsheet used in the tutorial."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Add a bold format to use to highlight cells.
+ bold = workbook.add_format({'bold': 1})
+
+ # Add a number format for cells with money.
+ money_format = workbook.add_format({'num_format': '\\$#,##0'})
+
+ # Add an Excel date format.
+ date_format = workbook.add_format({'num_format': 'mmmm\\ d\\ yyyy'})
+
+ # Adjust the column width.
+ worksheet.set_column('B:B', 15)
+
+ # Write some data headers.
+ worksheet.write('A1', 'Item', bold)
+ worksheet.write('B1', 'Date', bold)
+ worksheet.write('C1', 'Cost', bold)
+
+ # Some data we want to write to the worksheet.
+ expenses = (
+ ['Rent', '2013-01-13', 1000],
+ ['Gas', '2013-01-14', 100],
+ ['Food', '2013-01-16', 300],
+ ['Gym', '2013-01-20', 50],
+ )
+
+ # Start from the first cell below the headers.
+ row = 1
+ col = 0
+
+ for item, date_str, cost in (expenses):
+
+ # Convert the date string into a datetime object.
+ date = datetime.strptime(date_str, "%Y-%m-%d")
+
+ worksheet.write_string(row, col, item)
+ worksheet.write_datetime(row, col + 1, date, date_format)
+ worksheet.write_number(row, col + 2, cost, money_format)
+ row += 1
+
+ # Write a total using a formula.
+ worksheet.write(row, 0, 'Total', bold)
+ worksheet.write(row, 2, '=SUM(C2:C5)', money_format, 1450)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_create_file2(self):
+ """
+ Example spreadsheet used in the tutorial. Format creation is
+ re-ordered to ensure correct internal order is maintained.
+
+ """
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Same as above but re-ordered.
+ date_format = workbook.add_format({'num_format': 'mmmm\\ d\\ yyyy'})
+ money_format = workbook.add_format({'num_format': '\\$#,##0'})
+ bold = workbook.add_format({'bold': 1})
+
+ # Adjust the column width.
+ worksheet.set_column(1, 1, 15)
+
+ # Write some data headers.
+ worksheet.write('A1', 'Item', bold)
+ worksheet.write('B1', 'Date', bold)
+ worksheet.write('C1', 'Cost', bold)
+
+ # Some data we want to write to the worksheet.
+ expenses = (
+ ['Rent', '2013-01-13', 1000],
+ ['Gas', '2013-01-14', 100],
+ ['Food', '2013-01-16', 300],
+ ['Gym', '2013-01-20', 50],
+ )
+
+ # Start from the first cell below the headers.
+ row = 1
+ col = 0
+
+ for item, date_str, cost in (expenses):
+
+ # Convert the date string into a datetime object.
+ date = datetime.strptime(date_str, "%Y-%m-%d")
+
+ worksheet.write_string(row, col, item)
+ worksheet.write_datetime(row, col + 1, date, date_format)
+ worksheet.write_number(row, col + 2, cost, money_format)
+ row += 1
+
+ # Write a total using a formula.
+ worksheet.write(row, 0, 'Total', bold)
+ worksheet.write(row, 2, '=SUM(C2:C5)', money_format, 1450)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_types01.py b/xlsxwriter/test/comparison/test_types01.py
new file mode 100644
index 0000000..5bf9c56
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_types01.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'types01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_write_number_as_text(self):
+ """Test writing numbers as text."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_string(0, 0, 'Hello')
+ worksheet.write_string(1, 0, '123')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_write_number_as_text_with_write(self):
+ """Test writing numbers as text using write() without conversion."""
+
+ workbook = Workbook(self.got_filename, {'strings_to_numbers': False})
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write(0, 0, 'Hello')
+ worksheet.write(1, 0, '123')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_types02.py b/xlsxwriter/test/comparison/test_types02.py
new file mode 100644
index 0000000..a60a470
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_types02.py
@@ -0,0 +1,54 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'types02.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_write_boolean(self):
+ """Test writing boolean."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_boolean(0, 0, True)
+ worksheet.write_boolean(1, 0, False)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_write_boolean_write(self):
+ """Test writing boolean with write()."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write(0, 0, True)
+ worksheet.write(1, 0, False)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_types03.py b/xlsxwriter/test/comparison/test_types03.py
new file mode 100644
index 0000000..5bde582
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_types03.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from decimal import Decimal
+from fractions import Fraction
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'types03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_write_number_float(self):
+ """Test writing number types."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 0.5)
+ worksheet.write_number('A2', 0.5)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_write_number_decimal(self):
+ """Test writing number types."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', Decimal('0.5'))
+ worksheet.write_number('A2', Decimal('0.5'))
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_write_number_fraction(self):
+ """Test writing number types."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', Fraction(1, 2))
+ worksheet.write_number('A2', Fraction(2, 4))
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_types04.py b/xlsxwriter/test/comparison/test_types04.py
new file mode 100644
index 0000000..1cec43b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_types04.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'types04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_write_url_default(self):
+ """Test writing hyperlinks with strings_to_urls on."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+ red = workbook.add_format({'font_color': 'red'})
+
+ worksheet.write(0, 0, 'http://www.google.com/', red)
+ worksheet.write_string(1, 0, 'http://www.google.com/', red)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_write_url_implicit(self):
+ """Test writing hyperlinks with strings_to_urls on."""
+
+ workbook = Workbook(self.got_filename, {'strings_to_urls': True})
+ worksheet = workbook.add_worksheet()
+ red = workbook.add_format({'font_color': 'red'})
+
+ worksheet.write(0, 0, 'http://www.google.com/', red)
+ worksheet.write_string(1, 0, 'http://www.google.com/', red)
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_write_url_explicit(self):
+ """Test writing hyperlinks with strings_to_urls off."""
+
+ workbook = Workbook(self.got_filename, {'strings_to_urls': False})
+ worksheet = workbook.add_worksheet()
+ red = workbook.add_format({'font_color': 'red'})
+
+ worksheet.write_url(0, 0, 'http://www.google.com/', red)
+ worksheet.write(1, 0, 'http://www.google.com/', red)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_types05.py b/xlsxwriter/test/comparison/test_types05.py
new file mode 100644
index 0000000..d9f6831
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_types05.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'types05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml',
+ '[Content_Types].xml',
+ 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_write_formula_default(self):
+ """Test writing formulas with strings_to_formulas on."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write(0, 0, '=1+1', None, 2)
+ worksheet.write_string(1, 0, '=1+1')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_write_formula_implicit(self):
+ """Test writing formulas with strings_to_formulas on."""
+
+ workbook = Workbook(self.got_filename, {'strings_to_formulas': True})
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write(0, 0, '=1+1', None, 2)
+ worksheet.write_string(1, 0, '=1+1')
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_write_formula_explicit(self):
+ """Test writing formulas with strings_to_formulas off."""
+
+ workbook = Workbook(self.got_filename, {'strings_to_formulas': False})
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_formula(0, 0, '=1+1', None, 2)
+ worksheet.write(1, 0, '=1+1')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_types06.py b/xlsxwriter/test/comparison/test_types06.py
new file mode 100644
index 0000000..302a826
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_types06.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'types06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml',
+ '[Content_Types].xml',
+ 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_write_formula_default(self):
+ """Test writing formulas with strings_to_formulas on."""
+
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write(0, 0, '="0"&".0"', None, '0.0')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_types07.py b/xlsxwriter/test/comparison/test_types07.py
new file mode 100644
index 0000000..04584de
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_types07.py
@@ -0,0 +1,71 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'types07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml', '[Content_Types].xml', 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_write_nan_and_inf(self):
+ """Test writing special numbers."""
+
+ workbook = Workbook(self.got_filename, {'nan_inf_to_errors': True})
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write(0, 0, float('nan'))
+ worksheet.write(1, 0, float('inf'))
+ worksheet.write(2, 0, float('-inf'))
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_write_nan_and_inf_write_number(self):
+ """Test writing special numbers."""
+
+ workbook = Workbook(self.got_filename, {'nan_inf_to_errors': True})
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_number(0, 0, float('nan'))
+ worksheet.write_number(1, 0, float('inf'))
+ worksheet.write_number(2, 0, float('-inf'))
+
+ workbook.close()
+
+ self.assertExcelEqual()
+
+ def test_write_nan_and_inf_write_as_string(self):
+ """Test writing special numbers."""
+
+ workbook = Workbook(self.got_filename, {'nan_inf_to_errors': True,
+ 'strings_to_numbers': True})
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write(0, 0, 'nan')
+ worksheet.write(1, 0, 'inf')
+ worksheet.write(2, 0, '-inf')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_unicode_polish_utf8.py b/xlsxwriter/test/comparison/test_unicode_polish_utf8.py
new file mode 100644
index 0000000..45cee9d
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_unicode_polish_utf8.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+import codecs
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'unicode_polish_utf8.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+ self.txt_filename = test_dir + 'xlsx_files/' + 'unicode_polish_utf8.txt'
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test example file converting Unicode text."""
+
+ # Open the input file with the correct encoding.
+ textfile = codecs.open(self.txt_filename, 'r', 'utf-8')
+
+ # Create an new Excel file and convert the text data.
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Widen the first column to make the text clearer.
+ worksheet.set_column('A:A', 50)
+
+ # Start from the first cell.
+ row = 0
+ col = 0
+
+ # Read the text file and write it to the worksheet.
+ for line in textfile:
+ # Ignore the comments in the sample file.
+ if line.startswith('#'):
+ continue
+
+ # Write any other lines to the worksheet.
+ worksheet.write(row, col, line.rstrip("\n"))
+ row += 1
+
+ workbook.close()
+ textfile.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_unicode_shift_jis.py b/xlsxwriter/test/comparison/test_unicode_shift_jis.py
new file mode 100644
index 0000000..ded43a3
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_unicode_shift_jis.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+from ..excel_comparsion_test import ExcelComparisonTest
+import codecs
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'unicode_shift_jis.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+ self.txt_filename = test_dir + 'xlsx_files/' + 'unicode_shift_jis.txt'
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test example file converting Unicode text."""
+
+ # Open the input file with the correct encoding.
+ textfile = codecs.open(self.txt_filename, 'r', 'shift_jis')
+
+ # Create an new Excel file and convert the text data.
+ workbook = Workbook(self.got_filename)
+ worksheet = workbook.add_worksheet()
+
+ # Widen the first column to make the text clearer.
+ worksheet.set_column('A:A', 50)
+
+ # Start from the first cell.
+ row = 0
+ col = 0
+
+ # Read the text file and write it to the worksheet.
+ for line in textfile:
+ # Ignore the comments in the sample file.
+ if line.startswith('#'):
+ continue
+
+ # Write any other lines to the worksheet.
+ worksheet.write(row, col, line.rstrip("\n"))
+ row += 1
+
+ workbook.close()
+ textfile.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_utf8_01.py b/xlsxwriter/test/comparison/test_utf8_01.py
new file mode 100644
index 0000000..e9c0a2b
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_utf8_01.py
@@ -0,0 +1,42 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'utf8_01.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with utf-8 strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Это фраза на русском!')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_utf8_03.py b/xlsxwriter/test/comparison/test_utf8_03.py
new file mode 100644
index 0000000..97bdeec
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_utf8_03.py
@@ -0,0 +1,42 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'utf8_03.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with utf-8 strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet('Café')
+
+ worksheet.write('A1', 'Café')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_utf8_04.py b/xlsxwriter/test/comparison/test_utf8_04.py
new file mode 100644
index 0000000..92b6809
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_utf8_04.py
@@ -0,0 +1,42 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'utf8_04.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with utf-8 strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet('Café & Café')
+
+ worksheet.write('A1', 'Café & Café')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_utf8_05.py b/xlsxwriter/test/comparison/test_utf8_05.py
new file mode 100644
index 0000000..05d0609
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_utf8_05.py
@@ -0,0 +1,42 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'utf8_05.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/calcChain.xml', '[Content_Types].xml', 'xl/_rels/workbook.xml.rels']
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with utf-8 strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write_formula('A1', '="Café"', None, 'Café')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_utf8_06.py b/xlsxwriter/test/comparison/test_utf8_06.py
new file mode 100644
index 0000000..a6a8818
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_utf8_06.py
@@ -0,0 +1,47 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'utf8_06.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with utf-8 strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ bold = workbook.add_format({'bold': 1})
+ italic = workbook.add_format({'italic': 1})
+
+ worksheet.write('A1', 'Foo', bold)
+ worksheet.write('A2', 'Bar', italic)
+ worksheet.write_rich_string('A3', 'Caf', bold, 'é')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_utf8_07.py b/xlsxwriter/test/comparison/test_utf8_07.py
new file mode 100644
index 0000000..d9f0eea
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_utf8_07.py
@@ -0,0 +1,46 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'utf8_07.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with utf-8 strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Foo')
+ worksheet.write_comment('A1', 'Café')
+
+ # Set the author to match the target XLSX file.
+ worksheet.set_comments_author('John')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_utf8_08.py b/xlsxwriter/test/comparison/test_utf8_08.py
new file mode 100644
index 0000000..11592ac
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_utf8_08.py
@@ -0,0 +1,48 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'utf8_08.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = ['xl/printerSettings/printerSettings1.bin',
+ 'xl/worksheets/_rels/sheet1.xml.rels']
+ self.ignore_elements = {'[Content_Types].xml': ['<Default Extension="bin"']}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with utf-8 strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', 'Foo')
+
+ worksheet.set_header('&LCafé')
+ worksheet.set_footer('&Rclé')
+
+ worksheet.set_paper(9)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_utf8_09.py b/xlsxwriter/test/comparison/test_utf8_09.py
new file mode 100644
index 0000000..9b43d7c
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_utf8_09.py
@@ -0,0 +1,43 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'utf8_09.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with utf-8 strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ num_format = workbook.add_format({'num_format': '[$¥-411]#,##0.00'})
+
+ worksheet.write('A1', 1, num_format)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_utf8_10.py b/xlsxwriter/test/comparison/test_utf8_10.py
new file mode 100644
index 0000000..83a0e0a
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_utf8_10.py
@@ -0,0 +1,63 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'utf8_10.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with utf-8 strings."""
+
+ workbook = Workbook(self.got_filename)
+
+ worksheet = workbook.add_worksheet()
+ chart = workbook.add_chart({'type': 'bar'})
+
+ chart.axis_ids = [86604416, 89227648]
+
+ data = [
+ [1, 2, 3, 4, 5],
+ [2, 4, 6, 8, 10],
+ [3, 6, 9, 12, 15],
+ ]
+
+ worksheet.write_column('A1', data[0])
+ worksheet.write_column('B1', data[1])
+ worksheet.write_column('C1', data[2])
+
+ chart.add_series({'values': '=Sheet1!$A$1:$A$5'})
+ chart.add_series({'values': '=Sheet1!$B$1:$B$5'})
+ chart.add_series({'values': '=Sheet1!$C$1:$C$5'})
+
+ chart.set_x_axis({'name': 'café'})
+ chart.set_y_axis({'name': 'sauté'})
+ chart.set_title({'name': 'résumé'})
+
+ worksheet.insert_chart('E9', chart)
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/test_utf8_11.py b/xlsxwriter/test/comparison/test_utf8_11.py
new file mode 100644
index 0000000..accfb00
--- /dev/null
+++ b/xlsxwriter/test/comparison/test_utf8_11.py
@@ -0,0 +1,42 @@
+###############################################################################
+# _*_ coding: utf-8
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+from __future__ import unicode_literals
+from ..excel_comparsion_test import ExcelComparisonTest
+from ...workbook import Workbook
+
+
+class TestCompareXLSXFiles(ExcelComparisonTest):
+ """
+ Test file created by XlsxWriter against a file created by Excel.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+
+ filename = 'utf8_11.xlsx'
+
+ test_dir = 'xlsxwriter/test/comparison/'
+ self.got_filename = test_dir + '_test_' + filename
+ self.exp_filename = test_dir + 'xlsx_files/' + filename
+
+ self.ignore_files = []
+ self.ignore_elements = {}
+
+ def test_create_file(self):
+ """Test the creation of an XlsxWriter file with utf-8 strings."""
+
+ workbook = Workbook(self.got_filename, {'strings_to_urls': True})
+
+ worksheet = workbook.add_worksheet()
+
+ worksheet.write('A1', '12345')
+
+ workbook.close()
+
+ self.assertExcelEqual()
diff --git a/xlsxwriter/test/comparison/xlsx_files/array_formula01.xlsx b/xlsxwriter/test/comparison/xlsx_files/array_formula01.xlsx
new file mode 100644
index 0000000..41acd0a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/array_formula01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/array_formula02.xlsx b/xlsxwriter/test/comparison/xlsx_files/array_formula02.xlsx
new file mode 100644
index 0000000..390ada8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/array_formula02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/autofilter00.xlsx b/xlsxwriter/test/comparison/xlsx_files/autofilter00.xlsx
new file mode 100644
index 0000000..bb12352
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/autofilter00.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/autofilter01.xlsx b/xlsxwriter/test/comparison/xlsx_files/autofilter01.xlsx
new file mode 100644
index 0000000..fa923ad
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/autofilter01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/autofilter02.xlsx b/xlsxwriter/test/comparison/xlsx_files/autofilter02.xlsx
new file mode 100644
index 0000000..3076308
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/autofilter02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/autofilter03.xlsx b/xlsxwriter/test/comparison/xlsx_files/autofilter03.xlsx
new file mode 100644
index 0000000..0135290
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/autofilter03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/autofilter04.xlsx b/xlsxwriter/test/comparison/xlsx_files/autofilter04.xlsx
new file mode 100644
index 0000000..e64282a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/autofilter04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/autofilter05.xlsx b/xlsxwriter/test/comparison/xlsx_files/autofilter05.xlsx
new file mode 100644
index 0000000..ea0f667
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/autofilter05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/autofilter06.xlsx b/xlsxwriter/test/comparison/xlsx_files/autofilter06.xlsx
new file mode 100644
index 0000000..c498ae6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/autofilter06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/autofilter07.xlsx b/xlsxwriter/test/comparison/xlsx_files/autofilter07.xlsx
new file mode 100644
index 0000000..9392716
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/autofilter07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/autofilter_data.txt b/xlsxwriter/test/comparison/xlsx_files/autofilter_data.txt
new file mode 100644
index 0000000..2c970a2
--- /dev/null
+++ b/xlsxwriter/test/comparison/xlsx_files/autofilter_data.txt
@@ -0,0 +1,51 @@
+Region Item Volume Month
+East Apple 9000 July
+East Apple 5000 July
+South Orange 9000 September
+North Apple 2000 November
+West Apple 9000 November
+South Pear 7000 October
+North Pear 9000 August
+West Orange 1000 December
+West Grape 1000 November
+South Pear 10000 April
+West Grape 6000 January
+South Orange 3000 May
+North Apple 3000 December
+South Apple 7000 February
+West Grape 1000 December
+East Grape 8000 February
+South Grape 10000 June
+West Pear 7000 December
+South Apple 2000 October
+East Grape 7000 December
+North Grape 6000 April
+East Pear 8000 February
+North Apple 7000 August
+North Orange 7000 July
+North Apple 6000 June
+South Grape 8000 September
+West Apple 3000 October
+South Orange 10000 November
+West Grape 4000 July
+North Orange 5000 August
+East Orange 1000 November
+East Orange 4000 October
+North Grape 5000 August
+East Apple 1000 December
+South Apple 10000 March
+East Grape 7000 October
+West Grape 1000 September
+East Grape 10000 October
+South Orange 8000 March
+North Apple 4000 July
+South Orange 5000 July
+West Apple 4000 June
+East Apple 5000 April
+North Pear 3000 August
+East Grape 9000 November
+North Orange 8000 October
+East Apple 10000 June
+South Pear 1000 December
+North Grape 10000 July
+East Grape 6000 February
diff --git a/xlsxwriter/test/comparison/xlsx_files/button01.xlsx b/xlsxwriter/test/comparison/xlsx_files/button01.xlsx
new file mode 100644
index 0000000..1712d53
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/button01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/button02.xlsx b/xlsxwriter/test/comparison/xlsx_files/button02.xlsx
new file mode 100644
index 0000000..311f689
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/button02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/button03.xlsx b/xlsxwriter/test/comparison/xlsx_files/button03.xlsx
new file mode 100644
index 0000000..a26303a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/button03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/button04.xlsx b/xlsxwriter/test/comparison/xlsx_files/button04.xlsx
new file mode 100644
index 0000000..8a6f458
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/button04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/button05.xlsx b/xlsxwriter/test/comparison/xlsx_files/button05.xlsx
new file mode 100644
index 0000000..852d6ed
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/button05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/button07.xlsm b/xlsxwriter/test/comparison/xlsx_files/button07.xlsm
new file mode 100644
index 0000000..835fe45
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/button07.xlsm differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/button08.xlsx b/xlsxwriter/test/comparison/xlsx_files/button08.xlsx
new file mode 100644
index 0000000..a875107
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/button08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/button09.xlsx b/xlsxwriter/test/comparison/xlsx_files/button09.xlsx
new file mode 100644
index 0000000..83ae73c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/button09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/button10.xlsx b/xlsxwriter/test/comparison/xlsx_files/button10.xlsx
new file mode 100644
index 0000000..dca6dcd
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/button10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/button11.xlsx b/xlsxwriter/test/comparison/xlsx_files/button11.xlsx
new file mode 100644
index 0000000..4f5296c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/button11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/button12.xlsx b/xlsxwriter/test/comparison/xlsx_files/button12.xlsx
new file mode 100644
index 0000000..6b9084a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/button12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_area01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_area01.xlsx
new file mode 100644
index 0000000..97b7240
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_area01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_area02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_area02.xlsx
new file mode 100644
index 0000000..a50ac53
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_area02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_area03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_area03.xlsx
new file mode 100644
index 0000000..2a8ff6e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_area03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_area04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_area04.xlsx
new file mode 100644
index 0000000..25222cf
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_area04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis01.xlsx
new file mode 100644
index 0000000..aab3819
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis02.xlsx
new file mode 100644
index 0000000..b492e7d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis03.xlsx
new file mode 100644
index 0000000..fd8fb5d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis04.xlsx
new file mode 100644
index 0000000..c3cc964
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis05.xlsx
new file mode 100644
index 0000000..0b89cdd
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis06.xlsx
new file mode 100644
index 0000000..36eba1f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis07.xlsx
new file mode 100644
index 0000000..8fab0ff
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis08.xlsx
new file mode 100644
index 0000000..3995f8f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis09.xlsx
new file mode 100644
index 0000000..39ae4fd
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis10.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis10.xlsx
new file mode 100644
index 0000000..c48e01b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis11.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis11.xlsx
new file mode 100644
index 0000000..67fb9f1
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis12.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis12.xlsx
new file mode 100644
index 0000000..e9d0fb6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis13.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis13.xlsx
new file mode 100644
index 0000000..85efab8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis13.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis14.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis14.xlsx
new file mode 100644
index 0000000..f45feba
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis14.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis15.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis15.xlsx
new file mode 100644
index 0000000..52fab75
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis15.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis16.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis16.xlsx
new file mode 100644
index 0000000..ddf53e1
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis16.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis17.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis17.xlsx
new file mode 100644
index 0000000..9eac49c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis17.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis18.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis18.xlsx
new file mode 100644
index 0000000..8cc2581
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis18.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis19.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis19.xlsx
new file mode 100644
index 0000000..16fee04
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis19.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis20.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis20.xlsx
new file mode 100644
index 0000000..1685e5a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis20.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis21.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis21.xlsx
new file mode 100644
index 0000000..906dcca
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis21.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis22.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis22.xlsx
new file mode 100644
index 0000000..b75247d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis22.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis23.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis23.xlsx
new file mode 100644
index 0000000..4ee538f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis23.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis24.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis24.xlsx
new file mode 100644
index 0000000..c9e515d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis24.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis25.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis25.xlsx
new file mode 100644
index 0000000..600b7f8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis25.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis26.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis26.xlsx
new file mode 100644
index 0000000..4c4cba9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis26.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis27.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis27.xlsx
new file mode 100644
index 0000000..647e819
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis27.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis28.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis28.xlsx
new file mode 100644
index 0000000..43af0c4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis28.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis29.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis29.xlsx
new file mode 100644
index 0000000..63170c0
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis29.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis30.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis30.xlsx
new file mode 100644
index 0000000..5500e74
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis30.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis31.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis31.xlsx
new file mode 100644
index 0000000..08f6008
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis31.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis32.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis32.xlsx
new file mode 100644
index 0000000..a1da7d6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis32.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis33.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis33.xlsx
new file mode 100644
index 0000000..c36fd5c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis33.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis34.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis34.xlsx
new file mode 100644
index 0000000..27c49ec
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis34.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis35.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis35.xlsx
new file mode 100644
index 0000000..419cf69
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis35.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis36.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis36.xlsx
new file mode 100644
index 0000000..84574de
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis36.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis37.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis37.xlsx
new file mode 100644
index 0000000..9377406
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis37.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis38.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis38.xlsx
new file mode 100644
index 0000000..a01a2ce
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis38.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis39.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis39.xlsx
new file mode 100644
index 0000000..101a871
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis39.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis40.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis40.xlsx
new file mode 100644
index 0000000..9fad51e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis40.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_axis41.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_axis41.xlsx
new file mode 100644
index 0000000..3ac9342
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_axis41.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar01.xlsx
new file mode 100644
index 0000000..4ce2ba3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar02.xlsx
new file mode 100644
index 0000000..2ed2565
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar03.xlsx
new file mode 100644
index 0000000..309c07e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar04.xlsx
new file mode 100644
index 0000000..0c4207a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar05.xlsx
new file mode 100644
index 0000000..6c65e4f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar06.xlsx
new file mode 100644
index 0000000..6bc4d81
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar07.xlsx
new file mode 100644
index 0000000..40757d7
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar08.xlsx
new file mode 100644
index 0000000..d35a7d8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar09.xlsx
new file mode 100644
index 0000000..a0ab896
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar10.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar10.xlsx
new file mode 100644
index 0000000..7ff2068
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar11.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar11.xlsx
new file mode 100644
index 0000000..776910f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar18.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar18.xlsx
new file mode 100644
index 0000000..796938b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar18.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar19.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar19.xlsx
new file mode 100644
index 0000000..40757d7
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar19.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar20.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar20.xlsx
new file mode 100644
index 0000000..449834a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar20.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar21.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar21.xlsx
new file mode 100644
index 0000000..4ce2ba3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar21.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar22.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar22.xlsx
new file mode 100644
index 0000000..7008786
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar22.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar23.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar23.xlsx
new file mode 100644
index 0000000..7008786
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar23.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_bar24.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_bar24.xlsx
new file mode 100644
index 0000000..26e690a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_bar24.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_blank01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_blank01.xlsx
new file mode 100644
index 0000000..d59b0c1
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_blank01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_blank02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_blank02.xlsx
new file mode 100644
index 0000000..8760b39
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_blank02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_blank03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_blank03.xlsx
new file mode 100644
index 0000000..1c0d41a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_blank03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_blank04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_blank04.xlsx
new file mode 100644
index 0000000..70f0b1e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_blank04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_blank05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_blank05.xlsx
new file mode 100644
index 0000000..1008b14
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_blank05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_blank06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_blank06.xlsx
new file mode 100644
index 0000000..2c6f5e4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_blank06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_chartarea01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_chartarea01.xlsx
new file mode 100644
index 0000000..e5e9b1d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_chartarea01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_chartarea03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_chartarea03.xlsx
new file mode 100644
index 0000000..9146db6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_chartarea03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_chartarea04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_chartarea04.xlsx
new file mode 100644
index 0000000..7f9b19e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_chartarea04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_chartarea05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_chartarea05.xlsx
new file mode 100644
index 0000000..e324458
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_chartarea05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_clustered01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_clustered01.xlsx
new file mode 100644
index 0000000..e63f5cb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_clustered01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_column01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_column01.xlsx
new file mode 100644
index 0000000..728ac97
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_column01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_column02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_column02.xlsx
new file mode 100644
index 0000000..6acd635
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_column02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_column03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_column03.xlsx
new file mode 100644
index 0000000..c038130
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_column03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_column04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_column04.xlsx
new file mode 100644
index 0000000..637d735
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_column04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_column05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_column05.xlsx
new file mode 100644
index 0000000..b387bb5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_column05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_column06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_column06.xlsx
new file mode 100644
index 0000000..9552ddb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_column06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_column07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_column07.xlsx
new file mode 100644
index 0000000..871856b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_column07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_column08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_column08.xlsx
new file mode 100644
index 0000000..41c37de
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_column08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_column09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_column09.xlsx
new file mode 100644
index 0000000..d8ca51a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_column09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_column10.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_column10.xlsx
new file mode 100644
index 0000000..77ba56f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_column10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_column11.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_column11.xlsx
new file mode 100644
index 0000000..b424d08
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_column11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_column12.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_column12.xlsx
new file mode 100644
index 0000000..2e7772c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_column12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_combined01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_combined01.xlsx
new file mode 100644
index 0000000..778f552
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_combined01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_combined02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_combined02.xlsx
new file mode 100644
index 0000000..739aa51
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_combined02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_combined03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_combined03.xlsx
new file mode 100644
index 0000000..5c5b2f1
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_combined03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_combined04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_combined04.xlsx
new file mode 100644
index 0000000..c4ff0ea
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_combined04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_combined05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_combined05.xlsx
new file mode 100644
index 0000000..9e9fe1d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_combined05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_combined06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_combined06.xlsx
new file mode 100644
index 0000000..6d7a31e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_combined06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_combined07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_combined07.xlsx
new file mode 100644
index 0000000..3999678
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_combined07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_combined08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_combined08.xlsx
new file mode 100644
index 0000000..e13215f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_combined08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_combined09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_combined09.xlsx
new file mode 100644
index 0000000..478c4a8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_combined09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_crossing01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_crossing01.xlsx
new file mode 100644
index 0000000..10199f9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_crossing01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_crossing02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_crossing02.xlsx
new file mode 100644
index 0000000..848166b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_crossing02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_crossing03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_crossing03.xlsx
new file mode 100644
index 0000000..4b3db9d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_crossing03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_crossing04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_crossing04.xlsx
new file mode 100644
index 0000000..0b1cfea
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_crossing04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels01.xlsx
new file mode 100644
index 0000000..6b0e2fc
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels02.xlsx
new file mode 100644
index 0000000..52b78bc
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels03.xlsx
new file mode 100644
index 0000000..c9ca73d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels04.xlsx
new file mode 100644
index 0000000..4a1a26e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels05.xlsx
new file mode 100644
index 0000000..dcd260a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels06.xlsx
new file mode 100644
index 0000000..d445d5d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels07.xlsx
new file mode 100644
index 0000000..950288b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels08.xlsx
new file mode 100644
index 0000000..09b91fd
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels09.xlsx
new file mode 100644
index 0000000..2c22cb0
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels10.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels10.xlsx
new file mode 100644
index 0000000..e531de1
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels11.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels11.xlsx
new file mode 100644
index 0000000..d969280
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels12.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels12.xlsx
new file mode 100644
index 0000000..f7330dd
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels13.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels13.xlsx
new file mode 100644
index 0000000..b722e36
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels13.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels14.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels14.xlsx
new file mode 100644
index 0000000..a594d8a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels14.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels15.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels15.xlsx
new file mode 100644
index 0000000..e0060b3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels15.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels16.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels16.xlsx
new file mode 100644
index 0000000..740d142
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels16.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels17.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels17.xlsx
new file mode 100644
index 0000000..ac29344
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels17.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels18.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels18.xlsx
new file mode 100644
index 0000000..fe691f3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels18.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels19.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels19.xlsx
new file mode 100644
index 0000000..50d338a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels19.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels20.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels20.xlsx
new file mode 100644
index 0000000..0bcb133
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels20.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels21.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels21.xlsx
new file mode 100644
index 0000000..96de31d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels21.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels22.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels22.xlsx
new file mode 100644
index 0000000..ce009ae
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels22.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels23.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels23.xlsx
new file mode 100644
index 0000000..f2b2faa
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels23.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels24.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels24.xlsx
new file mode 100644
index 0000000..37a696b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels24.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_data_labels25.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels25.xlsx
new file mode 100644
index 0000000..dbb9188
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_data_labels25.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_date01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_date01.xlsx
new file mode 100644
index 0000000..7c21e06
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_date01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_date02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_date02.xlsx
new file mode 100644
index 0000000..ed66ac5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_date02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_date03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_date03.xlsx
new file mode 100644
index 0000000..be97dc3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_date03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_date04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_date04.xlsx
new file mode 100644
index 0000000..456cb78
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_date04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_date05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_date05.xlsx
new file mode 100644
index 0000000..4fbece8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_date05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_display_units01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_display_units01.xlsx
new file mode 100644
index 0000000..6cd31c5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_display_units01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_display_units02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_display_units02.xlsx
new file mode 100644
index 0000000..824eef6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_display_units02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_display_units03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_display_units03.xlsx
new file mode 100644
index 0000000..a6b80ae
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_display_units03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_display_units04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_display_units04.xlsx
new file mode 100644
index 0000000..fe1df2d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_display_units04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_display_units05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_display_units05.xlsx
new file mode 100644
index 0000000..6d4a96b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_display_units05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_display_units06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_display_units06.xlsx
new file mode 100644
index 0000000..8257dba
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_display_units06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_display_units07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_display_units07.xlsx
new file mode 100644
index 0000000..18c79c9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_display_units07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_display_units08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_display_units08.xlsx
new file mode 100644
index 0000000..55966e4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_display_units08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_display_units09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_display_units09.xlsx
new file mode 100644
index 0000000..788839b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_display_units09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_display_units10.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_display_units10.xlsx
new file mode 100644
index 0000000..59caf5e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_display_units10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_display_units11.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_display_units11.xlsx
new file mode 100644
index 0000000..a6b3afa
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_display_units11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_display_units12.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_display_units12.xlsx
new file mode 100644
index 0000000..059b49e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_display_units12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_doughnut01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_doughnut01.xlsx
new file mode 100644
index 0000000..9c098a6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_doughnut01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_doughnut02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_doughnut02.xlsx
new file mode 100644
index 0000000..66e9474
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_doughnut02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_doughnut03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_doughnut03.xlsx
new file mode 100644
index 0000000..55af11a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_doughnut03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_doughnut04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_doughnut04.xlsx
new file mode 100644
index 0000000..6d9ee34
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_doughnut04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_doughnut05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_doughnut05.xlsx
new file mode 100644
index 0000000..007a32a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_doughnut05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_doughnut06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_doughnut06.xlsx
new file mode 100644
index 0000000..f41fc7a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_doughnut06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_drop_lines01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_drop_lines01.xlsx
new file mode 100644
index 0000000..f72fd7f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_drop_lines01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_drop_lines02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_drop_lines02.xlsx
new file mode 100644
index 0000000..13bf679
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_drop_lines02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_drop_lines03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_drop_lines03.xlsx
new file mode 100644
index 0000000..a917b1d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_drop_lines03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_drop_lines04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_drop_lines04.xlsx
new file mode 100644
index 0000000..b86a147
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_drop_lines04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_errorbars01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars01.xlsx
new file mode 100644
index 0000000..c8e607f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_errorbars02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars02.xlsx
new file mode 100644
index 0000000..2c78325
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_errorbars03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars03.xlsx
new file mode 100644
index 0000000..1b927d4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_errorbars04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars04.xlsx
new file mode 100644
index 0000000..0effdeb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_errorbars05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars05.xlsx
new file mode 100644
index 0000000..5424c82
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_errorbars06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars06.xlsx
new file mode 100644
index 0000000..83da839
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_errorbars07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars07.xlsx
new file mode 100644
index 0000000..43f03e3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_errorbars08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars08.xlsx
new file mode 100644
index 0000000..5c22cbb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_errorbars09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars09.xlsx
new file mode 100644
index 0000000..4e14d3e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_errorbars10.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars10.xlsx
new file mode 100644
index 0000000..02fc650
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_errorbars10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_font01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_font01.xlsx
new file mode 100644
index 0000000..dba2529
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_font01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_font02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_font02.xlsx
new file mode 100644
index 0000000..0e66996
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_font02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_font03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_font03.xlsx
new file mode 100644
index 0000000..d832f50
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_font03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_font04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_font04.xlsx
new file mode 100644
index 0000000..a58b5f5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_font04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_font05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_font05.xlsx
new file mode 100644
index 0000000..689bf4a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_font05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_font06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_font06.xlsx
new file mode 100644
index 0000000..f57a91b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_font06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_font07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_font07.xlsx
new file mode 100644
index 0000000..dfea4a4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_font07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_font08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_font08.xlsx
new file mode 100644
index 0000000..e29e0b3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_font08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_font09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_font09.xlsx
new file mode 100644
index 0000000..5bd2fdc
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_font09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format01.xlsx
new file mode 100644
index 0000000..53d3c11
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format02.xlsx
new file mode 100644
index 0000000..6431ea3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format03.xlsx
new file mode 100644
index 0000000..183dfa2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format04.xlsx
new file mode 100644
index 0000000..205a3a5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format05.xlsx
new file mode 100644
index 0000000..bc10ad2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format06.xlsx
new file mode 100644
index 0000000..5be6c42
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format07.xlsx
new file mode 100644
index 0000000..4f73f66
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format08.xlsx
new file mode 100644
index 0000000..cc9868b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format09.xlsx
new file mode 100644
index 0000000..347ac79
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format10.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format10.xlsx
new file mode 100644
index 0000000..c3db856
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format11.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format11.xlsx
new file mode 100644
index 0000000..d050dcc
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format12.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format12.xlsx
new file mode 100644
index 0000000..56fc02e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format13.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format13.xlsx
new file mode 100644
index 0000000..15fa603
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format13.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format14.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format14.xlsx
new file mode 100644
index 0000000..b235478
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format14.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format15.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format15.xlsx
new file mode 100644
index 0000000..29ed3a0
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format15.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format16.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format16.xlsx
new file mode 100644
index 0000000..ff71b2e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format16.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format17.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format17.xlsx
new file mode 100644
index 0000000..84ad958
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format17.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format18.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format18.xlsx
new file mode 100644
index 0000000..f47ec2a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format18.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format19.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format19.xlsx
new file mode 100644
index 0000000..b34eb00
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format19.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format20.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format20.xlsx
new file mode 100644
index 0000000..3dc47e6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format20.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format21.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format21.xlsx
new file mode 100644
index 0000000..138b720
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format21.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format22.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format22.xlsx
new file mode 100644
index 0000000..f4f9021
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format22.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format23.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format23.xlsx
new file mode 100644
index 0000000..dd390b7
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format23.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format24.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format24.xlsx
new file mode 100644
index 0000000..3bd2472
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format24.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format25.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format25.xlsx
new file mode 100644
index 0000000..1cd1754
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format25.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format26.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format26.xlsx
new file mode 100644
index 0000000..b2dace9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format26.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format27.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format27.xlsx
new file mode 100644
index 0000000..99220bb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format27.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format28.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format28.xlsx
new file mode 100644
index 0000000..9a837b5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format28.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format29.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format29.xlsx
new file mode 100644
index 0000000..c589f0d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format29.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format30.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format30.xlsx
new file mode 100644
index 0000000..ba0371e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format30.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_format31.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_format31.xlsx
new file mode 100644
index 0000000..5561ba8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_format31.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gap01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gap01.xlsx
new file mode 100644
index 0000000..adacec2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gap01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gap02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gap02.xlsx
new file mode 100644
index 0000000..c982651
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gap02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gap03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gap03.xlsx
new file mode 100644
index 0000000..4b52a4b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gap03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gap04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gap04.xlsx
new file mode 100644
index 0000000..cf57e4a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gap04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gap05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gap05.xlsx
new file mode 100644
index 0000000..7f6625c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gap05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gradient01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gradient01.xlsx
new file mode 100644
index 0000000..a0b029d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gradient01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gradient02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gradient02.xlsx
new file mode 100644
index 0000000..ee25012
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gradient02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gradient03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gradient03.xlsx
new file mode 100644
index 0000000..e371041
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gradient03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gradient04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gradient04.xlsx
new file mode 100644
index 0000000..0026dcc
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gradient04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gradient05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gradient05.xlsx
new file mode 100644
index 0000000..ccf72cc
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gradient05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gradient06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gradient06.xlsx
new file mode 100644
index 0000000..2447dc5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gradient06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gradient07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gradient07.xlsx
new file mode 100644
index 0000000..9dfe0b9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gradient07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gradient08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gradient08.xlsx
new file mode 100644
index 0000000..79deff8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gradient08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gradient09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gradient09.xlsx
new file mode 100644
index 0000000..7095ce4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gradient09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gradient10.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gradient10.xlsx
new file mode 100644
index 0000000..d33bc55
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gradient10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gradient11.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gradient11.xlsx
new file mode 100644
index 0000000..73aed36
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gradient11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gradient12.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gradient12.xlsx
new file mode 100644
index 0000000..1a1e07e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gradient12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gradient13.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gradient13.xlsx
new file mode 100644
index 0000000..981cd9c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gradient13.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gridlines01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines01.xlsx
new file mode 100644
index 0000000..dbcdf24
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gridlines02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines02.xlsx
new file mode 100644
index 0000000..8414bf4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gridlines03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines03.xlsx
new file mode 100644
index 0000000..0fbbcb1
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gridlines04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines04.xlsx
new file mode 100644
index 0000000..986193c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gridlines05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines05.xlsx
new file mode 100644
index 0000000..a2e7a96
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gridlines06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines06.xlsx
new file mode 100644
index 0000000..1313c57
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gridlines07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines07.xlsx
new file mode 100644
index 0000000..dad5b71
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gridlines08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines08.xlsx
new file mode 100644
index 0000000..74e22d2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_gridlines09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines09.xlsx
new file mode 100644
index 0000000..00ce4b2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_gridlines09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_high_low_lines01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_high_low_lines01.xlsx
new file mode 100644
index 0000000..9655cd3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_high_low_lines01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_high_low_lines02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_high_low_lines02.xlsx
new file mode 100644
index 0000000..93bdd38
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_high_low_lines02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_layout01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_layout01.xlsx
new file mode 100644
index 0000000..6e123b2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_layout01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_layout02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_layout02.xlsx
new file mode 100644
index 0000000..1b93dcc
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_layout02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_layout03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_layout03.xlsx
new file mode 100644
index 0000000..cce4b03
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_layout03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_layout04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_layout04.xlsx
new file mode 100644
index 0000000..1e60412
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_layout04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_layout05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_layout05.xlsx
new file mode 100644
index 0000000..78b54fb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_layout05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_layout06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_layout06.xlsx
new file mode 100644
index 0000000..cc9e56b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_layout06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_layout07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_layout07.xlsx
new file mode 100644
index 0000000..2b39e5d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_layout07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_layout08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_layout08.xlsx
new file mode 100644
index 0000000..4399e55
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_layout08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_legend01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_legend01.xlsx
new file mode 100644
index 0000000..ff7e934
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_legend01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_line01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_line01.xlsx
new file mode 100644
index 0000000..92ca45a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_line01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_line02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_line02.xlsx
new file mode 100644
index 0000000..842fe30
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_line02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_line03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_line03.xlsx
new file mode 100644
index 0000000..ae5353c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_line03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_line04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_line04.xlsx
new file mode 100644
index 0000000..a15a55a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_line04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_name01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_name01.xlsx
new file mode 100644
index 0000000..13a4c17
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_name01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_name02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_name02.xlsx
new file mode 100644
index 0000000..7ecd923
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_name02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_name03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_name03.xlsx
new file mode 100644
index 0000000..7ecd923
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_name03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_order01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_order01.xlsx
new file mode 100644
index 0000000..6e84a93
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_order01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pattern01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pattern01.xlsx
new file mode 100644
index 0000000..b3870c5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pattern01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pattern02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pattern02.xlsx
new file mode 100644
index 0000000..b39a73a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pattern02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pattern03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pattern03.xlsx
new file mode 100644
index 0000000..e0159e0
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pattern03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pattern04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pattern04.xlsx
new file mode 100644
index 0000000..443d402
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pattern04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pattern05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pattern05.xlsx
new file mode 100644
index 0000000..7cb719a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pattern05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pattern06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pattern06.xlsx
new file mode 100644
index 0000000..2f2be18
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pattern06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pattern07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pattern07.xlsx
new file mode 100644
index 0000000..343ca5e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pattern07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pattern08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pattern08.xlsx
new file mode 100644
index 0000000..83012cb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pattern08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pattern09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pattern09.xlsx
new file mode 100644
index 0000000..9021ef6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pattern09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pattern10.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pattern10.xlsx
new file mode 100644
index 0000000..2743d3c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pattern10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pie01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pie01.xlsx
new file mode 100644
index 0000000..11a4423
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pie01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pie02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pie02.xlsx
new file mode 100644
index 0000000..6f57dd2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pie02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pie03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pie03.xlsx
new file mode 100644
index 0000000..542e1ac
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pie03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pie04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pie04.xlsx
new file mode 100644
index 0000000..5c92828
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pie04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_pie05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_pie05.xlsx
new file mode 100644
index 0000000..e78d229
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_pie05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_points01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_points01.xlsx
new file mode 100644
index 0000000..cf0f457
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_points01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_points02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_points02.xlsx
new file mode 100644
index 0000000..bc7675d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_points02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_points03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_points03.xlsx
new file mode 100644
index 0000000..4439ffb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_points03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_points04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_points04.xlsx
new file mode 100644
index 0000000..fea1128
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_points04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_points05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_points05.xlsx
new file mode 100644
index 0000000..690498d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_points05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_points06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_points06.xlsx
new file mode 100644
index 0000000..db522d5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_points06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_radar01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_radar01.xlsx
new file mode 100644
index 0000000..ee36d31
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_radar01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_radar02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_radar02.xlsx
new file mode 100644
index 0000000..c978925
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_radar02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_radar03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_radar03.xlsx
new file mode 100644
index 0000000..9410079
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_radar03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_scatter01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_scatter01.xlsx
new file mode 100644
index 0000000..0850fd2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_scatter01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_scatter02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_scatter02.xlsx
new file mode 100644
index 0000000..192ab56
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_scatter02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_scatter03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_scatter03.xlsx
new file mode 100644
index 0000000..34779ce
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_scatter03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_scatter04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_scatter04.xlsx
new file mode 100644
index 0000000..d9a88a6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_scatter04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_scatter05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_scatter05.xlsx
new file mode 100644
index 0000000..4347e9d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_scatter05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_scatter06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_scatter06.xlsx
new file mode 100644
index 0000000..c9e2106
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_scatter06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_scatter07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_scatter07.xlsx
new file mode 100644
index 0000000..76b387f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_scatter07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_scatter09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_scatter09.xlsx
new file mode 100644
index 0000000..ecf8aed
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_scatter09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_scatter10.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_scatter10.xlsx
new file mode 100644
index 0000000..dc03d46
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_scatter10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_scatter11.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_scatter11.xlsx
new file mode 100644
index 0000000..b722a7f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_scatter11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_scatter12.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_scatter12.xlsx
new file mode 100644
index 0000000..3cc1ef9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_scatter12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_scatter14.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_scatter14.xlsx
new file mode 100644
index 0000000..e21beed
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_scatter14.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_scatter15.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_scatter15.xlsx
new file mode 100644
index 0000000..958174e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_scatter15.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_size01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_size01.xlsx
new file mode 100644
index 0000000..09d6051
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_size01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_size04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_size04.xlsx
new file mode 100644
index 0000000..f177530
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_size04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_sparse01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_sparse01.xlsx
new file mode 100644
index 0000000..7872281
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_sparse01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_stock01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_stock01.xlsx
new file mode 100644
index 0000000..7f0c368
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_stock01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_stock02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_stock02.xlsx
new file mode 100644
index 0000000..a29fa08
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_stock02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_str01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_str01.xlsx
new file mode 100644
index 0000000..e5739a0
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_str01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_str02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_str02.xlsx
new file mode 100644
index 0000000..4a80fbe
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_str02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_table01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_table01.xlsx
new file mode 100644
index 0000000..cce7642
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_table01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_table02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_table02.xlsx
new file mode 100644
index 0000000..e721c1e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_table02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_table03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_table03.xlsx
new file mode 100644
index 0000000..a0cc3e0
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_table03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_title01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_title01.xlsx
new file mode 100644
index 0000000..5342878
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_title01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_title02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_title02.xlsx
new file mode 100644
index 0000000..e3fb8f4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_title02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_up_down_bars01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_up_down_bars01.xlsx
new file mode 100644
index 0000000..105efb3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_up_down_bars01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_up_down_bars02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_up_down_bars02.xlsx
new file mode 100644
index 0000000..40de735
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_up_down_bars02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chart_up_down_bars03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chart_up_down_bars03.xlsx
new file mode 100644
index 0000000..e3e287f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chart_up_down_bars03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chartsheet01.xlsx b/xlsxwriter/test/comparison/xlsx_files/chartsheet01.xlsx
new file mode 100644
index 0000000..a787271
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chartsheet01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chartsheet02.xlsx b/xlsxwriter/test/comparison/xlsx_files/chartsheet02.xlsx
new file mode 100644
index 0000000..20de114
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chartsheet02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chartsheet03.xlsx b/xlsxwriter/test/comparison/xlsx_files/chartsheet03.xlsx
new file mode 100644
index 0000000..292147b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chartsheet03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chartsheet04.xlsx b/xlsxwriter/test/comparison/xlsx_files/chartsheet04.xlsx
new file mode 100644
index 0000000..a4d8de2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chartsheet04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chartsheet05.xlsx b/xlsxwriter/test/comparison/xlsx_files/chartsheet05.xlsx
new file mode 100644
index 0000000..1170f5c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chartsheet05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chartsheet06.xlsx b/xlsxwriter/test/comparison/xlsx_files/chartsheet06.xlsx
new file mode 100644
index 0000000..b529fbd
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chartsheet06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chartsheet07.xlsx b/xlsxwriter/test/comparison/xlsx_files/chartsheet07.xlsx
new file mode 100644
index 0000000..3507d20
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chartsheet07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chartsheet08.xlsx b/xlsxwriter/test/comparison/xlsx_files/chartsheet08.xlsx
new file mode 100644
index 0000000..f31479b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chartsheet08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/chartsheet09.xlsx b/xlsxwriter/test/comparison/xlsx_files/chartsheet09.xlsx
new file mode 100644
index 0000000..8470389
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/chartsheet09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/comment01.xlsx b/xlsxwriter/test/comparison/xlsx_files/comment01.xlsx
new file mode 100644
index 0000000..69a9c20
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/comment01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/comment02.xlsx b/xlsxwriter/test/comparison/xlsx_files/comment02.xlsx
new file mode 100644
index 0000000..d461471
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/comment02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/comment03.xlsx b/xlsxwriter/test/comparison/xlsx_files/comment03.xlsx
new file mode 100644
index 0000000..b8c9c38
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/comment03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/comment04.xlsx b/xlsxwriter/test/comparison/xlsx_files/comment04.xlsx
new file mode 100644
index 0000000..2bc1bcd
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/comment04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/comment05.xlsx b/xlsxwriter/test/comparison/xlsx_files/comment05.xlsx
new file mode 100644
index 0000000..cf13891
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/comment05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/comment06.xlsx b/xlsxwriter/test/comparison/xlsx_files/comment06.xlsx
new file mode 100644
index 0000000..4948708
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/comment06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/comment07.xlsx b/xlsxwriter/test/comparison/xlsx_files/comment07.xlsx
new file mode 100644
index 0000000..4066c94
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/comment07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/comment08.xlsx b/xlsxwriter/test/comparison/xlsx_files/comment08.xlsx
new file mode 100644
index 0000000..92ee26b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/comment08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/comment09.xlsx b/xlsxwriter/test/comparison/xlsx_files/comment09.xlsx
new file mode 100644
index 0000000..192b443
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/comment09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/comment10.xlsx b/xlsxwriter/test/comparison/xlsx_files/comment10.xlsx
new file mode 100644
index 0000000..ee9410a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/comment10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/comment12.xlsx b/xlsxwriter/test/comparison/xlsx_files/comment12.xlsx
new file mode 100644
index 0000000..5747b39
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/comment12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/cond_format01.xlsx b/xlsxwriter/test/comparison/xlsx_files/cond_format01.xlsx
new file mode 100644
index 0000000..b75d30c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/cond_format01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/cond_format02.xlsx b/xlsxwriter/test/comparison/xlsx_files/cond_format02.xlsx
new file mode 100644
index 0000000..6fb24a0
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/cond_format02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/cond_format03.xlsx b/xlsxwriter/test/comparison/xlsx_files/cond_format03.xlsx
new file mode 100644
index 0000000..999a880
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/cond_format03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/cond_format04.xlsx b/xlsxwriter/test/comparison/xlsx_files/cond_format04.xlsx
new file mode 100644
index 0000000..8767fc6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/cond_format04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/cond_format05.xlsx b/xlsxwriter/test/comparison/xlsx_files/cond_format05.xlsx
new file mode 100644
index 0000000..54df54e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/cond_format05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/cond_format06.xlsx b/xlsxwriter/test/comparison/xlsx_files/cond_format06.xlsx
new file mode 100644
index 0000000..a2a9fe0
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/cond_format06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/cond_format07.xlsx b/xlsxwriter/test/comparison/xlsx_files/cond_format07.xlsx
new file mode 100644
index 0000000..a999aa5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/cond_format07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/cond_format08.xlsx b/xlsxwriter/test/comparison/xlsx_files/cond_format08.xlsx
new file mode 100644
index 0000000..81283d3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/cond_format08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/cond_format10.xlsx b/xlsxwriter/test/comparison/xlsx_files/cond_format10.xlsx
new file mode 100644
index 0000000..5554003
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/cond_format10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/cond_format11.xlsx b/xlsxwriter/test/comparison/xlsx_files/cond_format11.xlsx
new file mode 100644
index 0000000..4bd1c1c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/cond_format11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/cond_format12.xlsx b/xlsxwriter/test/comparison/xlsx_files/cond_format12.xlsx
new file mode 100644
index 0000000..2f6be28
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/cond_format12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/cond_format13.xlsx b/xlsxwriter/test/comparison/xlsx_files/cond_format13.xlsx
new file mode 100644
index 0000000..bbd7f88
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/cond_format13.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/data_validation01.xlsx b/xlsxwriter/test/comparison/xlsx_files/data_validation01.xlsx
new file mode 100644
index 0000000..eead063
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/data_validation01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/data_validation02.xlsx b/xlsxwriter/test/comparison/xlsx_files/data_validation02.xlsx
new file mode 100644
index 0000000..f86bc26
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/data_validation02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/data_validation03.xlsx b/xlsxwriter/test/comparison/xlsx_files/data_validation03.xlsx
new file mode 100644
index 0000000..627831b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/data_validation03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/data_validation07.xlsx b/xlsxwriter/test/comparison/xlsx_files/data_validation07.xlsx
new file mode 100644
index 0000000..856eaa8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/data_validation07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/data_validation08.xlsx b/xlsxwriter/test/comparison/xlsx_files/data_validation08.xlsx
new file mode 100644
index 0000000..187a49e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/data_validation08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/date_1904_01.xlsx b/xlsxwriter/test/comparison/xlsx_files/date_1904_01.xlsx
new file mode 100644
index 0000000..912fb1b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/date_1904_01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/date_1904_02.xlsx b/xlsxwriter/test/comparison/xlsx_files/date_1904_02.xlsx
new file mode 100644
index 0000000..b61c97f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/date_1904_02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/date_examples01.xlsx b/xlsxwriter/test/comparison/xlsx_files/date_examples01.xlsx
new file mode 100644
index 0000000..cf5c626
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/date_examples01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/default_date_format01.xlsx b/xlsxwriter/test/comparison/xlsx_files/default_date_format01.xlsx
new file mode 100644
index 0000000..2456ee5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/default_date_format01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/default_date_format02.xlsx b/xlsxwriter/test/comparison/xlsx_files/default_date_format02.xlsx
new file mode 100644
index 0000000..9118044
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/default_date_format02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/default_format01.xlsx b/xlsxwriter/test/comparison/xlsx_files/default_format01.xlsx
new file mode 100644
index 0000000..e8673e4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/default_format01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/default_row01.xlsx b/xlsxwriter/test/comparison/xlsx_files/default_row01.xlsx
new file mode 100644
index 0000000..7ed503d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/default_row01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/default_row02.xlsx b/xlsxwriter/test/comparison/xlsx_files/default_row02.xlsx
new file mode 100644
index 0000000..53342fa
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/default_row02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/default_row03.xlsx b/xlsxwriter/test/comparison/xlsx_files/default_row03.xlsx
new file mode 100644
index 0000000..d224304
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/default_row03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/default_row04.xlsx b/xlsxwriter/test/comparison/xlsx_files/default_row04.xlsx
new file mode 100644
index 0000000..d334842
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/default_row04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/default_row05.xlsx b/xlsxwriter/test/comparison/xlsx_files/default_row05.xlsx
new file mode 100644
index 0000000..f8c409f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/default_row05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/defined_name01.xlsx b/xlsxwriter/test/comparison/xlsx_files/defined_name01.xlsx
new file mode 100644
index 0000000..2574e21
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/defined_name01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/defined_name02.xlsx b/xlsxwriter/test/comparison/xlsx_files/defined_name02.xlsx
new file mode 100644
index 0000000..43ead40
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/defined_name02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/defined_name03.xlsx b/xlsxwriter/test/comparison/xlsx_files/defined_name03.xlsx
new file mode 100644
index 0000000..fe7132d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/defined_name03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/defined_name04.xlsx b/xlsxwriter/test/comparison/xlsx_files/defined_name04.xlsx
new file mode 100644
index 0000000..516fe37
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/defined_name04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/escapes01.xlsx b/xlsxwriter/test/comparison/xlsx_files/escapes01.xlsx
new file mode 100644
index 0000000..46df0cc
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/escapes01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/escapes02.xlsx b/xlsxwriter/test/comparison/xlsx_files/escapes02.xlsx
new file mode 100644
index 0000000..cb2c59f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/escapes02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/escapes03.xlsx b/xlsxwriter/test/comparison/xlsx_files/escapes03.xlsx
new file mode 100644
index 0000000..cbe665f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/escapes03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/escapes04.xlsx b/xlsxwriter/test/comparison/xlsx_files/escapes04.xlsx
new file mode 100644
index 0000000..ebd1a73
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/escapes04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/escapes05.xlsx b/xlsxwriter/test/comparison/xlsx_files/escapes05.xlsx
new file mode 100644
index 0000000..9a7db5c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/escapes05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/escapes06.xlsx b/xlsxwriter/test/comparison/xlsx_files/escapes06.xlsx
new file mode 100644
index 0000000..3a9b230
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/escapes06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/escapes07.xlsx b/xlsxwriter/test/comparison/xlsx_files/escapes07.xlsx
new file mode 100644
index 0000000..f1b8143
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/escapes07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/escapes08.xlsx b/xlsxwriter/test/comparison/xlsx_files/escapes08.xlsx
new file mode 100644
index 0000000..11e0ba4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/escapes08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/excel2003_style01.xlsx b/xlsxwriter/test/comparison/xlsx_files/excel2003_style01.xlsx
new file mode 100644
index 0000000..544a483
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/excel2003_style01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/excel2003_style02.xlsx b/xlsxwriter/test/comparison/xlsx_files/excel2003_style02.xlsx
new file mode 100644
index 0000000..4b2b53f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/excel2003_style02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/excel2003_style03.xlsx b/xlsxwriter/test/comparison/xlsx_files/excel2003_style03.xlsx
new file mode 100644
index 0000000..2e98ae8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/excel2003_style03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/excel2003_style04.xlsx b/xlsxwriter/test/comparison/xlsx_files/excel2003_style04.xlsx
new file mode 100644
index 0000000..eaeef64
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/excel2003_style04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/excel2003_style05.xlsx b/xlsxwriter/test/comparison/xlsx_files/excel2003_style05.xlsx
new file mode 100644
index 0000000..6a35378
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/excel2003_style05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/excel2003_style06.xlsx b/xlsxwriter/test/comparison/xlsx_files/excel2003_style06.xlsx
new file mode 100644
index 0000000..86ea9fd
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/excel2003_style06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/excel2003_style07.xlsx b/xlsxwriter/test/comparison/xlsx_files/excel2003_style07.xlsx
new file mode 100644
index 0000000..edf0679
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/excel2003_style07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/excel2003_style08.xlsx b/xlsxwriter/test/comparison/xlsx_files/excel2003_style08.xlsx
new file mode 100644
index 0000000..c78bc6e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/excel2003_style08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/firstsheet01.xlsx b/xlsxwriter/test/comparison/xlsx_files/firstsheet01.xlsx
new file mode 100644
index 0000000..10463f2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/firstsheet01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/fit_to_pages01.xlsx b/xlsxwriter/test/comparison/xlsx_files/fit_to_pages01.xlsx
new file mode 100644
index 0000000..8d3a8df
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/fit_to_pages01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/fit_to_pages02.xlsx b/xlsxwriter/test/comparison/xlsx_files/fit_to_pages02.xlsx
new file mode 100644
index 0000000..2c687fb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/fit_to_pages02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/fit_to_pages03.xlsx b/xlsxwriter/test/comparison/xlsx_files/fit_to_pages03.xlsx
new file mode 100644
index 0000000..3dc39c3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/fit_to_pages03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/fit_to_pages04.xlsx b/xlsxwriter/test/comparison/xlsx_files/fit_to_pages04.xlsx
new file mode 100644
index 0000000..55ff211
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/fit_to_pages04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/fit_to_pages05.xlsx b/xlsxwriter/test/comparison/xlsx_files/fit_to_pages05.xlsx
new file mode 100644
index 0000000..131a6ae
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/fit_to_pages05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/format01.xlsx b/xlsxwriter/test/comparison/xlsx_files/format01.xlsx
new file mode 100644
index 0000000..be7a8c9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/format01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/format11.xlsx b/xlsxwriter/test/comparison/xlsx_files/format11.xlsx
new file mode 100644
index 0000000..1b3e7ac
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/format11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/format12.xlsx b/xlsxwriter/test/comparison/xlsx_files/format12.xlsx
new file mode 100644
index 0000000..c5ecb25
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/format12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/format13.xlsx b/xlsxwriter/test/comparison/xlsx_files/format13.xlsx
new file mode 100644
index 0000000..4235967
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/format13.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/format14.xlsx b/xlsxwriter/test/comparison/xlsx_files/format14.xlsx
new file mode 100644
index 0000000..b5d6c37
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/format14.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/formula_results01.xlsx b/xlsxwriter/test/comparison/xlsx_files/formula_results01.xlsx
new file mode 100644
index 0000000..7b797f5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/formula_results01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header01.xlsx b/xlsxwriter/test/comparison/xlsx_files/header01.xlsx
new file mode 100644
index 0000000..bf57bb9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header02.xlsx b/xlsxwriter/test/comparison/xlsx_files/header02.xlsx
new file mode 100644
index 0000000..edd12cf
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header03.xlsx b/xlsxwriter/test/comparison/xlsx_files/header03.xlsx
new file mode 100644
index 0000000..5d195ab
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image01.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image01.xlsx
new file mode 100644
index 0000000..64df3f1
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image02.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image02.xlsx
new file mode 100644
index 0000000..62af382
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image03.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image03.xlsx
new file mode 100644
index 0000000..dfa974b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image04.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image04.xlsx
new file mode 100644
index 0000000..0b923ce
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image05.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image05.xlsx
new file mode 100644
index 0000000..69d9e86
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image06.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image06.xlsx
new file mode 100644
index 0000000..6577612
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image07.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image07.xlsx
new file mode 100644
index 0000000..6af8e2c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image08.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image08.xlsx
new file mode 100644
index 0000000..17d8ba2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image09.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image09.xlsx
new file mode 100644
index 0000000..4d5e687
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image10.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image10.xlsx
new file mode 100644
index 0000000..1cee71c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image11.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image11.xlsx
new file mode 100644
index 0000000..47e447f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image12.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image12.xlsx
new file mode 100644
index 0000000..727a862
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image13.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image13.xlsx
new file mode 100644
index 0000000..4a5627e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image13.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/header_image14.xlsx b/xlsxwriter/test/comparison/xlsx_files/header_image14.xlsx
new file mode 100644
index 0000000..5791035
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/header_image14.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hide01.xlsx b/xlsxwriter/test/comparison/xlsx_files/hide01.xlsx
new file mode 100644
index 0000000..340db34
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hide01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink01.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink01.xlsx
new file mode 100644
index 0000000..b78809f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink02.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink02.xlsx
new file mode 100644
index 0000000..1ce8ade
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink03.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink03.xlsx
new file mode 100644
index 0000000..4c150c7
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink04.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink04.xlsx
new file mode 100644
index 0000000..547a8e7
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink05.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink05.xlsx
new file mode 100644
index 0000000..58dc5f7
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink06.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink06.xlsx
new file mode 100644
index 0000000..f72b9c9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink07.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink07.xlsx
new file mode 100644
index 0000000..9dee4b9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink08.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink08.xlsx
new file mode 100644
index 0000000..9dee4b9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink09.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink09.xlsx
new file mode 100644
index 0000000..5e56d08
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink10.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink10.xlsx
new file mode 100644
index 0000000..f7d1b10
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink11.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink11.xlsx
new file mode 100644
index 0000000..1a039ed
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink12.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink12.xlsx
new file mode 100644
index 0000000..053f832
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink13.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink13.xlsx
new file mode 100644
index 0000000..2eb028f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink13.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink14.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink14.xlsx
new file mode 100644
index 0000000..ace9810
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink14.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink15.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink15.xlsx
new file mode 100644
index 0000000..2030ca6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink15.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink16.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink16.xlsx
new file mode 100644
index 0000000..4856abf
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink16.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink17.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink17.xlsx
new file mode 100644
index 0000000..8863fa6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink17.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink18.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink18.xlsx
new file mode 100644
index 0000000..6ed4529
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink18.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink19.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink19.xlsx
new file mode 100644
index 0000000..455ad5f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink19.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink20.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink20.xlsx
new file mode 100644
index 0000000..7b5021e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink20.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink21.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink21.xlsx
new file mode 100644
index 0000000..dce27bf
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink21.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink22.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink22.xlsx
new file mode 100644
index 0000000..dad27ac
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink22.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink23.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink23.xlsx
new file mode 100644
index 0000000..4f0d38d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink23.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink24.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink24.xlsx
new file mode 100644
index 0000000..b456179
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink24.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink25.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink25.xlsx
new file mode 100644
index 0000000..cf07450
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink25.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink26.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink26.xlsx
new file mode 100644
index 0000000..5b6f955
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink26.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/hyperlink27.xlsx b/xlsxwriter/test/comparison/xlsx_files/hyperlink27.xlsx
new file mode 100644
index 0000000..6d3253f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/hyperlink27.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image01.xlsx b/xlsxwriter/test/comparison/xlsx_files/image01.xlsx
new file mode 100644
index 0000000..b7314d7
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image02.xlsx b/xlsxwriter/test/comparison/xlsx_files/image02.xlsx
new file mode 100644
index 0000000..a4316a8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image03.xlsx b/xlsxwriter/test/comparison/xlsx_files/image03.xlsx
new file mode 100644
index 0000000..c6889d7
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image04.xlsx b/xlsxwriter/test/comparison/xlsx_files/image04.xlsx
new file mode 100644
index 0000000..538918a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image05.xlsx b/xlsxwriter/test/comparison/xlsx_files/image05.xlsx
new file mode 100644
index 0000000..3d7396f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image07.xlsx b/xlsxwriter/test/comparison/xlsx_files/image07.xlsx
new file mode 100644
index 0000000..511911d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image08.xlsx b/xlsxwriter/test/comparison/xlsx_files/image08.xlsx
new file mode 100644
index 0000000..2b72925
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image09.xlsx b/xlsxwriter/test/comparison/xlsx_files/image09.xlsx
new file mode 100644
index 0000000..1daab83
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image10.xlsx b/xlsxwriter/test/comparison/xlsx_files/image10.xlsx
new file mode 100644
index 0000000..19f3b96
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image11.xlsx b/xlsxwriter/test/comparison/xlsx_files/image11.xlsx
new file mode 100644
index 0000000..8a8635d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image12.xlsx b/xlsxwriter/test/comparison/xlsx_files/image12.xlsx
new file mode 100644
index 0000000..f00c32f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image13.xlsx b/xlsxwriter/test/comparison/xlsx_files/image13.xlsx
new file mode 100644
index 0000000..7973bf4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image13.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image14.xlsx b/xlsxwriter/test/comparison/xlsx_files/image14.xlsx
new file mode 100644
index 0000000..54ddb1e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image14.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image15.xlsx b/xlsxwriter/test/comparison/xlsx_files/image15.xlsx
new file mode 100644
index 0000000..99928a6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image15.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image16.xlsx b/xlsxwriter/test/comparison/xlsx_files/image16.xlsx
new file mode 100644
index 0000000..ff97b8f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image16.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image17.xlsx b/xlsxwriter/test/comparison/xlsx_files/image17.xlsx
new file mode 100644
index 0000000..ea2e671
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image17.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image18.xlsx b/xlsxwriter/test/comparison/xlsx_files/image18.xlsx
new file mode 100644
index 0000000..4bdd7a2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image18.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image19.xlsx b/xlsxwriter/test/comparison/xlsx_files/image19.xlsx
new file mode 100644
index 0000000..64ff774
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image19.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image20.xlsx b/xlsxwriter/test/comparison/xlsx_files/image20.xlsx
new file mode 100644
index 0000000..4e96783
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image20.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image21.xlsx b/xlsxwriter/test/comparison/xlsx_files/image21.xlsx
new file mode 100644
index 0000000..abb4601
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image21.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image22.xlsx b/xlsxwriter/test/comparison/xlsx_files/image22.xlsx
new file mode 100644
index 0000000..cc89a5b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image22.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image23.xlsx b/xlsxwriter/test/comparison/xlsx_files/image23.xlsx
new file mode 100644
index 0000000..df349f3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image23.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image24.xlsx b/xlsxwriter/test/comparison/xlsx_files/image24.xlsx
new file mode 100644
index 0000000..f3bf36c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image24.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image25.xlsx b/xlsxwriter/test/comparison/xlsx_files/image25.xlsx
new file mode 100644
index 0000000..fc910f1
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image25.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image26.xlsx b/xlsxwriter/test/comparison/xlsx_files/image26.xlsx
new file mode 100644
index 0000000..d9a90e4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image26.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image27.xlsx b/xlsxwriter/test/comparison/xlsx_files/image27.xlsx
new file mode 100644
index 0000000..1aa4cbb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image27.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image28.xlsx b/xlsxwriter/test/comparison/xlsx_files/image28.xlsx
new file mode 100644
index 0000000..d54d953
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image28.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image29.xlsx b/xlsxwriter/test/comparison/xlsx_files/image29.xlsx
new file mode 100644
index 0000000..d54d953
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image29.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image30.xlsx b/xlsxwriter/test/comparison/xlsx_files/image30.xlsx
new file mode 100644
index 0000000..715d2ba
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image30.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image31.xlsx b/xlsxwriter/test/comparison/xlsx_files/image31.xlsx
new file mode 100644
index 0000000..25426ab
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image31.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image32.xlsx b/xlsxwriter/test/comparison/xlsx_files/image32.xlsx
new file mode 100644
index 0000000..a3dfa2d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image32.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image33.xlsx b/xlsxwriter/test/comparison/xlsx_files/image33.xlsx
new file mode 100644
index 0000000..d2e6525
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image33.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image34.xlsx b/xlsxwriter/test/comparison/xlsx_files/image34.xlsx
new file mode 100644
index 0000000..9fbea52
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image34.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image35.xlsx b/xlsxwriter/test/comparison/xlsx_files/image35.xlsx
new file mode 100644
index 0000000..a9c9a0c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image35.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image_anchor01.xlsx b/xlsxwriter/test/comparison/xlsx_files/image_anchor01.xlsx
new file mode 100644
index 0000000..079d2d3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image_anchor01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image_anchor02.xlsx b/xlsxwriter/test/comparison/xlsx_files/image_anchor02.xlsx
new file mode 100644
index 0000000..51f8538
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image_anchor02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image_anchor03.xlsx b/xlsxwriter/test/comparison/xlsx_files/image_anchor03.xlsx
new file mode 100644
index 0000000..8404893
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image_anchor03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image_anchor04.xlsx b/xlsxwriter/test/comparison/xlsx_files/image_anchor04.xlsx
new file mode 100644
index 0000000..a2e024f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image_anchor04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image_anchor05.xlsx b/xlsxwriter/test/comparison/xlsx_files/image_anchor05.xlsx
new file mode 100644
index 0000000..b0b4d41
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image_anchor05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image_anchor06.xlsx b/xlsxwriter/test/comparison/xlsx_files/image_anchor06.xlsx
new file mode 100644
index 0000000..1820345
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image_anchor06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/image_anchor07.xlsx b/xlsxwriter/test/comparison/xlsx_files/image_anchor07.xlsx
new file mode 100644
index 0000000..0984656
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/image_anchor07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/landscape01.xlsx b/xlsxwriter/test/comparison/xlsx_files/landscape01.xlsx
new file mode 100644
index 0000000..f606427
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/landscape01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/macro01.xlsm b/xlsxwriter/test/comparison/xlsx_files/macro01.xlsm
new file mode 100644
index 0000000..4550cf2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/macro01.xlsm differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/merge_cells01.xlsx b/xlsxwriter/test/comparison/xlsx_files/merge_cells01.xlsx
new file mode 100644
index 0000000..9f92e79
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/merge_cells01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/merge_range01.xlsx b/xlsxwriter/test/comparison/xlsx_files/merge_range01.xlsx
new file mode 100644
index 0000000..bb4b1ca
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/merge_range01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/merge_range02.xlsx b/xlsxwriter/test/comparison/xlsx_files/merge_range02.xlsx
new file mode 100644
index 0000000..8a54562
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/merge_range02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/merge_range03.xlsx b/xlsxwriter/test/comparison/xlsx_files/merge_range03.xlsx
new file mode 100644
index 0000000..9ca46d5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/merge_range03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/merge_range04.xlsx b/xlsxwriter/test/comparison/xlsx_files/merge_range04.xlsx
new file mode 100644
index 0000000..f7a9977
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/merge_range04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/merge_range05.xlsx b/xlsxwriter/test/comparison/xlsx_files/merge_range05.xlsx
new file mode 100644
index 0000000..4c8dd5c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/merge_range05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/optimize01.xlsx b/xlsxwriter/test/comparison/xlsx_files/optimize01.xlsx
new file mode 100644
index 0000000..5d776e3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/optimize01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/optimize02.xlsx b/xlsxwriter/test/comparison/xlsx_files/optimize02.xlsx
new file mode 100644
index 0000000..5d776e3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/optimize02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/optimize03.xlsx b/xlsxwriter/test/comparison/xlsx_files/optimize03.xlsx
new file mode 100644
index 0000000..a3d19dc
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/optimize03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/optimize04.xlsx b/xlsxwriter/test/comparison/xlsx_files/optimize04.xlsx
new file mode 100644
index 0000000..2edb2bb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/optimize04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/optimize05.xlsx b/xlsxwriter/test/comparison/xlsx_files/optimize05.xlsx
new file mode 100644
index 0000000..d79bed2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/optimize05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/optimize06.xlsx b/xlsxwriter/test/comparison/xlsx_files/optimize06.xlsx
new file mode 100644
index 0000000..5fc906e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/optimize06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/optimize07.xlsx b/xlsxwriter/test/comparison/xlsx_files/optimize07.xlsx
new file mode 100644
index 0000000..3df49de
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/optimize07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/optimize08.xlsx b/xlsxwriter/test/comparison/xlsx_files/optimize08.xlsx
new file mode 100644
index 0000000..dc3dbdd
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/optimize08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/optimize09.xlsx b/xlsxwriter/test/comparison/xlsx_files/optimize09.xlsx
new file mode 100644
index 0000000..daacb19
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/optimize09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/optimize10.xlsx b/xlsxwriter/test/comparison/xlsx_files/optimize10.xlsx
new file mode 100644
index 0000000..3936786
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/optimize10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/optimize11.xlsx b/xlsxwriter/test/comparison/xlsx_files/optimize11.xlsx
new file mode 100644
index 0000000..e271606
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/optimize11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/outline01.xlsx b/xlsxwriter/test/comparison/xlsx_files/outline01.xlsx
new file mode 100644
index 0000000..926b013
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/outline01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/outline02.xlsx b/xlsxwriter/test/comparison/xlsx_files/outline02.xlsx
new file mode 100644
index 0000000..97d8b80
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/outline02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/outline03.xlsx b/xlsxwriter/test/comparison/xlsx_files/outline03.xlsx
new file mode 100644
index 0000000..22f2682
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/outline03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/outline04.xlsx b/xlsxwriter/test/comparison/xlsx_files/outline04.xlsx
new file mode 100644
index 0000000..ca14fab
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/outline04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/outline05.xlsx b/xlsxwriter/test/comparison/xlsx_files/outline05.xlsx
new file mode 100644
index 0000000..5361265
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/outline05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/outline06.xlsx b/xlsxwriter/test/comparison/xlsx_files/outline06.xlsx
new file mode 100644
index 0000000..482bb2e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/outline06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/page_breaks01.xlsx b/xlsxwriter/test/comparison/xlsx_files/page_breaks01.xlsx
new file mode 100644
index 0000000..179b981
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/page_breaks01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/page_breaks02.xlsx b/xlsxwriter/test/comparison/xlsx_files/page_breaks02.xlsx
new file mode 100644
index 0000000..929001b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/page_breaks02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/page_breaks03.xlsx b/xlsxwriter/test/comparison/xlsx_files/page_breaks03.xlsx
new file mode 100644
index 0000000..3e98bc8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/page_breaks03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/page_breaks04.xlsx b/xlsxwriter/test/comparison/xlsx_files/page_breaks04.xlsx
new file mode 100644
index 0000000..906f51c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/page_breaks04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/page_breaks05.xlsx b/xlsxwriter/test/comparison/xlsx_files/page_breaks05.xlsx
new file mode 100644
index 0000000..76a1ff8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/page_breaks05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/page_breaks06.xlsx b/xlsxwriter/test/comparison/xlsx_files/page_breaks06.xlsx
new file mode 100644
index 0000000..e328e7b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/page_breaks06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/page_view01.xlsx b/xlsxwriter/test/comparison/xlsx_files/page_view01.xlsx
new file mode 100644
index 0000000..442d8d2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/page_view01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/panes01.xlsx b/xlsxwriter/test/comparison/xlsx_files/panes01.xlsx
new file mode 100644
index 0000000..50dac25
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/panes01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_across01.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_across01.xlsx
new file mode 100644
index 0000000..e33a89d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_across01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_area01.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_area01.xlsx
new file mode 100644
index 0000000..88adb1c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_area01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_area02.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_area02.xlsx
new file mode 100644
index 0000000..6d72dda
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_area02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_area03.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_area03.xlsx
new file mode 100644
index 0000000..e1aa416
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_area03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_area04.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_area04.xlsx
new file mode 100644
index 0000000..bdfa4f1
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_area04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_area05.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_area05.xlsx
new file mode 100644
index 0000000..e7e2bad
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_area05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_area06.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_area06.xlsx
new file mode 100644
index 0000000..f9e7981
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_area06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_area07.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_area07.xlsx
new file mode 100644
index 0000000..0970423
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_area07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_options01.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_options01.xlsx
new file mode 100644
index 0000000..bab282a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_options01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_options02.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_options02.xlsx
new file mode 100644
index 0000000..8f5a16c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_options02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_options03.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_options03.xlsx
new file mode 100644
index 0000000..d6722b4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_options03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_options04.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_options04.xlsx
new file mode 100644
index 0000000..7ada761
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_options04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_options05.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_options05.xlsx
new file mode 100644
index 0000000..ba92127
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_options05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/print_options06.xlsx b/xlsxwriter/test/comparison/xlsx_files/print_options06.xlsx
new file mode 100644
index 0000000..75730fd
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/print_options06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/properties01.xlsx b/xlsxwriter/test/comparison/xlsx_files/properties01.xlsx
new file mode 100644
index 0000000..c9d8036
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/properties01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/properties02.xlsx b/xlsxwriter/test/comparison/xlsx_files/properties02.xlsx
new file mode 100644
index 0000000..c5b4354
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/properties02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/properties03.xlsx b/xlsxwriter/test/comparison/xlsx_files/properties03.xlsx
new file mode 100644
index 0000000..391e196
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/properties03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/properties04.xlsx b/xlsxwriter/test/comparison/xlsx_files/properties04.xlsx
new file mode 100644
index 0000000..f334ef5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/properties04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/properties05.xlsx b/xlsxwriter/test/comparison/xlsx_files/properties05.xlsx
new file mode 100644
index 0000000..610f35a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/properties05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/protect01.xlsx b/xlsxwriter/test/comparison/xlsx_files/protect01.xlsx
new file mode 100644
index 0000000..81582e7
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/protect01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/protect02.xlsx b/xlsxwriter/test/comparison/xlsx_files/protect02.xlsx
new file mode 100644
index 0000000..c46820d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/protect02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/protect03.xlsx b/xlsxwriter/test/comparison/xlsx_files/protect03.xlsx
new file mode 100644
index 0000000..35f78b1
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/protect03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/quote_name01.xlsx b/xlsxwriter/test/comparison/xlsx_files/quote_name01.xlsx
new file mode 100644
index 0000000..64e56fb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/quote_name01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/quote_name02.xlsx b/xlsxwriter/test/comparison/xlsx_files/quote_name02.xlsx
new file mode 100644
index 0000000..3dd02ce
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/quote_name02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/quote_name03.xlsx b/xlsxwriter/test/comparison/xlsx_files/quote_name03.xlsx
new file mode 100644
index 0000000..c83a4d4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/quote_name03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/quote_name04.xlsx b/xlsxwriter/test/comparison/xlsx_files/quote_name04.xlsx
new file mode 100644
index 0000000..cce9691
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/quote_name04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/remove_timezone01.xlsx b/xlsxwriter/test/comparison/xlsx_files/remove_timezone01.xlsx
new file mode 100644
index 0000000..f3342bb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/remove_timezone01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/repeat01.xlsx b/xlsxwriter/test/comparison/xlsx_files/repeat01.xlsx
new file mode 100644
index 0000000..8659279
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/repeat01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/repeat02.xlsx b/xlsxwriter/test/comparison/xlsx_files/repeat02.xlsx
new file mode 100644
index 0000000..469705b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/repeat02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/repeat03.xlsx b/xlsxwriter/test/comparison/xlsx_files/repeat03.xlsx
new file mode 100644
index 0000000..1c2c0e6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/repeat03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/repeat04.xlsx b/xlsxwriter/test/comparison/xlsx_files/repeat04.xlsx
new file mode 100644
index 0000000..159f418
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/repeat04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/repeat05.xlsx b/xlsxwriter/test/comparison/xlsx_files/repeat05.xlsx
new file mode 100644
index 0000000..e1be245
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/repeat05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/rich_string01.xlsx b/xlsxwriter/test/comparison/xlsx_files/rich_string01.xlsx
new file mode 100644
index 0000000..03f9133
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/rich_string01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/rich_string02.xlsx b/xlsxwriter/test/comparison/xlsx_files/rich_string02.xlsx
new file mode 100644
index 0000000..ccfa3f9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/rich_string02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/rich_string03.xlsx b/xlsxwriter/test/comparison/xlsx_files/rich_string03.xlsx
new file mode 100644
index 0000000..3210db9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/rich_string03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/rich_string04.xlsx b/xlsxwriter/test/comparison/xlsx_files/rich_string04.xlsx
new file mode 100644
index 0000000..39507ff
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/rich_string04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/rich_string05.xlsx b/xlsxwriter/test/comparison/xlsx_files/rich_string05.xlsx
new file mode 100644
index 0000000..0547642
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/rich_string05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/rich_string06.xlsx b/xlsxwriter/test/comparison/xlsx_files/rich_string06.xlsx
new file mode 100644
index 0000000..1782914
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/rich_string06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/rich_string07.xlsx b/xlsxwriter/test/comparison/xlsx_files/rich_string07.xlsx
new file mode 100644
index 0000000..02f5629
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/rich_string07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/rich_string08.xlsx b/xlsxwriter/test/comparison/xlsx_files/rich_string08.xlsx
new file mode 100644
index 0000000..0a7336e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/rich_string08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/rich_string09.xlsx b/xlsxwriter/test/comparison/xlsx_files/rich_string09.xlsx
new file mode 100644
index 0000000..03f9133
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/rich_string09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/rich_string10.xlsx b/xlsxwriter/test/comparison/xlsx_files/rich_string10.xlsx
new file mode 100644
index 0000000..1ab07fb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/rich_string10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/rich_string11.xlsx b/xlsxwriter/test/comparison/xlsx_files/rich_string11.xlsx
new file mode 100644
index 0000000..1644dd2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/rich_string11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/rich_string12.xlsx b/xlsxwriter/test/comparison/xlsx_files/rich_string12.xlsx
new file mode 100644
index 0000000..5fc34ba
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/rich_string12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/selection01.xlsx b/xlsxwriter/test/comparison/xlsx_files/selection01.xlsx
new file mode 100644
index 0000000..0f939de
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/selection01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/selection02.xlsx b/xlsxwriter/test/comparison/xlsx_files/selection02.xlsx
new file mode 100644
index 0000000..3c7987a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/selection02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/set_column01.xlsx b/xlsxwriter/test/comparison/xlsx_files/set_column01.xlsx
new file mode 100644
index 0000000..460e6b4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/set_column01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/set_column03.xlsx b/xlsxwriter/test/comparison/xlsx_files/set_column03.xlsx
new file mode 100644
index 0000000..1149c1c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/set_column03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/set_column04.xlsx b/xlsxwriter/test/comparison/xlsx_files/set_column04.xlsx
new file mode 100644
index 0000000..1a694d8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/set_column04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/set_column05.xlsx b/xlsxwriter/test/comparison/xlsx_files/set_column05.xlsx
new file mode 100644
index 0000000..7207350
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/set_column05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/set_column06.xlsx b/xlsxwriter/test/comparison/xlsx_files/set_column06.xlsx
new file mode 100644
index 0000000..35221ba
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/set_column06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/set_column07.xlsx b/xlsxwriter/test/comparison/xlsx_files/set_column07.xlsx
new file mode 100644
index 0000000..dd30edb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/set_column07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/set_column08.xlsx b/xlsxwriter/test/comparison/xlsx_files/set_column08.xlsx
new file mode 100644
index 0000000..947b949
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/set_column08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/set_column09.xlsx b/xlsxwriter/test/comparison/xlsx_files/set_column09.xlsx
new file mode 100644
index 0000000..a024cf9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/set_column09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/set_print_scale01.xlsx b/xlsxwriter/test/comparison/xlsx_files/set_print_scale01.xlsx
new file mode 100644
index 0000000..3690960
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/set_print_scale01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/set_start_page01.xlsx b/xlsxwriter/test/comparison/xlsx_files/set_start_page01.xlsx
new file mode 100644
index 0000000..38d02c9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/set_start_page01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/set_start_page02.xlsx b/xlsxwriter/test/comparison/xlsx_files/set_start_page02.xlsx
new file mode 100644
index 0000000..a175cd6
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/set_start_page02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/set_start_page03.xlsx b/xlsxwriter/test/comparison/xlsx_files/set_start_page03.xlsx
new file mode 100644
index 0000000..ef06a8e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/set_start_page03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/shared_strings01.xlsx b/xlsxwriter/test/comparison/xlsx_files/shared_strings01.xlsx
new file mode 100644
index 0000000..e591c52
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/shared_strings01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/shared_strings02.xlsx b/xlsxwriter/test/comparison/xlsx_files/shared_strings02.xlsx
new file mode 100644
index 0000000..9430a77
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/shared_strings02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/simple01.xlsx b/xlsxwriter/test/comparison/xlsx_files/simple01.xlsx
new file mode 100644
index 0000000..dddce90
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/simple01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/simple02.xlsx b/xlsxwriter/test/comparison/xlsx_files/simple02.xlsx
new file mode 100644
index 0000000..be7a8c9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/simple02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/simple03.xlsx b/xlsxwriter/test/comparison/xlsx_files/simple03.xlsx
new file mode 100644
index 0000000..1f8db55
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/simple03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/simple04.xlsx b/xlsxwriter/test/comparison/xlsx_files/simple04.xlsx
new file mode 100644
index 0000000..d9282fa
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/simple04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/simple05.xlsx b/xlsxwriter/test/comparison/xlsx_files/simple05.xlsx
new file mode 100644
index 0000000..5e4af77
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/simple05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/simple07.xlsx b/xlsxwriter/test/comparison/xlsx_files/simple07.xlsx
new file mode 100644
index 0000000..fc3b152
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/simple07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/simple08.xlsx b/xlsxwriter/test/comparison/xlsx_files/simple08.xlsx
new file mode 100644
index 0000000..d869966
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/simple08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/simple09.xlsx b/xlsxwriter/test/comparison/xlsx_files/simple09.xlsx
new file mode 100644
index 0000000..095841b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/simple09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/tab_color01.xlsx b/xlsxwriter/test/comparison/xlsx_files/tab_color01.xlsx
new file mode 100644
index 0000000..9ec8970
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/tab_color01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table01.xlsx b/xlsxwriter/test/comparison/xlsx_files/table01.xlsx
new file mode 100644
index 0000000..43a326b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table02.xlsx b/xlsxwriter/test/comparison/xlsx_files/table02.xlsx
new file mode 100644
index 0000000..1687c1b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table03.xlsx b/xlsxwriter/test/comparison/xlsx_files/table03.xlsx
new file mode 100644
index 0000000..c437915
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table04.xlsx b/xlsxwriter/test/comparison/xlsx_files/table04.xlsx
new file mode 100644
index 0000000..b93fa22
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table05.xlsx b/xlsxwriter/test/comparison/xlsx_files/table05.xlsx
new file mode 100644
index 0000000..e9f7b8a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table06.xlsx b/xlsxwriter/test/comparison/xlsx_files/table06.xlsx
new file mode 100644
index 0000000..a655781
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table07.xlsx b/xlsxwriter/test/comparison/xlsx_files/table07.xlsx
new file mode 100644
index 0000000..ffa7d71
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table08.xlsx b/xlsxwriter/test/comparison/xlsx_files/table08.xlsx
new file mode 100644
index 0000000..c20181b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table09.xlsx b/xlsxwriter/test/comparison/xlsx_files/table09.xlsx
new file mode 100644
index 0000000..0b501da
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table10.xlsx b/xlsxwriter/test/comparison/xlsx_files/table10.xlsx
new file mode 100644
index 0000000..9491fcd
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table11.xlsx b/xlsxwriter/test/comparison/xlsx_files/table11.xlsx
new file mode 100644
index 0000000..f0582cf
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table12.xlsx b/xlsxwriter/test/comparison/xlsx_files/table12.xlsx
new file mode 100644
index 0000000..373763b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table13.xlsx b/xlsxwriter/test/comparison/xlsx_files/table13.xlsx
new file mode 100644
index 0000000..c8c2c33
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table13.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table14.xlsx b/xlsxwriter/test/comparison/xlsx_files/table14.xlsx
new file mode 100644
index 0000000..9f526ef
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table14.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table15.xlsx b/xlsxwriter/test/comparison/xlsx_files/table15.xlsx
new file mode 100644
index 0000000..038489b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table15.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table17.xlsx b/xlsxwriter/test/comparison/xlsx_files/table17.xlsx
new file mode 100644
index 0000000..a481d97
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table17.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table18.xlsx b/xlsxwriter/test/comparison/xlsx_files/table18.xlsx
new file mode 100644
index 0000000..16cdbe1
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table18.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table19.xlsx b/xlsxwriter/test/comparison/xlsx_files/table19.xlsx
new file mode 100644
index 0000000..118803e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table19.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table21.xlsx b/xlsxwriter/test/comparison/xlsx_files/table21.xlsx
new file mode 100644
index 0000000..b83e61b
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table21.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/table22.xlsx b/xlsxwriter/test/comparison/xlsx_files/table22.xlsx
new file mode 100644
index 0000000..37f3db5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/table22.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox01.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox01.xlsx
new file mode 100644
index 0000000..b46ffb5
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox02.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox02.xlsx
new file mode 100644
index 0000000..92c475c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox03.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox03.xlsx
new file mode 100644
index 0000000..5306095
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox04.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox04.xlsx
new file mode 100644
index 0000000..4beaf8a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox05.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox05.xlsx
new file mode 100644
index 0000000..146cebb
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox06.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox06.xlsx
new file mode 100644
index 0000000..0c8aef2
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox07.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox07.xlsx
new file mode 100644
index 0000000..d487b6f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox08.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox08.xlsx
new file mode 100644
index 0000000..9a6e521
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox09.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox09.xlsx
new file mode 100644
index 0000000..74019fa
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox10.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox10.xlsx
new file mode 100644
index 0000000..11efec1
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox11.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox11.xlsx
new file mode 100644
index 0000000..008afea
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox12.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox12.xlsx
new file mode 100644
index 0000000..f20699a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox12.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox13.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox13.xlsx
new file mode 100644
index 0000000..d904d9d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox13.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox14.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox14.xlsx
new file mode 100644
index 0000000..bc21713
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox14.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox15.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox15.xlsx
new file mode 100644
index 0000000..8567eb4
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox15.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox16.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox16.xlsx
new file mode 100644
index 0000000..2f2a44e
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox16.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox17.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox17.xlsx
new file mode 100644
index 0000000..11e7baa
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox17.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox18.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox18.xlsx
new file mode 100644
index 0000000..553b31f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox18.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox19.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox19.xlsx
new file mode 100644
index 0000000..d8acd59
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox19.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox20.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox20.xlsx
new file mode 100644
index 0000000..d4117ac
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox20.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox21.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox21.xlsx
new file mode 100644
index 0000000..526b30d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox21.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox22.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox22.xlsx
new file mode 100644
index 0000000..54cabd3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox22.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox23.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox23.xlsx
new file mode 100644
index 0000000..efaeef8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox23.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox24.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox24.xlsx
new file mode 100644
index 0000000..b69b601
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox24.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox25.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox25.xlsx
new file mode 100644
index 0000000..b1e9513
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox25.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/textbox26.xlsx b/xlsxwriter/test/comparison/xlsx_files/textbox26.xlsx
new file mode 100644
index 0000000..19dd328
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/textbox26.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/tutorial01.xlsx b/xlsxwriter/test/comparison/xlsx_files/tutorial01.xlsx
new file mode 100644
index 0000000..cba43a8
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/tutorial01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/tutorial02.xlsx b/xlsxwriter/test/comparison/xlsx_files/tutorial02.xlsx
new file mode 100644
index 0000000..46991cc
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/tutorial02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/tutorial03.xlsx b/xlsxwriter/test/comparison/xlsx_files/tutorial03.xlsx
new file mode 100644
index 0000000..465d5f9
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/tutorial03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/types01.xlsx b/xlsxwriter/test/comparison/xlsx_files/types01.xlsx
new file mode 100644
index 0000000..201ef68
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/types01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/types02.xlsx b/xlsxwriter/test/comparison/xlsx_files/types02.xlsx
new file mode 100644
index 0000000..d1f9539
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/types02.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/types03.xlsx b/xlsxwriter/test/comparison/xlsx_files/types03.xlsx
new file mode 100644
index 0000000..8ba9d5a
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/types03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/types04.xlsx b/xlsxwriter/test/comparison/xlsx_files/types04.xlsx
new file mode 100644
index 0000000..733761c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/types04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/types05.xlsx b/xlsxwriter/test/comparison/xlsx_files/types05.xlsx
new file mode 100644
index 0000000..f4c2343
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/types05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/types06.xlsx b/xlsxwriter/test/comparison/xlsx_files/types06.xlsx
new file mode 100644
index 0000000..0c42333
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/types06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/types07.xlsx b/xlsxwriter/test/comparison/xlsx_files/types07.xlsx
new file mode 100644
index 0000000..569b364
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/types07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/unicode_polish_utf8.txt b/xlsxwriter/test/comparison/xlsx_files/unicode_polish_utf8.txt
new file mode 100644
index 0000000..db53a80
--- /dev/null
+++ b/xlsxwriter/test/comparison/xlsx_files/unicode_polish_utf8.txt
@@ -0,0 +1,34 @@
+#
+# XlsxWriter Unicode examples.
+#
+# Sample encoded text borrowed from Sean Burke's Pod::Simple distro.
+#
+# The text is Polish encoded as UTF8
+#
+# See the unicode_polish_utf8.py example.
+#
+WŚRÓD NOCNEJ CISZY
+
+Wśród nocnej ciszy głos się rozchodzi:
+Wstańcie, pasterze, Bóg się nam rodzi!
+Czym prędzej się wybierajcie,
+Do Betlejem pospieszajcie
+Przywitać Pana.
+
+Poszli, znaleźli Dzieciątko w żłobie
+Z wszystkimi znaki danymi sobie.
+Jako Bogu cześć Mu dali,
+A witając zawołali
+Z wielkiej radości:
+
+Ach, witaj Zbawco z dawno żądany,
+Wiele tysięcy lat wyglądany
+Na Ciebie króle, prorocy
+Czekali, a Tyś tej nocy
+Nam się objawił.
+
+I my czekamy na Ciebie, Pana,
+A skoro przyjdziesz na głos kapłana,
+Padniemy na twarz przed Tobą,
+Wierząc, żeś jest pod osłoną
+Chleba i wina.
diff --git a/xlsxwriter/test/comparison/xlsx_files/unicode_polish_utf8.xlsx b/xlsxwriter/test/comparison/xlsx_files/unicode_polish_utf8.xlsx
new file mode 100644
index 0000000..e8bcba3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/unicode_polish_utf8.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/unicode_shift_jis.txt b/xlsxwriter/test/comparison/xlsx_files/unicode_shift_jis.txt
new file mode 100644
index 0000000..60fc196
--- /dev/null
+++ b/xlsxwriter/test/comparison/xlsx_files/unicode_shift_jis.txt
@@ -0,0 +1,35 @@
+#
+# XlsxWriter Unicode examples.
+#
+# Sample encoded text borrowed from Sean Burke's Pod::Simple distro.
+#
+# The text is encoded as Japanese Shift-JIS.
+#
+# See the unicode_shift_jis.py example.
+#
+Some uninteresting product specs found on the Net
+
+�^��
+
+S2763
+
+����
+
+GZ4 �_�C�N���C�b�N�~���[�����v 12V 10W�~1
+
+���@
+
+���E295 ���E365 ���E76mm
+
+����
+
+8.0kg
+
+�ގ�
+
+�����@�A���~�A�A���}�C�g�d��@�K���X
+
+���i
+
+76,000�~�i�����v�E�g�����X���݁j
+
diff --git a/xlsxwriter/test/comparison/xlsx_files/unicode_shift_jis.xlsx b/xlsxwriter/test/comparison/xlsx_files/unicode_shift_jis.xlsx
new file mode 100644
index 0000000..ce5abef
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/unicode_shift_jis.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/utf8_01.xlsx b/xlsxwriter/test/comparison/xlsx_files/utf8_01.xlsx
new file mode 100644
index 0000000..7ac4267
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/utf8_01.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/utf8_03.xlsx b/xlsxwriter/test/comparison/xlsx_files/utf8_03.xlsx
new file mode 100644
index 0000000..53871e3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/utf8_03.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/utf8_04.xlsx b/xlsxwriter/test/comparison/xlsx_files/utf8_04.xlsx
new file mode 100644
index 0000000..1b22711
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/utf8_04.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/utf8_05.xlsx b/xlsxwriter/test/comparison/xlsx_files/utf8_05.xlsx
new file mode 100644
index 0000000..e22d92d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/utf8_05.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/utf8_06.xlsx b/xlsxwriter/test/comparison/xlsx_files/utf8_06.xlsx
new file mode 100644
index 0000000..1ff29cf
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/utf8_06.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/utf8_07.xlsx b/xlsxwriter/test/comparison/xlsx_files/utf8_07.xlsx
new file mode 100644
index 0000000..ed52daa
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/utf8_07.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/utf8_08.xlsx b/xlsxwriter/test/comparison/xlsx_files/utf8_08.xlsx
new file mode 100644
index 0000000..a2784f3
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/utf8_08.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/utf8_09.xlsx b/xlsxwriter/test/comparison/xlsx_files/utf8_09.xlsx
new file mode 100644
index 0000000..0b6659f
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/utf8_09.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/utf8_10.xlsx b/xlsxwriter/test/comparison/xlsx_files/utf8_10.xlsx
new file mode 100644
index 0000000..5c98cc7
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/utf8_10.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/utf8_11.xlsx b/xlsxwriter/test/comparison/xlsx_files/utf8_11.xlsx
new file mode 100644
index 0000000..0357b0c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/utf8_11.xlsx differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/vbaProject01.bin b/xlsxwriter/test/comparison/xlsx_files/vbaProject01.bin
new file mode 100644
index 0000000..5bd5a0c
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/vbaProject01.bin differ
diff --git a/xlsxwriter/test/comparison/xlsx_files/vbaProject02.bin b/xlsxwriter/test/comparison/xlsx_files/vbaProject02.bin
new file mode 100644
index 0000000..61b760d
Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/vbaProject02.bin differ
diff --git a/xlsxwriter/test/contenttypes/__init__.py b/xlsxwriter/test/contenttypes/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/contenttypes/test_contenttypes01.py b/xlsxwriter/test/contenttypes/test_contenttypes01.py
new file mode 100644
index 0000000..23b4cc8
--- /dev/null
+++ b/xlsxwriter/test/contenttypes/test_contenttypes01.py
@@ -0,0 +1,55 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...contenttypes import ContentTypes
+
+
+class TestAssembleContentTypes(unittest.TestCase):
+ """
+ Test assembling a complete ContentTypes file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing an ContentTypes file."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ content = ContentTypes()
+ content._set_filehandle(fh)
+
+ content._add_worksheet_name('sheet1')
+ content._add_default(('jpeg', 'image/jpeg'))
+ content._add_shared_strings()
+ content._add_calc_chain()
+
+ content._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
+
+ <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
+ <Default Extension="xml" ContentType="application/xml"/>
+ <Default Extension="jpeg" ContentType="image/jpeg"/>
+
+ <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
+ <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
+ <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
+ <Override PartName="/xl/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>
+ <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
+ <Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
+ <Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
+ <Override PartName="/xl/calcChain.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml"/>
+ </Types>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/contenttypes/test_initialisation.py b/xlsxwriter/test/contenttypes/test_initialisation.py
new file mode 100644
index 0000000..bbca4c6
--- /dev/null
+++ b/xlsxwriter/test/contenttypes/test_initialisation.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...contenttypes import ContentTypes
+
+
+class TestInitialisation(unittest.TestCase):
+ """
+ Test initialisation of the ContentTypes class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.contenttypes = ContentTypes()
+ self.contenttypes._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test ContentTypes xml_declaration()"""
+
+ self.contenttypes._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/core/__init__.py b/xlsxwriter/test/core/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/core/test_core01.py b/xlsxwriter/test/core/test_core01.py
new file mode 100644
index 0000000..8ea9e1d
--- /dev/null
+++ b/xlsxwriter/test/core/test_core01.py
@@ -0,0 +1,49 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from datetime import datetime
+from ..helperfunctions import _xml_to_list
+from ...core import Core
+
+
+class TestAssembleCore(unittest.TestCase):
+ """
+ Test assembling a complete Core file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing an Core file."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ core = Core()
+ core._set_filehandle(fh)
+
+ properties = {
+ 'author': 'A User',
+ 'created': datetime(2010, 1, 1, 0, 0, 0),
+ }
+
+ core._set_properties(properties)
+
+ core._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <dc:creator>A User</dc:creator>
+ <cp:lastModifiedBy>A User</cp:lastModifiedBy>
+ <dcterms:created xsi:type="dcterms:W3CDTF">2010-01-01T00:00:00Z</dcterms:created>
+ <dcterms:modified xsi:type="dcterms:W3CDTF">2010-01-01T00:00:00Z</dcterms:modified>
+ </cp:coreProperties>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/core/test_core02.py b/xlsxwriter/test/core/test_core02.py
new file mode 100644
index 0000000..f0ac91d
--- /dev/null
+++ b/xlsxwriter/test/core/test_core02.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from datetime import datetime
+from ..helperfunctions import _xml_to_list
+from ...core import Core
+
+
+class TestAssembleCore(unittest.TestCase):
+ """
+ Test assembling a complete Core file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing an Core file."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ core = Core()
+ core._set_filehandle(fh)
+
+ properties = {
+ 'title': 'This is an example spreadsheet',
+ 'subject': 'With document properties',
+ 'author': 'John McNamara',
+ 'manager': 'Dr. Heinz Doofenshmirtz',
+ 'company': 'of Wolves',
+ 'category': 'Example spreadsheets',
+ 'keywords': 'Sample, Example, Properties',
+ 'comments': 'Created with Python and XlsxWriter',
+ 'status': 'Quo',
+ 'created': datetime(2011, 4, 6, 19, 45, 15),
+ }
+
+ core._set_properties(properties)
+
+ core._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <dc:title>This is an example spreadsheet</dc:title>
+ <dc:subject>With document properties</dc:subject>
+ <dc:creator>John McNamara</dc:creator>
+ <cp:keywords>Sample, Example, Properties</cp:keywords>
+ <dc:description>Created with Python and XlsxWriter</dc:description>
+ <cp:lastModifiedBy>John McNamara</cp:lastModifiedBy>
+ <dcterms:created xsi:type="dcterms:W3CDTF">2011-04-06T19:45:15Z</dcterms:created>
+ <dcterms:modified xsi:type="dcterms:W3CDTF">2011-04-06T19:45:15Z</dcterms:modified>
+ <cp:category>Example spreadsheets</cp:category>
+ <cp:contentStatus>Quo</cp:contentStatus>
+ </cp:coreProperties>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/core/test_initialisation.py b/xlsxwriter/test/core/test_initialisation.py
new file mode 100644
index 0000000..3807b32
--- /dev/null
+++ b/xlsxwriter/test/core/test_initialisation.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...core import Core
+
+
+class TestInitialisation(unittest.TestCase):
+ """
+ Test initialisation of the Core class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.core = Core()
+ self.core._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test Core xml_declaration()"""
+
+ self.core._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/drawing/__init__.py b/xlsxwriter/test/drawing/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/drawing/test_drawing_chart01.py b/xlsxwriter/test/drawing/test_drawing_chart01.py
new file mode 100644
index 0000000..10bae5c
--- /dev/null
+++ b/xlsxwriter/test/drawing/test_drawing_chart01.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...drawing import Drawing
+
+
+class TestAssembleDrawing(unittest.TestCase):
+ """
+ Test assembling a complete Drawing file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a drawing with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ drawing = Drawing()
+ drawing._set_filehandle(fh)
+
+ drawing._add_drawing_object([1, 4, 8, 457200, 104775, 12, 22, 152400, 180975, None, None, None, None, None, None])
+ drawing.embedded = 1
+
+ drawing._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
+ <xdr:twoCellAnchor>
+ <xdr:from>
+ <xdr:col>4</xdr:col>
+ <xdr:colOff>457200</xdr:colOff>
+ <xdr:row>8</xdr:row>
+ <xdr:rowOff>104775</xdr:rowOff>
+ </xdr:from>
+ <xdr:to>
+ <xdr:col>12</xdr:col>
+ <xdr:colOff>152400</xdr:colOff>
+ <xdr:row>22</xdr:row>
+ <xdr:rowOff>180975</xdr:rowOff>
+ </xdr:to>
+ <xdr:graphicFrame macro="">
+ <xdr:nvGraphicFramePr>
+ <xdr:cNvPr id="2" name="Chart 1"/>
+ <xdr:cNvGraphicFramePr/>
+ </xdr:nvGraphicFramePr>
+ <xdr:xfrm>
+ <a:off x="0" y="0"/>
+ <a:ext cx="0" cy="0"/>
+ </xdr:xfrm>
+ <a:graphic>
+ <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/chart">
+ <c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="rId1"/>
+ </a:graphicData>
+ </a:graphic>
+ </xdr:graphicFrame>
+ <xdr:clientData/>
+ </xdr:twoCellAnchor>
+ </xdr:wsDr>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/drawing/test_drawing_image01.py b/xlsxwriter/test/drawing/test_drawing_image01.py
new file mode 100644
index 0000000..4f9908f
--- /dev/null
+++ b/xlsxwriter/test/drawing/test_drawing_image01.py
@@ -0,0 +1,146 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...drawing import Drawing
+
+
+class TestAssembleDrawing(unittest.TestCase):
+ """
+ Test assembling a complete Drawing file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a drawing with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ drawing = Drawing()
+ drawing._set_filehandle(fh)
+
+ drawing._add_drawing_object([2, 2, 1, 0, 0, 3, 6, 533257, 190357, 1219200, 190500, 1142857, 1142857, 'republic.png', None])
+ drawing.embedded = 1
+
+ drawing._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
+ <xdr:twoCellAnchor editAs="oneCell">
+ <xdr:from>
+ <xdr:col>2</xdr:col>
+ <xdr:colOff>0</xdr:colOff>
+ <xdr:row>1</xdr:row>
+ <xdr:rowOff>0</xdr:rowOff>
+ </xdr:from>
+ <xdr:to>
+ <xdr:col>3</xdr:col>
+ <xdr:colOff>533257</xdr:colOff>
+ <xdr:row>6</xdr:row>
+ <xdr:rowOff>190357</xdr:rowOff>
+ </xdr:to>
+ <xdr:pic>
+ <xdr:nvPicPr>
+ <xdr:cNvPr id="2" name="Picture 1" descr="republic.png"/>
+ <xdr:cNvPicPr>
+ <a:picLocks noChangeAspect="1"/>
+ </xdr:cNvPicPr>
+ </xdr:nvPicPr>
+ <xdr:blipFill>
+ <a:blip xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:embed="rId1"/>
+ <a:stretch>
+ <a:fillRect/>
+ </a:stretch>
+ </xdr:blipFill>
+ <xdr:spPr>
+ <a:xfrm>
+ <a:off x="1219200" y="190500"/>
+ <a:ext cx="1142857" cy="1142857"/>
+ </a:xfrm>
+ <a:prstGeom prst="rect">
+ <a:avLst/>
+ </a:prstGeom>
+ </xdr:spPr>
+ </xdr:pic>
+ <xdr:clientData/>
+ </xdr:twoCellAnchor>
+ </xdr:wsDr>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
+
+ def test_assemble_xml_file_with_url(self):
+ """Test writing a drawing with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ drawing = Drawing()
+ drawing._set_filehandle(fh)
+
+ drawing = Drawing()
+ drawing._set_filehandle(fh)
+
+ tip = 'this is a tooltip'
+ url = 'https://www.github.com'
+ drawing._add_drawing_object([2, 2, 1, 0, 0, 3, 6, 533257, 190357, 1219200, 190500, 1142857, 1142857, 'republic.png', None, url, tip, None])
+ drawing.embedded = 1
+
+ drawing._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
+ <xdr:twoCellAnchor editAs="oneCell">
+ <xdr:from>
+ <xdr:col>2</xdr:col>
+ <xdr:colOff>0</xdr:colOff>
+ <xdr:row>1</xdr:row>
+ <xdr:rowOff>0</xdr:rowOff>
+ </xdr:from>
+ <xdr:to>
+ <xdr:col>3</xdr:col>
+ <xdr:colOff>533257</xdr:colOff>
+ <xdr:row>6</xdr:row>
+ <xdr:rowOff>190357</xdr:rowOff>
+ </xdr:to>
+ <xdr:pic>
+ <xdr:nvPicPr>
+ <xdr:cNvPr id="2" name="Picture 1" descr="republic.png">
+ <a:hlinkClick xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="rId1" tooltip="this is a tooltip"/>
+ </xdr:cNvPr>
+ <xdr:cNvPicPr>
+ <a:picLocks noChangeAspect="1"/>
+ </xdr:cNvPicPr>
+ </xdr:nvPicPr>
+ <xdr:blipFill>
+ <a:blip xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:embed="rId2"/>
+ <a:stretch>
+ <a:fillRect/>
+ </a:stretch>
+ </xdr:blipFill>
+ <xdr:spPr>
+ <a:xfrm>
+ <a:off x="1219200" y="190500"/>
+ <a:ext cx="1142857" cy="1142857"/>
+ </a:xfrm>
+ <a:prstGeom prst="rect">
+ <a:avLst/>
+ </a:prstGeom>
+ </xdr:spPr>
+ </xdr:pic>
+ <xdr:clientData/>
+ </xdr:twoCellAnchor>
+ </xdr:wsDr>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/drawing/test_write_a_graphic_frame_locks.py b/xlsxwriter/test/drawing/test_write_a_graphic_frame_locks.py
new file mode 100644
index 0000000..960ad6b
--- /dev/null
+++ b/xlsxwriter/test/drawing/test_write_a_graphic_frame_locks.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...drawing import Drawing
+
+
+class TestWriteAgraphicFrameLocks(unittest.TestCase):
+ """
+ Test the Drawing _write_a_graphic_frame_locks() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.drawing = Drawing()
+ self.drawing._set_filehandle(self.fh)
+
+ def test_write_a_graphic_frame_locks(self):
+ """Test the _write_a_graphic_frame_locks() method"""
+
+ self.drawing._write_a_graphic_frame_locks()
+
+ exp = """<a:graphicFrameLocks noGrp="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/drawing/test_write_c_chart.py b/xlsxwriter/test/drawing/test_write_c_chart.py
new file mode 100644
index 0000000..25259be
--- /dev/null
+++ b/xlsxwriter/test/drawing/test_write_c_chart.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...drawing import Drawing
+
+
+class TestWriteCchart(unittest.TestCase):
+ """
+ Test the Drawing _write_c_chart() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.drawing = Drawing()
+ self.drawing._set_filehandle(self.fh)
+
+ def test_write_c_chart(self):
+ """Test the _write_c_chart() method"""
+
+ self.drawing._write_c_chart('rId1')
+
+ exp = """<c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="rId1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/drawing/test_write_c_nv_graphic_frame_pr.py b/xlsxwriter/test/drawing/test_write_c_nv_graphic_frame_pr.py
new file mode 100644
index 0000000..7c870c3
--- /dev/null
+++ b/xlsxwriter/test/drawing/test_write_c_nv_graphic_frame_pr.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...drawing import Drawing
+
+
+class TestWriteXdrcNvGraphicFramePr(unittest.TestCase):
+ """
+ Test the Drawing _write_c_nv_graphic_frame_pr() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.drawing = Drawing()
+ self.drawing._set_filehandle(self.fh)
+
+ def test_write_c_nv_graphic_frame_pr(self):
+ """Test the _write_c_nv_graphic_frame_pr() method"""
+
+ self.drawing._write_c_nv_graphic_frame_pr()
+
+ exp = """<xdr:cNvGraphicFramePr><a:graphicFrameLocks noGrp="1"/></xdr:cNvGraphicFramePr>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/drawing/test_write_c_nv_pr.py b/xlsxwriter/test/drawing/test_write_c_nv_pr.py
new file mode 100644
index 0000000..48e659a
--- /dev/null
+++ b/xlsxwriter/test/drawing/test_write_c_nv_pr.py
@@ -0,0 +1,40 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...drawing import Drawing
+
+
+class TestWriteXdrcNvPr(unittest.TestCase):
+ """
+ Test the Drawing _write_c_nv_pr() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.drawing = Drawing()
+ self.drawing._set_filehandle(self.fh)
+
+ def test_write_c_nv_pr(self):
+ """Test the _write_c_nv_pr() method"""
+
+ self.drawing._write_c_nv_pr(2, 'Chart 1')
+
+ exp = """<xdr:cNvPr id="2" name="Chart 1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ options = {'url': 'https://www.github.com', 'tip': 'tip'}
+ self.drawing._write_c_nv_pr(2, 'Chart 1', options)
+
+ exp = """<xdr:cNvPr id="2" name="Chart 1"/><xdr:cNvPr id="2" name="Chart 1"><a:hlinkClick xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="rId1" tooltip="tip"/></xdr:cNvPr>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/drawing/test_write_col.py b/xlsxwriter/test/drawing/test_write_col.py
new file mode 100644
index 0000000..068cea8
--- /dev/null
+++ b/xlsxwriter/test/drawing/test_write_col.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...drawing import Drawing
+
+
+class TestWriteXdrcol(unittest.TestCase):
+ """
+ Test the Drawing _write_col() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.drawing = Drawing()
+ self.drawing._set_filehandle(self.fh)
+
+ def test_write_col(self):
+ """Test the _write_col() method"""
+
+ self.drawing._write_col(4)
+
+ exp = """<xdr:col>4</xdr:col>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/drawing/test_write_col_off.py b/xlsxwriter/test/drawing/test_write_col_off.py
new file mode 100644
index 0000000..58fa67b
--- /dev/null
+++ b/xlsxwriter/test/drawing/test_write_col_off.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...drawing import Drawing
+
+
+class TestWriteXdrcolOff(unittest.TestCase):
+ """
+ Test the Drawing _write_col_off() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.drawing = Drawing()
+ self.drawing._set_filehandle(self.fh)
+
+ def test_write_col_off(self):
+ """Test the _write_col_off() method"""
+
+ self.drawing._write_col_off(457200)
+
+ exp = """<xdr:colOff>457200</xdr:colOff>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/drawing/test_write_ext.py b/xlsxwriter/test/drawing/test_write_ext.py
new file mode 100644
index 0000000..3b9f327
--- /dev/null
+++ b/xlsxwriter/test/drawing/test_write_ext.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...drawing import Drawing
+
+
+class TestWriteXdrext(unittest.TestCase):
+ """
+ Test the Drawing _write_ext() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.drawing = Drawing()
+ self.drawing._set_filehandle(self.fh)
+
+ def test_write_ext(self):
+ """Test the _write_ext() method"""
+
+ self.drawing._write_ext(9308969, 6078325)
+
+ exp = """<xdr:ext cx="9308969" cy="6078325"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/drawing/test_write_pos.py b/xlsxwriter/test/drawing/test_write_pos.py
new file mode 100644
index 0000000..f448f98
--- /dev/null
+++ b/xlsxwriter/test/drawing/test_write_pos.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...drawing import Drawing
+
+
+class TestWriteXdrpos(unittest.TestCase):
+ """
+ Test the Drawing _write_pos() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.drawing = Drawing()
+ self.drawing._set_filehandle(self.fh)
+
+ def test_write_pos(self):
+ """Test the _write_pos() method"""
+
+ self.drawing._write_pos(0, 0)
+
+ exp = """<xdr:pos x="0" y="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/drawing/test_write_row.py b/xlsxwriter/test/drawing/test_write_row.py
new file mode 100644
index 0000000..905f27a
--- /dev/null
+++ b/xlsxwriter/test/drawing/test_write_row.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...drawing import Drawing
+
+
+class TestWriteXdrrow(unittest.TestCase):
+ """
+ Test the Drawing _write_row() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.drawing = Drawing()
+ self.drawing._set_filehandle(self.fh)
+
+ def test_write_row(self):
+ """Test the _write_row() method"""
+
+ self.drawing._write_row(8)
+
+ exp = """<xdr:row>8</xdr:row>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/drawing/test_write_row_off.py b/xlsxwriter/test/drawing/test_write_row_off.py
new file mode 100644
index 0000000..4cc9164
--- /dev/null
+++ b/xlsxwriter/test/drawing/test_write_row_off.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...drawing import Drawing
+
+
+class TestWriteXdrrowOff(unittest.TestCase):
+ """
+ Test the Drawing _write_row_off() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.drawing = Drawing()
+ self.drawing._set_filehandle(self.fh)
+
+ def test_write_row_off(self):
+ """Test the _write_row_off() method"""
+
+ self.drawing._write_row_off(104775)
+
+ exp = """<xdr:rowOff>104775</xdr:rowOff>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/drawing/test_xml_declaration.py b/xlsxwriter/test/drawing/test_xml_declaration.py
new file mode 100644
index 0000000..c6439c1
--- /dev/null
+++ b/xlsxwriter/test/drawing/test_xml_declaration.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...drawing import Drawing
+
+
+class TestDrawingXmlDeclaration(unittest.TestCase):
+ """
+ Test initialisation of the Drawing class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.drawing = Drawing()
+ self.drawing._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test Drawing xml_declaration()"""
+
+ self.drawing._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/excel_comparsion_test.py b/xlsxwriter/test/excel_comparsion_test.py
new file mode 100644
index 0000000..ec18ef2
--- /dev/null
+++ b/xlsxwriter/test/excel_comparsion_test.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+import os
+from .helperfunctions import _compare_xlsx_files
+
+
+class ExcelComparisonTest(unittest.TestCase):
+ """
+ Test class for comparing a file created by XlsxWriter against a file
+ created by Excel.
+
+ """
+
+ def assertExcelEqual(self):
+
+ got, exp = _compare_xlsx_files(self.got_filename,
+ self.exp_filename,
+ self.ignore_files,
+ self.ignore_elements)
+
+ self.assertEqual(got, exp)
+
+ def tearDown(self):
+ # Cleanup by removing the temp excel file created for testing.
+ if os.path.exists(self.got_filename):
+ os.remove(self.got_filename)
diff --git a/xlsxwriter/test/format/__init__.py b/xlsxwriter/test/format/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/format/test_initialisation.py b/xlsxwriter/test/format/test_initialisation.py
new file mode 100644
index 0000000..37b043c
--- /dev/null
+++ b/xlsxwriter/test/format/test_initialisation.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...format import Format
+
+
+class TestInitialisation(unittest.TestCase):
+ """
+ Test initialisation of the Format class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.format = Format()
+ self.format._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test Format xml_declaration()"""
+
+ self.format._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/helperfunctions.py b/xlsxwriter/test/helperfunctions.py
new file mode 100644
index 0000000..1e5ca6e
--- /dev/null
+++ b/xlsxwriter/test/helperfunctions.py
@@ -0,0 +1,224 @@
+###############################################################################
+#
+# Helper functions for testing XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import re
+import sys
+import os.path
+from zipfile import ZipFile
+from zipfile import BadZipfile
+from zipfile import LargeZipFile
+
+
+def _xml_to_list(xml_str):
+ # Convert test generated XML strings into lists for comparison testing.
+
+ # Split the XML string at tag boundaries.
+ parser = re.compile(r'>\s*<')
+ elements = parser.split(xml_str.strip())
+
+ elements = [s.replace("\r", "") for s in elements]
+
+ # Add back the removed brackets.
+ for index, element in enumerate(elements):
+ if not element[0] == '<':
+ elements[index] = '<' + elements[index]
+ if not element[-1] == '>':
+ elements[index] = elements[index] + '>'
+
+ return elements
+
+
+def _vml_to_list(vml_str):
+ # Convert an Excel generated VML string into a list for comparison testing.
+ #
+ # The VML data in the testcases is taken from Excel 2007 files. The data
+ # has to be massaged significantly to make it suitable for comparison.
+ #
+ # The VML produced by XlsxWriter can be parsed as ordinary XML.
+ vml_str = vml_str.replace("\r", "")
+
+ vml = vml_str.split("\n")
+ vml_str = ''
+
+ for line in vml:
+ # Skip blank lines.
+ if not line:
+ continue
+
+ # Strip leading and trailing whitespace.
+ line = line.strip()
+
+ # Convert VMLs attribute quotes.
+ line = line.replace("'", '"')
+
+ # Add space between attributes.
+ if re.search('"$', line):
+ line += " "
+
+ # Add newline after element end.
+ if re.search('>$', line):
+ line += "\n"
+
+ # Split multiple elements.
+ line = line.replace('><', ">\n<")
+
+ # Put all of Anchor on one line.
+ if line == "<x:Anchor>\n":
+ line = line.strip()
+
+ vml_str += line
+
+ # Remove the final newline.
+ vml_str = vml_str.rstrip()
+
+ return vml_str.split("\n")
+
+
+def _sort_rel_file_data(xml_elements):
+ # Re-order the relationship elements in an array of XLSX XML rel
+ # (relationship) data. This is necessary for comparison since
+ # Excel can produce the elements in a semi-random order.
+
+ # We don't want to sort the first or last elements.
+ first = xml_elements.pop(0)
+ last = xml_elements.pop()
+
+ # Sort the relationship elements.
+ xml_elements.sort()
+
+ # Add back the first and last elements.
+ xml_elements.insert(0, first)
+ xml_elements.append(last)
+
+ return xml_elements
+
+
+def _compare_xlsx_files(got_file, exp_file, ignore_files, ignore_elements):
+ # Compare two XLSX files by extracting the XML files from each
+ # zip archive and comparing them.
+ #
+ # This is used to compare an "expected" file produced by Excel
+ # with a "got" file produced by XlsxWriter.
+ #
+ # In order to compare the XLSX files we convert the data in each
+ # XML file into an list of XML elements.
+ try:
+ # Open the XlsxWriter as a zip file for testing.
+ got_zip = ZipFile(got_file, 'r')
+ except IOError:
+ # For Python 2.5+ compatibility.
+ e = sys.exc_info()[1]
+ error = "XlsxWriter file error: " + str(e)
+ return error, ''
+ except (BadZipfile, LargeZipFile):
+ e = sys.exc_info()[1]
+ error = "XlsxWriter zipfile error, '" + exp_file + "': " + str(e)
+ return error, ''
+
+ try:
+ # Open the Excel as a zip file for testing.
+ exp_zip = ZipFile(exp_file, 'r')
+ except IOError:
+ e = sys.exc_info()[1]
+ error = "Excel file error: " + str(e)
+ return error, ''
+ except (BadZipfile, LargeZipFile):
+ e = sys.exc_info()[1]
+ error = "Excel zipfile error, '" + exp_file + "': " + str(e)
+ return error, ''
+
+ # Get the filenames from the zip files.
+ got_files = sorted(got_zip.namelist())
+ exp_files = sorted(exp_zip.namelist())
+
+ # Ignore some test specific filenames.
+ got_files = [name for name in got_files if name not in ignore_files]
+ exp_files = [name for name in exp_files if name not in ignore_files]
+
+ # Check that each XLSX container has the same files.
+ if got_files != exp_files:
+ return got_files, exp_files
+
+ # Compare each file in the XLSX containers.
+ for filename in exp_files:
+ got_xml_str = got_zip.read(filename)
+ exp_xml_str = exp_zip.read(filename)
+
+ # Compare binary files with string comparison based on extension.
+ extension = os.path.splitext(filename)[1]
+ if extension in ('.png', '.jpeg', '.bmp', '.bin'):
+ if got_xml_str != exp_xml_str:
+ return 'got: %s' % filename, 'exp: %s' % filename
+ continue
+
+ if sys.version_info >= (3, 0, 0):
+ got_xml_str = got_xml_str.decode('utf-8')
+ exp_xml_str = exp_xml_str.decode('utf-8')
+
+ # Remove dates and user specific data from the core.xml data.
+ if filename == 'docProps/core.xml':
+ exp_xml_str = re.sub(r' ?John', '', exp_xml_str)
+ exp_xml_str = re.sub(r'\d\d\d\d-\d\d-\d\dT\d\d\:\d\d:\d\dZ',
+ '', exp_xml_str)
+ got_xml_str = re.sub(r'\d\d\d\d-\d\d-\d\dT\d\d\:\d\d:\d\dZ',
+ '', got_xml_str)
+
+ # Remove workbookView dimensions which are almost always different
+ # and calcPr which can have different Excel version ids.
+ if filename == 'xl/workbook.xml':
+ exp_xml_str = re.sub(r'<workbookView[^>]*>',
+ '<workbookView/>', exp_xml_str)
+ got_xml_str = re.sub(r'<workbookView[^>]*>',
+ '<workbookView/>', got_xml_str)
+ exp_xml_str = re.sub(r'<calcPr[^>]*>',
+ '<calcPr/>', exp_xml_str)
+ got_xml_str = re.sub(r'<calcPr[^>]*>',
+ '<calcPr/>', got_xml_str)
+
+ # Remove printer specific settings from Worksheet pageSetup elements.
+ if re.match(r'xl/worksheets/sheet\d.xml', filename):
+ exp_xml_str = re.sub(r'horizontalDpi="200" ', '', exp_xml_str)
+ exp_xml_str = re.sub(r'verticalDpi="200" ', '', exp_xml_str)
+ exp_xml_str = re.sub(r'(<pageSetup[^>]*) r:id="rId1"',
+ r'\1', exp_xml_str)
+
+ # Remove Chart pageMargin dimensions which are almost always different.
+ if re.match(r'xl/charts/chart\d.xml', filename):
+ exp_xml_str = re.sub(r'<c:pageMargins[^>]*>',
+ '<c:pageMargins/>', exp_xml_str)
+ got_xml_str = re.sub(r'<c:pageMargins[^>]*>',
+ '<c:pageMargins/>', got_xml_str)
+
+ # Convert the XML string to lists for comparison.
+ if re.search('.vml$', filename):
+ got_xml = _xml_to_list(got_xml_str)
+ exp_xml = _vml_to_list(exp_xml_str)
+ else:
+ got_xml = _xml_to_list(got_xml_str)
+ exp_xml = _xml_to_list(exp_xml_str)
+
+ # Ignore test specific XML elements for defined filenames.
+ if filename in ignore_elements:
+ patterns = ignore_elements[filename]
+
+ for pattern in patterns:
+ exp_xml = [tag for tag in exp_xml if not re.match(pattern, tag)]
+ got_xml = [tag for tag in got_xml if not re.match(pattern, tag)]
+
+ # Reorder the XML elements in the XLSX relationship files.
+ if filename == '[Content_Types].xml' or re.search('.rels$', filename):
+ got_xml = _sort_rel_file_data(got_xml)
+ exp_xml = _sort_rel_file_data(exp_xml)
+
+ # Compared the XML elements in each file.
+ if got_xml != exp_xml:
+ got_xml.insert(0, filename)
+ exp_xml.insert(0, filename)
+ return got_xml, exp_xml
+
+ # If we got here the files are the same.
+ return 'Ok', 'Ok'
diff --git a/xlsxwriter/test/relationships/__init__.py b/xlsxwriter/test/relationships/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/relationships/test_initialisation.py b/xlsxwriter/test/relationships/test_initialisation.py
new file mode 100644
index 0000000..760160b
--- /dev/null
+++ b/xlsxwriter/test/relationships/test_initialisation.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...relationships import Relationships
+
+
+class TestInitialisation(unittest.TestCase):
+ """
+ Test initialisation of the Relationships class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.relationships = Relationships()
+ self.relationships._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test Relationships xml_declaration()"""
+
+ self.relationships._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/relationships/test_relationships01.py b/xlsxwriter/test/relationships/test_relationships01.py
new file mode 100644
index 0000000..b90219f
--- /dev/null
+++ b/xlsxwriter/test/relationships/test_relationships01.py
@@ -0,0 +1,48 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...relationships import Relationships
+
+
+class TestAssembleRelationships(unittest.TestCase):
+ """
+ Test assembling a complete Relationships file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing an Relationships file."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ rels = Relationships()
+ rels._set_filehandle(fh)
+
+ rels._add_document_relationship('/worksheet', 'worksheets/sheet1.xml')
+ rels._add_document_relationship('/theme', 'theme/theme1.xml')
+ rels._add_document_relationship('/styles', 'styles.xml')
+ rels._add_document_relationship('/sharedStrings', 'sharedStrings.xml')
+ rels._add_document_relationship('/calcChain', 'calcChain.xml')
+
+ rels._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
+ <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/>
+ <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
+ <Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>
+ <Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/calcChain" Target="calcChain.xml"/>
+ </Relationships>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/sharedstrings/__init__.py b/xlsxwriter/test/sharedstrings/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/sharedstrings/test_initialisation.py b/xlsxwriter/test/sharedstrings/test_initialisation.py
new file mode 100644
index 0000000..e52f2b1
--- /dev/null
+++ b/xlsxwriter/test/sharedstrings/test_initialisation.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...sharedstrings import SharedStrings
+
+
+class TestInitialisation(unittest.TestCase):
+ """
+ Test initialisation of the SharedStrings class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.sharedstrings = SharedStrings()
+ self.sharedstrings._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test Sharedstrings xml_declaration()"""
+
+ self.sharedstrings._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/sharedstrings/test_sharedstrings01.py b/xlsxwriter/test/sharedstrings/test_sharedstrings01.py
new file mode 100644
index 0000000..ee42223
--- /dev/null
+++ b/xlsxwriter/test/sharedstrings/test_sharedstrings01.py
@@ -0,0 +1,73 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...sharedstrings import SharedStringTable
+from ...sharedstrings import SharedStrings
+
+
+class TestAssembleSharedStrings(unittest.TestCase):
+ """
+ Test assembling a complete SharedStrings file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test the _write_sheet_data() method"""
+
+ string_table = SharedStringTable()
+
+ # Add some strings and check the returned indices.
+ index = string_table._get_shared_string_index('neptune')
+ self.assertEqual(index, 0)
+
+ index = string_table._get_shared_string_index('neptune')
+ self.assertEqual(index, 0)
+
+ index = string_table._get_shared_string_index('neptune')
+ self.assertEqual(index, 0)
+
+ index = string_table._get_shared_string_index('mars')
+ self.assertEqual(index, 1)
+
+ index = string_table._get_shared_string_index('venus')
+ self.assertEqual(index, 2)
+
+ index = string_table._get_shared_string_index('mars')
+ self.assertEqual(index, 1)
+
+ index = string_table._get_shared_string_index('venus')
+ self.assertEqual(index, 2)
+
+ string_table._sort_string_data()
+
+ fh = StringIO()
+ sharedstrings = SharedStrings()
+ sharedstrings._set_filehandle(fh)
+ sharedstrings.string_table = string_table
+
+ sharedstrings._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="7" uniqueCount="3">
+ <si>
+ <t>neptune</t>
+ </si>
+ <si>
+ <t>mars</t>
+ </si>
+ <si>
+ <t>venus</t>
+ </si>
+ </sst>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/sharedstrings/test_sharedstrings02.py b/xlsxwriter/test/sharedstrings/test_sharedstrings02.py
new file mode 100644
index 0000000..d6b91fb
--- /dev/null
+++ b/xlsxwriter/test/sharedstrings/test_sharedstrings02.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...sharedstrings import SharedStringTable
+from ...sharedstrings import SharedStrings
+
+
+class TestAssembleSharedStrings(unittest.TestCase):
+ """
+ Test assembling a complete SharedStrings file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test the _assemble_xml_file() method"""
+
+ string_table = SharedStringTable()
+
+ # Add some strings with leading/trailing whitespace.
+ index = string_table._get_shared_string_index('abcdefg')
+ self.assertEqual(index, 0)
+
+ index = string_table._get_shared_string_index(' abcdefg')
+ self.assertEqual(index, 1)
+
+ index = string_table._get_shared_string_index('abcdefg ')
+ self.assertEqual(index, 2)
+
+ string_table._sort_string_data()
+
+ fh = StringIO()
+ sharedstrings = SharedStrings()
+ sharedstrings._set_filehandle(fh)
+ sharedstrings.string_table = string_table
+
+ sharedstrings._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="3" uniqueCount="3">
+ <si>
+ <t>abcdefg</t>
+ </si>
+ <si>
+ <t xml:space="preserve"> abcdefg</t>
+ </si>
+ <si>
+ <t xml:space="preserve">abcdefg </t>
+ </si>
+ </sst>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/sharedstrings/test_write_si.py b/xlsxwriter/test/sharedstrings/test_write_si.py
new file mode 100644
index 0000000..ea4999e
--- /dev/null
+++ b/xlsxwriter/test/sharedstrings/test_write_si.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...sharedstrings import SharedStrings
+
+
+class TestWriteSi(unittest.TestCase):
+ """
+ Test the SharedStrings _write_si() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.sharedstrings = SharedStrings()
+ self.sharedstrings._set_filehandle(self.fh)
+
+ def test_write_si(self):
+ """Test the _write_si() method"""
+
+ self.sharedstrings._write_si('neptune')
+
+ exp = """<si><t>neptune</t></si>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/sharedstrings/test_write_sst.py b/xlsxwriter/test/sharedstrings/test_write_sst.py
new file mode 100644
index 0000000..58c3f65
--- /dev/null
+++ b/xlsxwriter/test/sharedstrings/test_write_sst.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...sharedstrings import SharedStringTable
+from ...sharedstrings import SharedStrings
+
+
+class TestWriteSst(unittest.TestCase):
+ """
+ Test the SharedStrings _write_sst() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.sharedstrings = SharedStrings()
+ self.sharedstrings._set_filehandle(self.fh)
+
+ def test_write_sst(self):
+ """Test the _write_sst() method"""
+
+ string_table = SharedStringTable()
+
+ # Add some strings and check the returned indices.
+ string_table._get_shared_string_index('neptune')
+ string_table._get_shared_string_index('neptune')
+ string_table._get_shared_string_index('neptune')
+ string_table._get_shared_string_index('mars')
+ string_table._get_shared_string_index('venus')
+ string_table._get_shared_string_index('mars')
+ string_table._get_shared_string_index('venus')
+ self.sharedstrings.string_table = string_table
+
+ self.sharedstrings._write_sst()
+
+ exp = """<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="7" uniqueCount="3">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/__init__.py b/xlsxwriter/test/styles/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/styles/test_initialisation.py b/xlsxwriter/test/styles/test_initialisation.py
new file mode 100644
index 0000000..ac582a3
--- /dev/null
+++ b/xlsxwriter/test/styles/test_initialisation.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+
+
+class TestInitialisation(unittest.TestCase):
+ """
+ Test initialisation of the Styles class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test Styles xml_declaration()"""
+
+ self.styles._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_styles01.py b/xlsxwriter/test/styles/test_styles01.py
new file mode 100644
index 0000000..ac49ceb
--- /dev/null
+++ b/xlsxwriter/test/styles/test_styles01.py
@@ -0,0 +1,90 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...styles import Styles
+from ...workbook import Workbook
+
+
+class TestAssembleStyles(unittest.TestCase):
+ """
+ Test assembling a complete Styles file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test for styles.xml file with default styles."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ style = Styles()
+ style._set_filehandle(fh)
+
+ workbook = Workbook()
+ workbook._prepare_format_properties()
+
+ style._set_style_properties([
+ workbook.xf_formats,
+ workbook.palette,
+ workbook.font_count,
+ workbook.num_format_count,
+ workbook.border_count,
+ workbook.fill_count,
+ workbook.custom_colors,
+ workbook.dxf_formats,
+ ])
+
+ style._assemble_xml_file()
+ workbook.fileclosed = 1
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
+ <fonts count="1">
+ <font>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ </fonts>
+ <fills count="2">
+ <fill>
+ <patternFill patternType="none"/>
+ </fill>
+ <fill>
+ <patternFill patternType="gray125"/>
+ </fill>
+ </fills>
+ <borders count="1">
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ </borders>
+ <cellStyleXfs count="1">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
+ </cellStyleXfs>
+ <cellXfs count="1">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>
+ </cellXfs>
+ <cellStyles count="1">
+ <cellStyle name="Normal" xfId="0" builtinId="0"/>
+ </cellStyles>
+ <dxfs count="0"/>
+ <tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleLight16"/>
+ </styleSheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_styles02.py b/xlsxwriter/test/styles/test_styles02.py
new file mode 100644
index 0000000..1402966
--- /dev/null
+++ b/xlsxwriter/test/styles/test_styles02.py
@@ -0,0 +1,124 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...styles import Styles
+from ...workbook import Workbook
+
+
+class TestAssembleStyles(unittest.TestCase):
+ """
+ Test assembling a complete Styles file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test for simple font styles."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ style = Styles()
+ style._set_filehandle(fh)
+
+ workbook = Workbook()
+
+ workbook.add_format({'bold': 1})
+ workbook.add_format({'italic': 1})
+ workbook.add_format({'bold': 1, 'italic': 1})
+
+ workbook._set_default_xf_indices()
+ workbook._prepare_format_properties()
+
+ style._set_style_properties([
+ workbook.xf_formats,
+ workbook.palette,
+ workbook.font_count,
+ workbook.num_format_count,
+ workbook.border_count,
+ workbook.fill_count,
+ workbook.custom_colors,
+ workbook.dxf_formats,
+ ])
+
+ style._assemble_xml_file()
+ workbook.fileclosed = 1
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
+ <fonts count="4">
+ <font>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ <font>
+ <b/>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ <font>
+ <i/>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ <font>
+ <b/>
+ <i/>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ </fonts>
+ <fills count="2">
+ <fill>
+ <patternFill patternType="none"/>
+ </fill>
+ <fill>
+ <patternFill patternType="gray125"/>
+ </fill>
+ </fills>
+ <borders count="1">
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ </borders>
+ <cellStyleXfs count="1">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
+ </cellStyleXfs>
+ <cellXfs count="4">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>
+ <xf numFmtId="0" fontId="1" fillId="0" borderId="0" xfId="0" applyFont="1"/>
+ <xf numFmtId="0" fontId="2" fillId="0" borderId="0" xfId="0" applyFont="1"/>
+ <xf numFmtId="0" fontId="3" fillId="0" borderId="0" xfId="0" applyFont="1"/>
+ </cellXfs>
+ <cellStyles count="1">
+ <cellStyle name="Normal" xfId="0" builtinId="0"/>
+ </cellStyles>
+ <dxfs count="0"/>
+ <tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleLight16"/>
+ </styleSheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_styles03.py b/xlsxwriter/test/styles/test_styles03.py
new file mode 100644
index 0000000..e645d52
--- /dev/null
+++ b/xlsxwriter/test/styles/test_styles03.py
@@ -0,0 +1,110 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...styles import Styles
+from ...workbook import Workbook
+
+
+class TestAssembleStyles(unittest.TestCase):
+ """
+ Test assembling a complete Styles file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test for number format and font styles."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ style = Styles()
+ style._set_filehandle(fh)
+
+ workbook = Workbook()
+
+ workbook.add_format({'num_format': 2})
+ workbook.add_format({'num_format': 2, 'bold': 1})
+ workbook.add_format({'num_format': '0.0'})
+
+ workbook._set_default_xf_indices()
+ workbook._prepare_format_properties()
+
+ style._set_style_properties([
+ workbook.xf_formats,
+ workbook.palette,
+ workbook.font_count,
+ workbook.num_format_count,
+ workbook.border_count,
+ workbook.fill_count,
+ workbook.custom_colors,
+ workbook.dxf_formats,
+ ])
+
+ style._assemble_xml_file()
+ workbook.fileclosed = 1
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
+ <numFmts count="1">
+ <numFmt numFmtId="164" formatCode="0.0"/>
+ </numFmts>
+ <fonts count="2">
+ <font>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ <font>
+ <b/>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ </fonts>
+ <fills count="2">
+ <fill>
+ <patternFill patternType="none"/>
+ </fill>
+ <fill>
+ <patternFill patternType="gray125"/>
+ </fill>
+ </fills>
+ <borders count="1">
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ </borders>
+ <cellStyleXfs count="1">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
+ </cellStyleXfs>
+ <cellXfs count="4">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>
+ <xf numFmtId="2" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/>
+ <xf numFmtId="2" fontId="1" fillId="0" borderId="0" xfId="0" applyNumberFormat="1" applyFont="1"/>
+ <xf numFmtId="164" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/>
+ </cellXfs>
+ <cellStyles count="1">
+ <cellStyle name="Normal" xfId="0" builtinId="0"/>
+ </cellStyles>
+ <dxfs count="0"/>
+ <tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleLight16"/>
+ </styleSheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_styles04.py b/xlsxwriter/test/styles/test_styles04.py
new file mode 100644
index 0000000..502f8a3
--- /dev/null
+++ b/xlsxwriter/test/styles/test_styles04.py
@@ -0,0 +1,236 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...styles import Styles
+from ...workbook import Workbook
+
+
+class TestAssembleStyles(unittest.TestCase):
+ """
+ Test assembling a complete Styles file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test for border styles."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ style = Styles()
+ style._set_filehandle(fh)
+
+ workbook = Workbook()
+ workbook.fileclosed = 1
+
+ workbook.add_format({'top': 7})
+ workbook.add_format({'top': 4})
+ workbook.add_format({'top': 11})
+ workbook.add_format({'top': 9})
+ workbook.add_format({'top': 3})
+ workbook.add_format({'top': 1})
+ workbook.add_format({'top': 12})
+ workbook.add_format({'top': 13})
+ workbook.add_format({'top': 10})
+ workbook.add_format({'top': 8})
+ workbook.add_format({'top': 2})
+ workbook.add_format({'top': 5})
+ workbook.add_format({'top': 6})
+
+ workbook._set_default_xf_indices()
+ workbook._prepare_format_properties()
+
+ style._set_style_properties([
+ workbook.xf_formats,
+ workbook.palette,
+ workbook.font_count,
+ workbook.num_format_count,
+ workbook.border_count,
+ workbook.fill_count,
+ workbook.custom_colors,
+ workbook.dxf_formats,
+ ])
+
+ style._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
+ <fonts count="1">
+ <font>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ </fonts>
+ <fills count="2">
+ <fill>
+ <patternFill patternType="none"/>
+ </fill>
+ <fill>
+ <patternFill patternType="gray125"/>
+ </fill>
+ </fills>
+ <borders count="14">
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="hair">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="dotted">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="dashDotDot">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="dashDot">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="dashed">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="thin">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="mediumDashDotDot">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="slantDashDot">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="mediumDashDot">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="mediumDashed">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="medium">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="thick">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="double">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ </borders>
+ <cellStyleXfs count="1">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
+ </cellStyleXfs>
+ <cellXfs count="14">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="1" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="2" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="3" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="4" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="5" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="6" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="7" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="8" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="9" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="10" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="11" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="12" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="13" xfId="0" applyBorder="1"/>
+ </cellXfs>
+ <cellStyles count="1">
+ <cellStyle name="Normal" xfId="0" builtinId="0"/>
+ </cellStyles>
+ <dxfs count="0"/>
+ <tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleLight16"/>
+ </styleSheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_styles05.py b/xlsxwriter/test/styles/test_styles05.py
new file mode 100644
index 0000000..beaf1f1
--- /dev/null
+++ b/xlsxwriter/test/styles/test_styles05.py
@@ -0,0 +1,170 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...styles import Styles
+from ...workbook import Workbook
+
+
+class TestAssembleStyles(unittest.TestCase):
+ """
+ Test assembling a complete Styles file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Tests for diagonal border styles."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ style = Styles()
+ style._set_filehandle(fh)
+
+ workbook = Workbook()
+
+ workbook.add_format({'left': 1})
+ workbook.add_format({'right': 1})
+ workbook.add_format({'top': 1})
+ workbook.add_format({'bottom': 1})
+ workbook.add_format({'diag_type': 1, 'diag_border': 1})
+ workbook.add_format({'diag_type': 2, 'diag_border': 1})
+ workbook.add_format({'diag_type': 3}) # Test default border.
+
+ workbook._set_default_xf_indices()
+ workbook._prepare_format_properties()
+
+ style._set_style_properties([
+ workbook.xf_formats,
+ workbook.palette,
+ workbook.font_count,
+ workbook.num_format_count,
+ workbook.border_count,
+ workbook.fill_count,
+ workbook.custom_colors,
+ workbook.dxf_formats,
+ ])
+
+ style._assemble_xml_file()
+ workbook.fileclosed = 1
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
+ <fonts count="1">
+ <font>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ </fonts>
+ <fills count="2">
+ <fill>
+ <patternFill patternType="none"/>
+ </fill>
+ <fill>
+ <patternFill patternType="gray125"/>
+ </fill>
+ </fills>
+ <borders count="8">
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left style="thin">
+ <color auto="1"/>
+ </left>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right style="thin">
+ <color auto="1"/>
+ </right>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top style="thin">
+ <color auto="1"/>
+ </top>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom style="thin">
+ <color auto="1"/>
+ </bottom>
+ <diagonal/>
+ </border>
+ <border diagonalUp="1">
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal style="thin">
+ <color auto="1"/>
+ </diagonal>
+ </border>
+ <border diagonalDown="1">
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal style="thin">
+ <color auto="1"/>
+ </diagonal>
+ </border>
+ <border diagonalUp="1" diagonalDown="1">
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal style="thin">
+ <color auto="1"/>
+ </diagonal>
+ </border>
+ </borders>
+ <cellStyleXfs count="1">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
+ </cellStyleXfs>
+ <cellXfs count="8">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="1" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="2" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="3" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="4" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="5" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="6" xfId="0" applyBorder="1"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="7" xfId="0" applyBorder="1"/>
+ </cellXfs>
+ <cellStyles count="1">
+ <cellStyle name="Normal" xfId="0" builtinId="0"/>
+ </cellStyles>
+ <dxfs count="0"/>
+ <tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleLight16"/>
+ </styleSheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_styles06.py b/xlsxwriter/test/styles/test_styles06.py
new file mode 100644
index 0000000..b0900ee
--- /dev/null
+++ b/xlsxwriter/test/styles/test_styles06.py
@@ -0,0 +1,123 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...styles import Styles
+from ...workbook import Workbook
+
+
+class TestAssembleStyles(unittest.TestCase):
+ """
+ Test assembling a complete Styles file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test for border colour styles."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ style = Styles()
+ style._set_filehandle(fh)
+
+ workbook = Workbook()
+
+ workbook.add_format({
+ 'left': 1,
+ 'right': 1,
+ 'top': 1,
+ 'bottom': 1,
+ 'diag_border': 1,
+ 'diag_type': 3,
+ 'left_color': 'red',
+ 'right_color': 'red',
+ 'top_color': 'red',
+ 'bottom_color': 'red',
+ 'diag_color': 'red'})
+
+ workbook._set_default_xf_indices()
+ workbook._prepare_format_properties()
+
+ style._set_style_properties([
+ workbook.xf_formats,
+ workbook.palette,
+ workbook.font_count,
+ workbook.num_format_count,
+ workbook.border_count,
+ workbook.fill_count,
+ workbook.custom_colors,
+ workbook.dxf_formats,
+ ])
+
+ style._assemble_xml_file()
+ workbook.fileclosed = 1
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
+ <fonts count="1">
+ <font>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ </fonts>
+ <fills count="2">
+ <fill>
+ <patternFill patternType="none"/>
+ </fill>
+ <fill>
+ <patternFill patternType="gray125"/>
+ </fill>
+ </fills>
+ <borders count="2">
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <border diagonalUp="1" diagonalDown="1">
+ <left style="thin">
+ <color rgb="FFFF0000"/>
+ </left>
+ <right style="thin">
+ <color rgb="FFFF0000"/>
+ </right>
+ <top style="thin">
+ <color rgb="FFFF0000"/>
+ </top>
+ <bottom style="thin">
+ <color rgb="FFFF0000"/>
+ </bottom>
+ <diagonal style="thin">
+ <color rgb="FFFF0000"/>
+ </diagonal>
+ </border>
+ </borders>
+ <cellStyleXfs count="1">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
+ </cellStyleXfs>
+ <cellXfs count="2">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="1" xfId="0" applyBorder="1"/>
+ </cellXfs>
+ <cellStyles count="1">
+ <cellStyle name="Normal" xfId="0" builtinId="0"/>
+ </cellStyles>
+ <dxfs count="0"/>
+ <tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleLight16"/>
+ </styleSheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_styles07.py b/xlsxwriter/test/styles/test_styles07.py
new file mode 100644
index 0000000..8bf913b
--- /dev/null
+++ b/xlsxwriter/test/styles/test_styles07.py
@@ -0,0 +1,124 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...styles import Styles
+from ...workbook import Workbook
+
+
+class TestAssembleStyles(unittest.TestCase):
+ """
+ Test assembling a complete Styles file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test for simple fills."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ style = Styles()
+ style._set_filehandle(fh)
+
+ workbook = Workbook()
+
+ workbook.add_format({'pattern': 1, 'bg_color': 'red'})
+ workbook.add_format({'pattern': 11, 'bg_color': 'red'})
+ workbook.add_format({'pattern': 11, 'bg_color': 'red', 'fg_color': 'yellow'})
+ workbook.add_format({'pattern': 1, 'bg_color': 'red', 'fg_color': 'red'})
+
+ workbook._set_default_xf_indices()
+ workbook._prepare_format_properties()
+
+ style._set_style_properties([
+ workbook.xf_formats,
+ workbook.palette,
+ workbook.font_count,
+ workbook.num_format_count,
+ workbook.border_count,
+ workbook.fill_count,
+ workbook.custom_colors,
+ workbook.dxf_formats,
+ ])
+
+ style._assemble_xml_file()
+ workbook.fileclosed = 1
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
+ <fonts count="1">
+ <font>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ </fonts>
+ <fills count="6">
+ <fill>
+ <patternFill patternType="none"/>
+ </fill>
+ <fill>
+ <patternFill patternType="gray125"/>
+ </fill>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor rgb="FFFF0000"/>
+ <bgColor indexed="64"/>
+ </patternFill>
+ </fill>
+ <fill>
+ <patternFill patternType="lightHorizontal">
+ <bgColor rgb="FFFF0000"/>
+ </patternFill>
+ </fill>
+ <fill>
+ <patternFill patternType="lightHorizontal">
+ <fgColor rgb="FFFFFF00"/>
+ <bgColor rgb="FFFF0000"/>
+ </patternFill>
+ </fill>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor rgb="FFFF0000"/>
+ <bgColor rgb="FFFF0000"/>
+ </patternFill>
+ </fill>
+ </fills>
+ <borders count="1">
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ </borders>
+ <cellStyleXfs count="1">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
+ </cellStyleXfs>
+ <cellXfs count="5">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>
+ <xf numFmtId="0" fontId="0" fillId="2" borderId="0" xfId="0" applyFill="1"/>
+ <xf numFmtId="0" fontId="0" fillId="3" borderId="0" xfId="0" applyFill="1"/>
+ <xf numFmtId="0" fontId="0" fillId="4" borderId="0" xfId="0" applyFill="1"/>
+ <xf numFmtId="0" fontId="0" fillId="5" borderId="0" xfId="0" applyFill="1"/>
+ </cellXfs>
+ <cellStyles count="1">
+ <cellStyle name="Normal" xfId="0" builtinId="0"/>
+ </cellStyles>
+ <dxfs count="0"/>
+ <tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleLight16"/>
+ </styleSheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_styles08.py b/xlsxwriter/test/styles/test_styles08.py
new file mode 100644
index 0000000..867a828
--- /dev/null
+++ b/xlsxwriter/test/styles/test_styles08.py
@@ -0,0 +1,129 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...styles import Styles
+from ...workbook import Workbook
+
+
+class TestAssembleStyles(unittest.TestCase):
+ """
+ Test assembling a complete Styles file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test for simple fills with a default solid pattern."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ style = Styles()
+ style._set_filehandle(fh)
+
+ workbook = Workbook()
+
+ workbook.add_format({'pattern': 1, 'bg_color': 'red', 'bold': 1})
+ workbook.add_format({'bg_color': 'red', 'italic': 1})
+ workbook.add_format({'fg_color': 'red', 'underline': 1})
+
+ workbook._set_default_xf_indices()
+ workbook._prepare_format_properties()
+
+ style._set_style_properties([
+ workbook.xf_formats,
+ workbook.palette,
+ workbook.font_count,
+ workbook.num_format_count,
+ workbook.border_count,
+ workbook.fill_count,
+ workbook.custom_colors,
+ workbook.dxf_formats,
+ ])
+
+ style._assemble_xml_file()
+ workbook.fileclosed = 1
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
+ <fonts count="4">
+ <font>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ <font>
+ <b/>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ <font>
+ <i/>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ <font>
+ <u/>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ </fonts>
+ <fills count="3">
+ <fill>
+ <patternFill patternType="none"/>
+ </fill>
+ <fill>
+ <patternFill patternType="gray125"/>
+ </fill>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor rgb="FFFF0000"/>
+ <bgColor indexed="64"/>
+ </patternFill>
+ </fill>
+ </fills>
+ <borders count="1">
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ </borders>
+ <cellStyleXfs count="1">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
+ </cellStyleXfs>
+ <cellXfs count="4">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>
+ <xf numFmtId="0" fontId="1" fillId="2" borderId="0" xfId="0" applyFont="1" applyFill="1"/>
+ <xf numFmtId="0" fontId="2" fillId="2" borderId="0" xfId="0" applyFont="1" applyFill="1"/>
+ <xf numFmtId="0" fontId="3" fillId="2" borderId="0" xfId="0" applyFont="1" applyFill="1"/>
+ </cellXfs>
+ <cellStyles count="1">
+ <cellStyle name="Normal" xfId="0" builtinId="0"/>
+ </cellStyles>
+ <dxfs count="0"/>
+ <tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleLight16"/>
+ </styleSheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_styles09.py b/xlsxwriter/test/styles/test_styles09.py
new file mode 100644
index 0000000..1525603
--- /dev/null
+++ b/xlsxwriter/test/styles/test_styles09.py
@@ -0,0 +1,116 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...styles import Styles
+from ...workbook import Workbook
+
+
+class TestAssembleStyles(unittest.TestCase):
+ """
+ Test assembling a complete Styles file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test for simple font styles."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ style = Styles()
+ style._set_filehandle(fh)
+
+ workbook = Workbook()
+
+ format1 = workbook.add_format({
+ 'font_color': '#9C0006',
+ 'bg_color': '#FFC7CE',
+ 'font_condense': 1,
+ 'font_extend': 1,
+ 'has_fill': 1,
+ 'has_font': 1,
+ })
+
+ # Get (and set) the DXF format index.
+ format1._get_dxf_index()
+
+ workbook._prepare_format_properties()
+
+ style._set_style_properties([
+ workbook.xf_formats,
+ workbook.palette,
+ workbook.font_count,
+ workbook.num_format_count,
+ workbook.border_count,
+ workbook.fill_count,
+ workbook.custom_colors,
+ workbook.dxf_formats,
+ ])
+
+ style._assemble_xml_file()
+ workbook.fileclosed = 1
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
+ <fonts count="1">
+ <font>
+ <sz val="11"/>
+ <color theme="1"/>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <scheme val="minor"/>
+ </font>
+ </fonts>
+ <fills count="2">
+ <fill>
+ <patternFill patternType="none"/>
+ </fill>
+ <fill>
+ <patternFill patternType="gray125"/>
+ </fill>
+ </fills>
+ <borders count="1">
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ </borders>
+ <cellStyleXfs count="1">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
+ </cellStyleXfs>
+ <cellXfs count="1">
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>
+ </cellXfs>
+ <cellStyles count="1">
+ <cellStyle name="Normal" xfId="0" builtinId="0"/>
+ </cellStyles>
+ <dxfs count="1">
+ <dxf>
+ <font>
+ <condense val="0"/>
+ <extend val="0"/>
+ <color rgb="FF9C0006"/>
+ </font>
+ <fill>
+ <patternFill>
+ <bgColor rgb="FFFFC7CE"/>
+ </patternFill>
+ </fill>
+ </dxf>
+ </dxfs>
+ <tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleLight16"/>
+ </styleSheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_border.py b/xlsxwriter/test/styles/test_write_border.py
new file mode 100644
index 0000000..270ee6f
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_border.py
@@ -0,0 +1,36 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+from ...format import Format
+
+
+class TestWriteBorder(unittest.TestCase):
+ """
+ Test the Styles _write_border() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_border(self):
+ """Test the _write_border() method"""
+
+ xf_format = Format()
+ xf_format.has_border = 1
+
+ self.styles._write_border(xf_format)
+
+ exp = """<border><left/><right/><top/><bottom/><diagonal/></border>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_borders.py b/xlsxwriter/test/styles/test_write_borders.py
new file mode 100644
index 0000000..0dbb8d2
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_borders.py
@@ -0,0 +1,38 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+from ...format import Format
+
+
+class TestWriteBorders(unittest.TestCase):
+ """
+ Test the Styles _write_borders() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_borders(self):
+ """Test the _write_borders() method"""
+
+ xf_format = Format()
+ xf_format.has_border = 1
+
+ self.styles._set_style_properties([[xf_format], None, 0, 0, 1, 0, [], []])
+
+ self.styles._write_borders()
+
+ exp = """<borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_cell_style.py b/xlsxwriter/test/styles/test_write_cell_style.py
new file mode 100644
index 0000000..5d06277
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_cell_style.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+
+
+class TestWriteCellStyle(unittest.TestCase):
+ """
+ Test the Styles _write_cell_style() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_cell_style(self):
+ """Test the _write_cell_style() method"""
+
+ self.styles._write_cell_style()
+
+ exp = """<cellStyle name="Normal" xfId="0" builtinId="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_cell_style_xfs.py b/xlsxwriter/test/styles/test_write_cell_style_xfs.py
new file mode 100644
index 0000000..c84b7fa
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_cell_style_xfs.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+
+
+class TestWriteCellStyleXfs(unittest.TestCase):
+ """
+ Test the Styles _write_cell_style_xfs() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_cell_style_xfs(self):
+ """Test the _write_cell_style_xfs() method"""
+
+ self.styles._write_cell_style_xfs()
+
+ exp = """<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_cell_styles.py b/xlsxwriter/test/styles/test_write_cell_styles.py
new file mode 100644
index 0000000..a153272
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_cell_styles.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+
+
+class TestWriteCellStyles(unittest.TestCase):
+ """
+ Test the Styles _write_cell_styles() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_cell_styles(self):
+ """Test the _write_cell_styles() method"""
+
+ self.styles._write_cell_styles()
+
+ exp = """<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_cell_xfs.py b/xlsxwriter/test/styles/test_write_cell_xfs.py
new file mode 100644
index 0000000..7fec78e
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_cell_xfs.py
@@ -0,0 +1,38 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+from ...format import Format
+
+
+class TestWriteCellXfs(unittest.TestCase):
+ """
+ Test the Styles _write_cell_xfs() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_cell_xfs(self):
+ """Test the _write_cell_xfs() method"""
+
+ xf_format = Format()
+ xf_format.has_font = 1
+
+ self.styles._set_style_properties([[xf_format], None, 1, 0, 0, 0, [], []])
+
+ self.styles._write_cell_xfs()
+
+ exp = """<cellXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/></cellXfs>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_colors.py b/xlsxwriter/test/styles/test_write_colors.py
new file mode 100644
index 0000000..ae67e0e
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_colors.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+
+
+class TestWriteColors(unittest.TestCase):
+ """
+ Test the Styles _write_colors() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_colors1(self):
+ """Test the _write_colors() method"""
+
+ self.styles.custom_colors = ['FF26DA55']
+ self.styles._write_colors()
+
+ exp = """<colors><mruColors><color rgb="FF26DA55"/></mruColors></colors>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_colors2(self):
+ """Test the _write_colors() method"""
+
+ self.styles.custom_colors = ['FF26DA55', 'FF792DC8', 'FF646462']
+ self.styles._write_colors()
+
+ exp = """<colors><mruColors><color rgb="FF646462"/><color rgb="FF792DC8"/><color rgb="FF26DA55"/></mruColors></colors>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_colors3(self):
+ """Test the _write_colors() method"""
+
+ self.styles.custom_colors = ['FF792DC8', 'FF646462', 'FF5EA29C',
+ 'FF583AC6', 'FFE31DAF', 'FFA1A759',
+ 'FF600FF1', 'FF0CF49C', 'FFE3FA06',
+ 'FF913AC6', 'FFB97847', 'FFD97827']
+
+ self.styles._write_colors()
+
+ exp = """<colors><mruColors><color rgb="FFD97827"/><color rgb="FFB97847"/><color rgb="FF913AC6"/><color rgb="FFE3FA06"/><color rgb="FF0CF49C"/><color rgb="FF600FF1"/><color rgb="FFA1A759"/><color rgb="FFE31DAF"/><color rgb="FF583AC6"/><color rgb="FF5EA29C"/></mruColors></colors>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_default_fill.py b/xlsxwriter/test/styles/test_write_default_fill.py
new file mode 100644
index 0000000..15537bb
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_default_fill.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+
+
+class TestWriteDefaultFill(unittest.TestCase):
+ """
+ Test the Styles _write_default_fill() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_default_fill(self):
+ """Test the _write_default_fill() method"""
+
+ self.styles._write_default_fill('none')
+
+ exp = """<fill><patternFill patternType="none"/></fill>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_dxfs.py b/xlsxwriter/test/styles/test_write_dxfs.py
new file mode 100644
index 0000000..a2285a2
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_dxfs.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+
+
+class TestWriteDxfs(unittest.TestCase):
+ """
+ Test the Styles _write_dxfs() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_dxfs(self):
+ """Test the _write_dxfs() method"""
+
+ self.styles._write_dxfs()
+
+ exp = """<dxfs count="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_fills.py b/xlsxwriter/test/styles/test_write_fills.py
new file mode 100644
index 0000000..6b6140b
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_fills.py
@@ -0,0 +1,34 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+
+
+class TestWriteFills(unittest.TestCase):
+ """
+ Test the Styles _write_fills() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_fills(self):
+ """Test the _write_fills() method"""
+
+ self.styles.fill_count = 2
+
+ self.styles._write_fills()
+
+ exp = """<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_font.py b/xlsxwriter/test/styles/test_write_font.py
new file mode 100644
index 0000000..406a0f3
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_font.py
@@ -0,0 +1,254 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+from ...format import Format
+
+
+class TestWriteFont(unittest.TestCase):
+ """
+ Test the Styles _write_font() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_font_1(self):
+ """Test the _write_font() method. Default properties."""
+ properties = {}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_2(self):
+ """Test the _write_font() method. Bold."""
+ properties = {'bold': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><b/><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_3(self):
+ """Test the _write_font() method. Italic."""
+ properties = {'italic': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><i/><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_4(self):
+ """Test the _write_font() method. Underline."""
+ properties = {'underline': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><u/><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_5(self):
+ """Test the _write_font() method. Strikeout."""
+ properties = {'font_strikeout': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><strike/><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_6(self):
+ """Test the _write_font() method. Superscript."""
+ properties = {'font_script': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><vertAlign val="superscript"/><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_7(self):
+ """Test the _write_font() method. Subscript."""
+ properties = {'font_script': 2}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><vertAlign val="subscript"/><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_8(self):
+ """Test the _write_font() method. Font name."""
+ properties = {'font_name': 'Arial'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><sz val="11"/><color theme="1"/><name val="Arial"/><family val="2"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_9(self):
+ """Test the _write_font() method. Font size."""
+ properties = {'size': 12}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><sz val="12"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_10(self):
+ """Test the _write_font() method. Outline."""
+ properties = {'font_outline': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><outline/><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_11(self):
+ """Test the _write_font() method. Shadow."""
+ properties = {'font_shadow': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><shadow/><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_12(self):
+ """Test the _write_font() method. Colour = red."""
+ properties = {'color': '#FF0000'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><sz val="11"/><color rgb="FFFF0000"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_13(self):
+ """Test the _write_font() method. All font attributes to check order."""
+ properties = {
+ 'bold': 1,
+ 'color': '#FF0000',
+ 'font_outline': 1,
+ 'font_script': 1,
+ 'font_shadow': 1,
+ 'font_strikeout': 1,
+ 'italic': 1,
+ 'size': 12,
+ 'underline': 1,
+ }
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><b/><i/><strike/><outline/><shadow/><u/><vertAlign val="superscript"/><sz val="12"/><color rgb="FFFF0000"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_14(self):
+ """Test the _write_font() method. Double underline."""
+ properties = {'underline': 2}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><u val="double"/><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_15(self):
+ """Test the _write_font() method. Double underline."""
+ properties = {'underline': 33}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><u val="singleAccounting"/><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_16(self):
+ """Test the _write_font() method. Double underline."""
+ properties = {'underline': 34}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><u val="doubleAccounting"/><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_font_17(self):
+ """Test the _write_font() method. Hyperlink."""
+ properties = {'hyperlink': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_font(xf_format)
+
+ exp = """<font><u/><sz val="11"/><color theme="10"/><name val="Calibri"/><family val="2"/></font>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_fonts.py b/xlsxwriter/test/styles/test_write_fonts.py
new file mode 100644
index 0000000..ee8816d
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_fonts.py
@@ -0,0 +1,38 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+from ...format import Format
+
+
+class TestWriteFonts(unittest.TestCase):
+ """
+ Test the Styles _write_fonts() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_fonts(self):
+ """Test the _write_fonts() method"""
+
+ xf_format = Format()
+ xf_format.has_font = 1
+
+ self.styles._set_style_properties([[xf_format], None, 1, 0, 0, 0, [], []])
+
+ self.styles._write_fonts()
+
+ exp = """<fonts count="1"><font><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font></fonts>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_mru_colors.py b/xlsxwriter/test/styles/test_write_mru_colors.py
new file mode 100644
index 0000000..c58f7db
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_mru_colors.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+
+
+class TestWriteMruColors(unittest.TestCase):
+ """
+ Test the Styles _write_mru_colors() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_mru_colors(self):
+ """Test the _write_mru_colors() method"""
+
+ self.styles._write_mru_colors(['FF26DA55', 'FF792DC8', 'FF646462'])
+
+ exp = """<mruColors><color rgb="FF646462"/><color rgb="FF792DC8"/><color rgb="FF26DA55"/></mruColors>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_num_fmt.py b/xlsxwriter/test/styles/test_write_num_fmt.py
new file mode 100644
index 0000000..d69fcb7
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_num_fmt.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+
+
+class TestWriteNumFmt(unittest.TestCase):
+ """
+ Test the Styles _write_num_fmt() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_num_fmt(self):
+ """Test the _write_num_fmt() method"""
+
+ self.styles._write_num_fmt(164, '#,##0.0')
+
+ exp = """<numFmt numFmtId="164" formatCode="#,##0.0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_num_fmts.py b/xlsxwriter/test/styles/test_write_num_fmts.py
new file mode 100644
index 0000000..39a3c78
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_num_fmts.py
@@ -0,0 +1,39 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+from ...format import Format
+
+
+class TestWriteNumFmts(unittest.TestCase):
+ """
+ Test the Styles _write_num_fmts() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_num_fmts(self):
+ """Test the _write_num_fmts() method"""
+
+ xf_format = Format()
+ xf_format.num_format_index = 164
+ xf_format.set_num_format('#,##0.0')
+
+ self.styles._set_style_properties([[xf_format], None, 0, 1, 0, 0, [], []])
+
+ self.styles._write_num_fmts()
+
+ exp = """<numFmts count="1"><numFmt numFmtId="164" formatCode="#,##0.0"/></numFmts>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_style_sheet.py b/xlsxwriter/test/styles/test_write_style_sheet.py
new file mode 100644
index 0000000..f7eeffb
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_style_sheet.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+
+
+class TestWriteStyleSheet(unittest.TestCase):
+ """
+ Test the Styles _write_style_sheet() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_style_sheet(self):
+ """Test the _write_style_sheet() method"""
+
+ self.styles._write_style_sheet()
+
+ exp = """<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_style_xf.py b/xlsxwriter/test/styles/test_write_style_xf.py
new file mode 100644
index 0000000..4e26dc8
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_style_xf.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+
+
+class TestWriteStyleXf(unittest.TestCase):
+ """
+ Test the Styles _write_style_xf() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_style_xf(self):
+ """Test the _write_style_xf() method"""
+
+ self.styles._write_style_xf()
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_xf.py b/xlsxwriter/test/styles/test_write_xf.py
new file mode 100644
index 0000000..636f4a9
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_xf.py
@@ -0,0 +1,491 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+from ...format import Format
+
+
+class TestWriteXf(unittest.TestCase):
+ """
+ Test the Styles _write_xf() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_xf_1(self):
+ """Test the _write_xf() method. Default properties."""
+ properties = {}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_2(self):
+ """Test the _write_xf() method. Has font but is first XF."""
+ properties = {'has_font': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_3(self):
+ """Test the _write_xf() method. Has font but isn't first XF."""
+ properties = {'has_font': 1, 'font_index': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="1" fillId="0" borderId="0" xfId="0" applyFont="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_4(self):
+ """Test the _write_xf() method. Uses built-in number format."""
+ properties = {'num_format_index': 2}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="2" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_5(self):
+ """Test the _write_xf() method. Uses built-in number format + font."""
+ properties = {'num_format_index': 2, 'has_font': 1, 'font_index': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="2" fontId="1" fillId="0" borderId="0" xfId="0" applyNumberFormat="1" applyFont="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_6(self):
+ """Test the _write_xf() method. Vertical alignment = top."""
+ properties = {'align': 'top'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment vertical="top"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_7(self):
+ """Test the _write_xf() method. Vertical alignment = centre."""
+ properties = {'align': 'vcenter'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment vertical="center"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_8(self):
+ """Test the _write_xf() method. Vertical alignment = bottom."""
+ properties = {'align': 'bottom'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_9(self):
+ """Test the _write_xf() method. Vertical alignment = justify."""
+ properties = {'align': 'vjustify'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment vertical="justify"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_10(self):
+ """Test the _write_xf() method. Vertical alignment = distributed."""
+ properties = {'align': 'vdistributed'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment vertical="distributed"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_11(self):
+ """Test the _write_xf() method. Horizontal alignment = left."""
+ properties = {'align': 'left'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="left"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_12(self):
+ """Test the _write_xf() method. Horizontal alignment = center."""
+ properties = {'align': 'center'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="center"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_13(self):
+ """Test the _write_xf() method. Horizontal alignment = right."""
+ properties = {'align': 'right'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="right"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_14(self):
+ """Test the _write_xf() method. Horizontal alignment = left + indent."""
+ properties = {'align': 'left', 'indent': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="left" indent="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_15(self):
+ """Test the _write_xf() method. Horizontal alignment = right + indent."""
+ properties = {'align': 'right', 'indent': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="right" indent="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_16(self):
+ """Test the _write_xf() method. Horizontal alignment = fill."""
+ properties = {'align': 'fill'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="fill"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_17(self):
+ """Test the _write_xf() method. Horizontal alignment = justify."""
+ properties = {'align': 'justify'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="justify"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_18(self):
+ """Test the _write_xf() method. Horizontal alignment = center across."""
+ properties = {'align': 'center_across'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="centerContinuous"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_19(self):
+ """Test the _write_xf() method. Horizontal alignment = distributed."""
+ properties = {'align': 'distributed'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="distributed"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_20(self):
+ """Test the _write_xf() method. Horizontal alignment = distributed + indent."""
+ properties = {'align': 'distributed', 'indent': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="distributed" indent="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_21(self):
+ """Test the _write_xf() method. Horizontal alignment = justify distributed."""
+ properties = {'align': 'justify_distributed'}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="distributed" justifyLastLine="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_22(self):
+ """Test the _write_xf() method. Horizontal alignment = indent only."""
+ properties = {'indent': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="left" indent="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_23(self):
+ """Test the _write_xf() method. Horizontal alignment = distributed + indent."""
+ properties = {'align': 'justify_distributed', 'indent': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="distributed" indent="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_24(self):
+ """Test the _write_xf() method. Alignment = text wrap"""
+ properties = {'text_wrap': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment wrapText="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_25(self):
+ """Test the _write_xf() method. Alignment = shrink to fit"""
+ properties = {'shrink': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment shrinkToFit="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_26(self):
+ """Test the _write_xf() method. Alignment = reading order"""
+ properties = {'reading_order': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment readingOrder="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_27(self):
+ """Test the _write_xf() method. Alignment = reading order"""
+ properties = {'reading_order': 2}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment readingOrder="2"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_28(self):
+ """Test the _write_xf() method. Alignment = rotation"""
+ properties = {'rotation': 45}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment textRotation="45"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_29(self):
+ """Test the _write_xf() method. Alignment = rotation"""
+ properties = {'rotation': -45}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment textRotation="135"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_30(self):
+ """Test the _write_xf() method. Alignment = rotation"""
+ properties = {'rotation': 270}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment textRotation="255"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_31(self):
+ """Test the _write_xf() method. Alignment = rotation"""
+ properties = {'rotation': 90}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment textRotation="90"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_32(self):
+ """Test the _write_xf() method. Alignment = rotation"""
+ properties = {'rotation': -90}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment textRotation="180"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_33(self):
+ """Test the _write_xf() method. With cell protection."""
+ properties = {'locked': 0}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyProtection="1"><protection locked="0"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_34(self):
+ """Test the _write_xf() method. With cell protection."""
+ properties = {'hidden': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyProtection="1"><protection hidden="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_35(self):
+ """Test the _write_xf() method. With cell protection."""
+ properties = {'locked': 0, 'hidden': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyProtection="1"><protection locked="0" hidden="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_36(self):
+ """Test the _write_xf() method. With cell protection + align."""
+ properties = {'align': 'right', 'locked': 0, 'hidden': 1}
+
+ xf_format = Format(properties)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1" applyProtection="1"><alignment horizontal="right"/><protection locked="0" hidden="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/styles/test_write_xf_methods.py b/xlsxwriter/test/styles/test_write_xf_methods.py
new file mode 100644
index 0000000..88885e1
--- /dev/null
+++ b/xlsxwriter/test/styles/test_write_xf_methods.py
@@ -0,0 +1,501 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...styles import Styles
+from ...format import Format
+
+
+class TestWriteXf(unittest.TestCase):
+ """
+ Test the Styles _write_xf() method. This test case is similar to
+ test_write_xf.py but with methods calls instead of properties.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.styles = Styles()
+ self.styles._set_filehandle(self.fh)
+
+ def test_write_xf_1(self):
+ """Test the _write_xf() method. Default properties."""
+
+ xf_format = Format()
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_2(self):
+ """Test the _write_xf() method. Has font but is first XF."""
+
+ xf_format = Format()
+ xf_format.set_has_font()
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_3(self):
+ """Test the _write_xf() method. Has font but isn't first XF."""
+
+ xf_format = Format()
+ xf_format.set_has_font()
+ xf_format.set_font_index(1)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="1" fillId="0" borderId="0" xfId="0" applyFont="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_4(self):
+ """Test the _write_xf() method. Uses built-in number format."""
+
+ xf_format = Format()
+ xf_format.set_num_format_index(2)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="2" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_5(self):
+ """Test the _write_xf() method. Uses built-in number format + font."""
+
+ xf_format = Format()
+ xf_format.set_num_format_index(2)
+ xf_format.set_has_font()
+ xf_format.set_font_index(1)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="2" fontId="1" fillId="0" borderId="0" xfId="0" applyNumberFormat="1" applyFont="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_6(self):
+ """Test the _write_xf() method. Vertical alignment = top."""
+
+ xf_format = Format()
+ xf_format.set_align('top')
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment vertical="top"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_7(self):
+ """Test the _write_xf() method. Vertical alignment = centre."""
+
+ xf_format = Format()
+ xf_format.set_align('vcenter')
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment vertical="center"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_8(self):
+ """Test the _write_xf() method. Vertical alignment = bottom."""
+
+ xf_format = Format()
+ xf_format.set_align('bottom')
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_9(self):
+ """Test the _write_xf() method. Vertical alignment = justify."""
+
+ xf_format = Format()
+ xf_format.set_align('vjustify')
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment vertical="justify"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_10(self):
+ """Test the _write_xf() method. Vertical alignment = distributed."""
+
+ xf_format = Format()
+ xf_format.set_align('vdistributed')
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment vertical="distributed"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_11(self):
+ """Test the _write_xf() method. Horizontal alignment = left."""
+
+ xf_format = Format()
+ xf_format.set_align('left')
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="left"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_12(self):
+ """Test the _write_xf() method. Horizontal alignment = center."""
+
+ xf_format = Format()
+ xf_format.set_align('center')
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="center"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_13(self):
+ """Test the _write_xf() method. Horizontal alignment = right."""
+
+ xf_format = Format()
+ xf_format.set_align('right')
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="right"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_14(self):
+ """Test the _write_xf() method. Horizontal alignment = left + indent."""
+
+ xf_format = Format()
+ xf_format.set_align('left')
+ xf_format.set_indent()
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="left" indent="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_15(self):
+ """Test the _write_xf() method. Horizontal alignment = right + indent."""
+
+ xf_format = Format()
+ xf_format.set_align('right')
+ xf_format.set_indent()
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="right" indent="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_16(self):
+ """Test the _write_xf() method. Horizontal alignment = fill."""
+
+ xf_format = Format()
+ xf_format.set_align('fill')
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="fill"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_17(self):
+ """Test the _write_xf() method. Horizontal alignment = justify."""
+
+ xf_format = Format()
+ xf_format.set_align('justify')
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="justify"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_18(self):
+ """Test the _write_xf() method. Horizontal alignment = center across."""
+
+ xf_format = Format()
+ xf_format.set_align('center_across')
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="centerContinuous"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_19(self):
+ """Test the _write_xf() method. Horizontal alignment = distributed."""
+
+ xf_format = Format()
+ xf_format.set_align('distributed')
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="distributed"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_20(self):
+ """Test the _write_xf() method. Horizontal alignment = distributed + indent."""
+
+ xf_format = Format()
+ xf_format.set_align('distributed')
+ xf_format.set_indent()
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="distributed" indent="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_21(self):
+ """Test the _write_xf() method. Horizontal alignment = justify distributed."""
+
+ xf_format = Format()
+ xf_format.set_align('justify_distributed')
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="distributed" justifyLastLine="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_22(self):
+ """Test the _write_xf() method. Horizontal alignment = indent only."""
+
+ xf_format = Format()
+ xf_format.set_indent()
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="left" indent="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_23(self):
+ """Test the _write_xf() method. Horizontal alignment = distributed + indent."""
+
+ xf_format = Format()
+ xf_format.set_align('justify_distributed')
+ xf_format.set_indent()
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="distributed" indent="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_24(self):
+ """Test the _write_xf() method. Alignment = text wrap"""
+
+ xf_format = Format()
+ xf_format.set_text_wrap()
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment wrapText="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_25(self):
+ """Test the _write_xf() method. Alignment = shrink to fit"""
+
+ xf_format = Format()
+ xf_format.set_shrink()
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment shrinkToFit="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_26(self):
+ """Test the _write_xf() method. Alignment = reading order"""
+
+ xf_format = Format()
+ xf_format.set_reading_order()
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment readingOrder="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_27(self):
+ """Test the _write_xf() method. Alignment = reading order"""
+
+ xf_format = Format()
+ xf_format.set_reading_order(2)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment readingOrder="2"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_28(self):
+ """Test the _write_xf() method. Alignment = rotation"""
+
+ xf_format = Format()
+ xf_format.set_rotation(45)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment textRotation="45"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_29(self):
+ """Test the _write_xf() method. Alignment = rotation"""
+
+ xf_format = Format()
+ xf_format.set_rotation(-45)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment textRotation="135"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_30(self):
+ """Test the _write_xf() method. Alignment = rotation"""
+
+ xf_format = Format()
+ xf_format.set_rotation(270)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment textRotation="255"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_31(self):
+ """Test the _write_xf() method. Alignment = rotation"""
+
+ xf_format = Format()
+ xf_format.set_rotation(90)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment textRotation="90"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_32(self):
+ """Test the _write_xf() method. Alignment = rotation"""
+
+ xf_format = Format()
+ xf_format.set_rotation(-90)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment textRotation="180"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_33(self):
+ """Test the _write_xf() method. With cell protection."""
+
+ xf_format = Format()
+ xf_format.set_locked(0)
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyProtection="1"><protection locked="0"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_34(self):
+ """Test the _write_xf() method. With cell protection."""
+
+ xf_format = Format()
+ xf_format.set_hidden()
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyProtection="1"><protection hidden="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_35(self):
+ """Test the _write_xf() method. With cell protection."""
+
+ xf_format = Format()
+ xf_format.set_locked(0)
+ xf_format.set_hidden()
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyProtection="1"><protection locked="0" hidden="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_xf_36(self):
+ """Test the _write_xf() method. With cell protection + align."""
+
+ xf_format = Format()
+ xf_format.set_align('right')
+ xf_format.set_locked(0)
+ xf_format.set_hidden()
+
+ self.styles._write_xf(xf_format)
+
+ exp = """<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1" applyProtection="1"><alignment horizontal="right"/><protection locked="0" hidden="1"/></xf>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/__init__.py b/xlsxwriter/test/table/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/table/test_initialisation.py b/xlsxwriter/test/table/test_initialisation.py
new file mode 100644
index 0000000..50896c8
--- /dev/null
+++ b/xlsxwriter/test/table/test_initialisation.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...table import Table
+
+
+class TestInitialisation(unittest.TestCase):
+ """
+ Test initialisation of the Table class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.table = Table()
+ self.table._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test Table xml_declaration()"""
+
+ self.table._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_table01.py b/xlsxwriter/test/table/test_table01.py
new file mode 100644
index 0000000..1c8851a
--- /dev/null
+++ b/xlsxwriter/test/table/test_table01.py
@@ -0,0 +1,57 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...table import Table
+from ...worksheet import Worksheet
+from ...workbook import WorksheetMeta
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleTable(unittest.TestCase):
+ """
+ Test assembling a complete Table file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a table"""
+ self.maxDiff = None
+
+ worksheet = Worksheet()
+ worksheet.worksheet_meta = WorksheetMeta()
+ worksheet.str_table = SharedStringTable()
+
+ worksheet.add_table('C3:F13')
+ worksheet._prepare_tables(1, {})
+
+ fh = StringIO()
+ table = Table()
+ table._set_filehandle(fh)
+
+ table._set_properties(worksheet.tables[0])
+
+ table._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="C3:F13" totalsRowShown="0">
+ <autoFilter ref="C3:F13"/>
+ <tableColumns count="4">
+ <tableColumn id="1" name="Column1"/>
+ <tableColumn id="2" name="Column2"/>
+ <tableColumn id="3" name="Column3"/>
+ <tableColumn id="4" name="Column4"/>
+ </tableColumns>
+ <tableStyleInfo name="TableStyleMedium9" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>
+ </table>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_table02.py b/xlsxwriter/test/table/test_table02.py
new file mode 100644
index 0000000..cb39059
--- /dev/null
+++ b/xlsxwriter/test/table/test_table02.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...table import Table
+from ...worksheet import Worksheet
+from ...workbook import WorksheetMeta
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleTable(unittest.TestCase):
+ """
+ Test assembling a complete Table file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a table"""
+ self.maxDiff = None
+
+ worksheet = Worksheet()
+ worksheet.worksheet_meta = WorksheetMeta()
+ worksheet.str_table = SharedStringTable()
+
+ worksheet.add_table('D4:I15', {'style': 'Table Style Light 17'})
+ worksheet._prepare_tables(1, {})
+
+ fh = StringIO()
+ table = Table()
+ table._set_filehandle(fh)
+
+ table._set_properties(worksheet.tables[0])
+
+ table._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="D4:I15" totalsRowShown="0">
+ <autoFilter ref="D4:I15"/>
+ <tableColumns count="6">
+ <tableColumn id="1" name="Column1"/>
+ <tableColumn id="2" name="Column2"/>
+ <tableColumn id="3" name="Column3"/>
+ <tableColumn id="4" name="Column4"/>
+ <tableColumn id="5" name="Column5"/>
+ <tableColumn id="6" name="Column6"/>
+ </tableColumns>
+ <tableStyleInfo name="TableStyleLight17" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>
+ </table>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_table03.py b/xlsxwriter/test/table/test_table03.py
new file mode 100644
index 0000000..f6f54ea
--- /dev/null
+++ b/xlsxwriter/test/table/test_table03.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...table import Table
+from ...worksheet import Worksheet
+from ...workbook import WorksheetMeta
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleTable(unittest.TestCase):
+ """
+ Test assembling a complete Table file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a table"""
+ self.maxDiff = None
+
+ worksheet = Worksheet()
+ worksheet.worksheet_meta = WorksheetMeta()
+ worksheet.str_table = SharedStringTable()
+
+ worksheet.add_table('C5:D16', {'banded_rows': 0,
+ 'first_column': 1,
+ 'last_column': 1,
+ 'banded_columns': 1
+ })
+ worksheet._prepare_tables(1, {})
+
+ fh = StringIO()
+ table = Table()
+ table._set_filehandle(fh)
+
+ table._set_properties(worksheet.tables[0])
+
+ table._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="C5:D16" totalsRowShown="0">
+ <autoFilter ref="C5:D16"/>
+ <tableColumns count="2">
+ <tableColumn id="1" name="Column1"/>
+ <tableColumn id="2" name="Column2"/>
+ </tableColumns>
+ <tableStyleInfo name="TableStyleMedium9" showFirstColumn="1" showLastColumn="1" showRowStripes="0" showColumnStripes="1"/>
+ </table>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_table04.py b/xlsxwriter/test/table/test_table04.py
new file mode 100644
index 0000000..4e514aa
--- /dev/null
+++ b/xlsxwriter/test/table/test_table04.py
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...table import Table
+from ...worksheet import Worksheet
+from ...workbook import WorksheetMeta
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleTable(unittest.TestCase):
+ """
+ Test assembling a complete Table file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a table"""
+ self.maxDiff = None
+
+ worksheet = Worksheet()
+ worksheet.worksheet_meta = WorksheetMeta()
+ worksheet.str_table = SharedStringTable()
+
+ worksheet.add_table('C3:F13', {'autofilter': False})
+ worksheet._prepare_tables(1, {})
+
+ fh = StringIO()
+ table = Table()
+ table._set_filehandle(fh)
+
+ table._set_properties(worksheet.tables[0])
+
+ table._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="C3:F13" totalsRowShown="0">
+ <tableColumns count="4">
+ <tableColumn id="1" name="Column1"/>
+ <tableColumn id="2" name="Column2"/>
+ <tableColumn id="3" name="Column3"/>
+ <tableColumn id="4" name="Column4"/>
+ </tableColumns>
+ <tableStyleInfo name="TableStyleMedium9" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>
+ </table>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_table05.py b/xlsxwriter/test/table/test_table05.py
new file mode 100644
index 0000000..f28ebb1
--- /dev/null
+++ b/xlsxwriter/test/table/test_table05.py
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...table import Table
+from ...worksheet import Worksheet
+from ...workbook import WorksheetMeta
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleTable(unittest.TestCase):
+ """
+ Test assembling a complete Table file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a table"""
+ self.maxDiff = None
+
+ worksheet = Worksheet()
+ worksheet.worksheet_meta = WorksheetMeta()
+ worksheet.str_table = SharedStringTable()
+
+ worksheet.add_table('C4:F13', {'header_row': False})
+ worksheet._prepare_tables(1, {})
+
+ fh = StringIO()
+ table = Table()
+ table._set_filehandle(fh)
+
+ table._set_properties(worksheet.tables[0])
+
+ table._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="C4:F13" headerRowCount="0" totalsRowShown="0">
+ <tableColumns count="4">
+ <tableColumn id="1" name="Column1"/>
+ <tableColumn id="2" name="Column2"/>
+ <tableColumn id="3" name="Column3"/>
+ <tableColumn id="4" name="Column4"/>
+ </tableColumns>
+ <tableStyleInfo name="TableStyleMedium9" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>
+ </table>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_table06.py b/xlsxwriter/test/table/test_table06.py
new file mode 100644
index 0000000..e7230dc
--- /dev/null
+++ b/xlsxwriter/test/table/test_table06.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...table import Table
+from ...worksheet import Worksheet
+from ...workbook import WorksheetMeta
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleTable(unittest.TestCase):
+ """
+ Test assembling a complete Table file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a table"""
+ self.maxDiff = None
+
+ worksheet = Worksheet()
+ worksheet.worksheet_meta = WorksheetMeta()
+ worksheet.str_table = SharedStringTable()
+
+ # Set the table properties.
+ worksheet.add_table('C3:F13', {'columns': [{'header': 'Foo'},
+ {'header': ''},
+ {},
+ {'header': 'Baz'}
+ ]})
+ worksheet._prepare_tables(1, {})
+
+ fh = StringIO()
+ table = Table()
+ table._set_filehandle(fh)
+
+ table._set_properties(worksheet.tables[0])
+
+ table._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="C3:F13" totalsRowShown="0">
+ <autoFilter ref="C3:F13"/>
+ <tableColumns count="4">
+ <tableColumn id="1" name="Foo"/>
+ <tableColumn id="2" name="Column2"/>
+ <tableColumn id="3" name="Column3"/>
+ <tableColumn id="4" name="Baz"/>
+ </tableColumns>
+ <tableStyleInfo name="TableStyleMedium9" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>
+ </table>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_table07.py b/xlsxwriter/test/table/test_table07.py
new file mode 100644
index 0000000..2047417
--- /dev/null
+++ b/xlsxwriter/test/table/test_table07.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...table import Table
+from ...worksheet import Worksheet
+from ...workbook import WorksheetMeta
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleTable(unittest.TestCase):
+ """
+ Test assembling a complete Table file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a table"""
+ self.maxDiff = None
+
+ worksheet = Worksheet()
+ worksheet.worksheet_meta = WorksheetMeta()
+ worksheet.str_table = SharedStringTable()
+
+ # Set the table properties.
+ worksheet.add_table('C3:F14', {'total_row': 1})
+ worksheet._prepare_tables(1, {})
+
+ fh = StringIO()
+ table = Table()
+ table._set_filehandle(fh)
+
+ table._set_properties(worksheet.tables[0])
+
+ table._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="C3:F14" totalsRowCount="1">
+ <autoFilter ref="C3:F13"/>
+ <tableColumns count="4">
+ <tableColumn id="1" name="Column1"/>
+ <tableColumn id="2" name="Column2"/>
+ <tableColumn id="3" name="Column3"/>
+ <tableColumn id="4" name="Column4"/>
+ </tableColumns>
+ <tableStyleInfo name="TableStyleMedium9" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>
+ </table>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_table08.py b/xlsxwriter/test/table/test_table08.py
new file mode 100644
index 0000000..91be02a
--- /dev/null
+++ b/xlsxwriter/test/table/test_table08.py
@@ -0,0 +1,63 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...table import Table
+from ...worksheet import Worksheet
+from ...workbook import WorksheetMeta
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleTable(unittest.TestCase):
+ """
+ Test assembling a complete Table file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a table"""
+ self.maxDiff = None
+
+ worksheet = Worksheet()
+ worksheet.worksheet_meta = WorksheetMeta()
+ worksheet.str_table = SharedStringTable()
+
+ # Set the table properties.
+ worksheet.add_table('C3:F14', {'total_row': True,
+ 'columns': [{'total_string': 'Total'},
+ {},
+ {},
+ {'total_function': 'count'}
+ ]})
+ worksheet._prepare_tables(1, {})
+
+ fh = StringIO()
+ table = Table()
+ table._set_filehandle(fh)
+
+ table._set_properties(worksheet.tables[0])
+
+ table._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="C3:F14" totalsRowCount="1">
+ <autoFilter ref="C3:F13"/>
+ <tableColumns count="4">
+ <tableColumn id="1" name="Column1" totalsRowLabel="Total"/>
+ <tableColumn id="2" name="Column2"/>
+ <tableColumn id="3" name="Column3"/>
+ <tableColumn id="4" name="Column4" totalsRowFunction="count"/>
+ </tableColumns>
+ <tableStyleInfo name="TableStyleMedium9" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>
+ </table>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_table09.py b/xlsxwriter/test/table/test_table09.py
new file mode 100644
index 0000000..94f072c
--- /dev/null
+++ b/xlsxwriter/test/table/test_table09.py
@@ -0,0 +1,75 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...table import Table
+from ...worksheet import Worksheet
+from ...workbook import WorksheetMeta
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleTable(unittest.TestCase):
+ """
+ Test assembling a complete Table file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a table"""
+ self.maxDiff = None
+
+ worksheet = Worksheet()
+ worksheet.worksheet_meta = WorksheetMeta()
+ worksheet.str_table = SharedStringTable()
+
+ # Set the table properties.
+ worksheet.add_table('B2:K8', {'total_row': 1,
+ 'columns': [{'total_string': 'Total'},
+ {},
+ {'total_function': 'Average'},
+ {'total_function': 'COUNT'},
+ {'total_function': 'count_nums'},
+ {'total_function': 'max'},
+ {'total_function': 'min'},
+ {'total_function': 'sum'},
+ {'total_function': 'std Dev'},
+ {'total_function': 'var'}
+ ]})
+ worksheet._prepare_tables(1, {})
+
+ fh = StringIO()
+ table = Table()
+ table._set_filehandle(fh)
+
+ table._set_properties(worksheet.tables[0])
+
+ table._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="B2:K8" totalsRowCount="1">
+ <autoFilter ref="B2:K7"/>
+ <tableColumns count="10">
+ <tableColumn id="1" name="Column1" totalsRowLabel="Total"/>
+ <tableColumn id="2" name="Column2"/>
+ <tableColumn id="3" name="Column3" totalsRowFunction="average"/>
+ <tableColumn id="4" name="Column4" totalsRowFunction="count"/>
+ <tableColumn id="5" name="Column5" totalsRowFunction="countNums"/>
+ <tableColumn id="6" name="Column6" totalsRowFunction="max"/>
+ <tableColumn id="7" name="Column7" totalsRowFunction="min"/>
+ <tableColumn id="8" name="Column8" totalsRowFunction="sum"/>
+ <tableColumn id="9" name="Column9" totalsRowFunction="stdDev"/>
+ <tableColumn id="10" name="Column10" totalsRowFunction="var"/>
+ </tableColumns>
+ <tableStyleInfo name="TableStyleMedium9" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>
+ </table>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_table10.py b/xlsxwriter/test/table/test_table10.py
new file mode 100644
index 0000000..cd51285
--- /dev/null
+++ b/xlsxwriter/test/table/test_table10.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...table import Table
+from ...worksheet import Worksheet
+from ...workbook import WorksheetMeta
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleTable(unittest.TestCase):
+ """
+ Test assembling a complete Table file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a table"""
+ self.maxDiff = None
+
+ worksheet = Worksheet()
+ worksheet.worksheet_meta = WorksheetMeta()
+ worksheet.str_table = SharedStringTable()
+
+ # Set the table properties.
+ worksheet.add_table('C2:F13', {'name': 'MyTable'})
+ worksheet._prepare_tables(1, {})
+
+ fh = StringIO()
+ table = Table()
+ table._set_filehandle(fh)
+
+ table._set_properties(worksheet.tables[0])
+
+ table._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="MyTable" displayName="MyTable" ref="C2:F13" totalsRowShown="0">
+ <autoFilter ref="C2:F13"/>
+ <tableColumns count="4">
+ <tableColumn id="1" name="Column1"/>
+ <tableColumn id="2" name="Column2"/>
+ <tableColumn id="3" name="Column3"/>
+ <tableColumn id="4" name="Column4"/>
+ </tableColumns>
+ <tableStyleInfo name="TableStyleMedium9" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>
+ </table>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_table11.py b/xlsxwriter/test/table/test_table11.py
new file mode 100644
index 0000000..732d579
--- /dev/null
+++ b/xlsxwriter/test/table/test_table11.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...table import Table
+from ...worksheet import Worksheet
+from ...workbook import WorksheetMeta
+from ...sharedstrings import SharedStringTable
+from ...format import Format
+
+
+class TestAssembleTable(unittest.TestCase):
+ """
+ Test assembling a complete Table file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a table"""
+ self.maxDiff = None
+
+ worksheet = Worksheet()
+ worksheet.worksheet_meta = WorksheetMeta()
+ worksheet.str_table = SharedStringTable()
+ dxf_format = Format()
+ dxf_format.dxf_index = 0
+
+ # Set the table properties.
+ worksheet.add_table('C2:F14', {'total_row': 1,
+ 'columns': [{'total_string': 'Total'},
+ {},
+ {},
+ {'total_function': 'count',
+ 'format': dxf_format,
+ 'formula': 'SUM(Table1[[#This Row],[Column1]:[Column3]])'},
+ ]})
+ worksheet._prepare_tables(1, {})
+
+ fh = StringIO()
+ table = Table()
+ table._set_filehandle(fh)
+
+ table._set_properties(worksheet.tables[0])
+
+ table._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="C2:F14" totalsRowCount="1">
+ <autoFilter ref="C2:F13"/>
+ <tableColumns count="4">
+ <tableColumn id="1" name="Column1" totalsRowLabel="Total"/>
+ <tableColumn id="2" name="Column2"/>
+ <tableColumn id="3" name="Column3"/>
+ <tableColumn id="4" name="Column4" totalsRowFunction="count" dataDxfId="0">
+ <calculatedColumnFormula>SUM(Table1[[#This Row],[Column1]:[Column3]])</calculatedColumnFormula>
+ </tableColumn>
+ </tableColumns>
+ <tableStyleInfo name="TableStyleMedium9" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>
+ </table>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_table12.py b/xlsxwriter/test/table/test_table12.py
new file mode 100644
index 0000000..1682b3e
--- /dev/null
+++ b/xlsxwriter/test/table/test_table12.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...table import Table
+from ...worksheet import Worksheet
+from ...workbook import WorksheetMeta
+from ...sharedstrings import SharedStringTable
+from ...format import Format
+
+
+class TestAssembleTable(unittest.TestCase):
+ """
+ Test assembling a complete Table file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a table"""
+ self.maxDiff = None
+
+ worksheet = Worksheet()
+ worksheet.worksheet_meta = WorksheetMeta()
+ worksheet.str_table = SharedStringTable()
+ dxf_format = Format()
+ dxf_format.dxf_index = 0
+
+ # Set the table properties.
+ worksheet.add_table('C2:F14', {'total_row': 1,
+ 'columns': [{'total_string': 'Total'},
+ {},
+ {},
+ {'total_function': 'count',
+ 'format': dxf_format,
+ 'formula': '=SUM(Table1[@[Column1]:[Column3]])'},
+ ]})
+ worksheet._prepare_tables(1, {})
+
+ fh = StringIO()
+ table = Table()
+ table._set_filehandle(fh)
+
+ table._set_properties(worksheet.tables[0])
+
+ table._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="C2:F14" totalsRowCount="1">
+ <autoFilter ref="C2:F13"/>
+ <tableColumns count="4">
+ <tableColumn id="1" name="Column1" totalsRowLabel="Total"/>
+ <tableColumn id="2" name="Column2"/>
+ <tableColumn id="3" name="Column3"/>
+ <tableColumn id="4" name="Column4" totalsRowFunction="count" dataDxfId="0">
+ <calculatedColumnFormula>SUM(Table1[[#This Row],[Column1]:[Column3]])</calculatedColumnFormula>
+ </tableColumn>
+ </tableColumns>
+ <tableStyleInfo name="TableStyleMedium9" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>
+ </table>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_write_auto_filter.py b/xlsxwriter/test/table/test_write_auto_filter.py
new file mode 100644
index 0000000..feea72f
--- /dev/null
+++ b/xlsxwriter/test/table/test_write_auto_filter.py
@@ -0,0 +1,34 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...table import Table
+
+
+class TestWriteAutoFilter(unittest.TestCase):
+ """
+ Test the Table _write_auto_filter() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.table = Table()
+ self.table._set_filehandle(self.fh)
+
+ def test_write_auto_filter(self):
+ """Test the _write_auto_filter() method"""
+
+ self.table.properties['autofilter'] = 'C3:F13'
+
+ self.table._write_auto_filter()
+
+ exp = """<autoFilter ref="C3:F13"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_write_table_column.py b/xlsxwriter/test/table/test_write_table_column.py
new file mode 100644
index 0000000..73e0123
--- /dev/null
+++ b/xlsxwriter/test/table/test_write_table_column.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...table import Table
+
+
+class TestWriteTableColumn(unittest.TestCase):
+ """
+ Test the Table _write_table_column() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.table = Table()
+ self.table._set_filehandle(self.fh)
+
+ def test_write_table_column(self):
+ """Test the _write_table_column() method"""
+
+ self.table._write_table_column({'name': 'Column1', 'id': 1})
+
+ exp = """<tableColumn id="1" name="Column1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/table/test_write_table_style_info.py b/xlsxwriter/test/table/test_write_table_style_info.py
new file mode 100644
index 0000000..dab9f44
--- /dev/null
+++ b/xlsxwriter/test/table/test_write_table_style_info.py
@@ -0,0 +1,40 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...table import Table
+
+
+class TestWriteTableStyleInfo(unittest.TestCase):
+ """
+ Test the Table _write_table_style_info() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.table = Table()
+ self.table._set_filehandle(self.fh)
+
+ def test_write_table_style_info(self):
+ """Test the _write_table_style_info() method"""
+
+ self.table.properties = {
+ 'style': 'TableStyleMedium9',
+ 'show_first_col': 0,
+ 'show_last_col': 0,
+ 'show_row_stripes': 1,
+ 'show_col_stripes': 0,
+ }
+
+ self.table._write_table_style_info()
+
+ exp = """<tableStyleInfo name="TableStyleMedium9" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/theme/__init__.py b/xlsxwriter/test/theme/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/utility/__init__.py b/xlsxwriter/test/utility/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/utility/test_xl_cell_to_rowcol.py b/xlsxwriter/test/utility/test_xl_cell_to_rowcol.py
new file mode 100644
index 0000000..4ec05d4
--- /dev/null
+++ b/xlsxwriter/test/utility/test_xl_cell_to_rowcol.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...utility import xl_cell_to_rowcol
+
+
+class TestUtility(unittest.TestCase):
+ """
+ Test xl_cell_to_rowcol() utility function.
+
+ """
+
+ def test_xl_cell_to_rowcol(self):
+ """Test xl_cell_to_rowcol()"""
+
+ tests = [
+ # row, col, A1 string
+ (0, 0, 'A1'),
+ (0, 1, 'B1'),
+ (0, 2, 'C1'),
+ (0, 9, 'J1'),
+ (1, 0, 'A2'),
+ (2, 0, 'A3'),
+ (9, 0, 'A10'),
+ (1, 24, 'Y2'),
+ (7, 25, 'Z8'),
+ (9, 26, 'AA10'),
+ (1, 254, 'IU2'),
+ (1, 255, 'IV2'),
+ (1, 256, 'IW2'),
+ (0, 16383, 'XFD1'),
+ (1048576, 16384, 'XFE1048577'),
+ ]
+
+ for row, col, string in tests:
+ exp = (row, col)
+ got = xl_cell_to_rowcol(string)
+ self.assertEqual(got, exp)
+
+ def test_xl_cell_to_rowcol_abs(self):
+ """Test xl_cell_to_rowcol() with absolute references"""
+
+ tests = [
+ # row, col, row_abs, col_abs, A1 string
+ (0, 0, 0, 0, 'A1'),
+ (0, 0, 1, 0, 'A$1'),
+ (0, 0, 0, 1, '$A1'),
+ (0, 0, 1, 1, '$A$1'),
+ ]
+
+ for row, col, row_abs, col_abs, string in tests:
+ exp = (row, col)
+ got = xl_cell_to_rowcol(string)
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/utility/test_xl_cell_to_rowcol_abs.py b/xlsxwriter/test/utility/test_xl_cell_to_rowcol_abs.py
new file mode 100644
index 0000000..d7ab139
--- /dev/null
+++ b/xlsxwriter/test/utility/test_xl_cell_to_rowcol_abs.py
@@ -0,0 +1,59 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...utility import xl_cell_to_rowcol_abs
+
+
+class TestUtility(unittest.TestCase):
+ """
+ Test xl_cell_to_rowcol_abs() utility function.
+
+ """
+
+ def test_xl_cell_to_rowcol_abs(self):
+ """Test xl_cell_to_rowcol_abs()"""
+
+ tests = [
+ # row, col, A1 string
+ (0, 0, 'A1'),
+ (0, 1, 'B1'),
+ (0, 2, 'C1'),
+ (0, 9, 'J1'),
+ (1, 0, 'A2'),
+ (2, 0, 'A3'),
+ (9, 0, 'A10'),
+ (1, 24, 'Y2'),
+ (7, 25, 'Z8'),
+ (9, 26, 'AA10'),
+ (1, 254, 'IU2'),
+ (1, 255, 'IV2'),
+ (1, 256, 'IW2'),
+ (0, 16383, 'XFD1'),
+ (1048576, 16384, 'XFE1048577'),
+ ]
+
+ for row, col, string in tests:
+ exp = (row, col, 0, 0)
+ got = xl_cell_to_rowcol_abs(string)
+ self.assertEqual(got, exp)
+
+ def test_xl_cell_to_rowcol_abs_abs(self):
+ """Test xl_cell_to_rowcol_abs() with absolute references"""
+
+ tests = [
+ # row, col, row_abs, col_abs, A1 string
+ (0, 0, 0, 0, 'A1'),
+ (0, 0, 1, 0, 'A$1'),
+ (0, 0, 0, 1, '$A1'),
+ (0, 0, 1, 1, '$A$1'),
+ ]
+
+ for row, col, row_abs, col_abs, string in tests:
+ exp = (row, col, row_abs, col_abs)
+ got = xl_cell_to_rowcol_abs(string)
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/utility/test_xl_col_to_name.py b/xlsxwriter/test/utility/test_xl_col_to_name.py
new file mode 100644
index 0000000..f02264c
--- /dev/null
+++ b/xlsxwriter/test/utility/test_xl_col_to_name.py
@@ -0,0 +1,53 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...utility import xl_col_to_name
+
+
+class TestUtility(unittest.TestCase):
+ """
+ Test xl_col_to_name() utility function.
+
+ """
+
+ def test_xl_col_to_name(self):
+ """Test xl_col_to_name()"""
+
+ tests = [
+ # col, col string
+ (0, 'A'),
+ (1, 'B'),
+ (2, 'C'),
+ (9, 'J'),
+ (24, 'Y'),
+ (25, 'Z'),
+ (26, 'AA'),
+ (254, 'IU'),
+ (255, 'IV'),
+ (256, 'IW'),
+ (16383, 'XFD'),
+ (16384, 'XFE'),
+ ]
+
+ for col, string in tests:
+ exp = string
+ got = xl_col_to_name(col)
+ self.assertEqual(got, exp)
+
+ def test_xl_col_to_name_abs(self):
+ """Test xl_col_to_name() with absolute references"""
+
+ tests = [
+ # col, col_abs, col string
+ (0, 1, '$A'),
+ ]
+
+ for col, col_abs, string in tests:
+ exp = string
+ got = xl_col_to_name(col, col_abs)
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/utility/test_xl_range.py b/xlsxwriter/test/utility/test_xl_range.py
new file mode 100644
index 0000000..1035447
--- /dev/null
+++ b/xlsxwriter/test/utility/test_xl_range.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...utility import xl_range
+from ...utility import xl_range_abs
+
+
+class TestUtility(unittest.TestCase):
+ """
+ Test xl_range() utility function.
+
+ """
+
+ def test_xl_range(self):
+ """Test xl_range()"""
+
+ tests = [
+ # first_row, first_col, last_row, last_col, Range
+ (0, 0, 9, 0, 'A1:A10'),
+ (1, 2, 8, 2, 'C2:C9'),
+ (0, 0, 3, 4, 'A1:E4'),
+ (0, 0, 0, 0, 'A1:A1'),
+ (0, 0, 0, 1, 'A1:B1'),
+ (0, 2, 0, 9, 'C1:J1'),
+ (1, 0, 2, 0, 'A2:A3'),
+ (9, 0, 1, 24, 'A10:Y2'),
+ (7, 25, 9, 26, 'Z8:AA10'),
+ (1, 254, 1, 255, 'IU2:IV2'),
+ (1, 256, 0, 16383, 'IW2:XFD1'),
+ (0, 0, 1048576, 16384, 'A1:XFE1048577'),
+ ]
+
+ for first_row, first_col, last_row, last_col, cell_range in tests:
+ exp = cell_range
+ got = xl_range(first_row, first_col, last_row, last_col)
+ self.assertEqual(got, exp)
+
+ def test_xl_range_abs(self):
+ """Test xl_range_abs()"""
+
+ tests = [
+ # first_row, first_col, last_row, last_col, Range
+ (0, 0, 9, 0, '$A$1:$A$10'),
+ (1, 2, 8, 2, '$C$2:$C$9'),
+ (0, 0, 3, 4, '$A$1:$E$4'),
+ (0, 0, 0, 0, '$A$1:$A$1'),
+ (0, 0, 0, 1, '$A$1:$B$1'),
+ (0, 2, 0, 9, '$C$1:$J$1'),
+ (1, 0, 2, 0, '$A$2:$A$3'),
+ (9, 0, 1, 24, '$A$10:$Y$2'),
+ (7, 25, 9, 26, '$Z$8:$AA$10'),
+ (1, 254, 1, 255, '$IU$2:$IV$2'),
+ (1, 256, 0, 16383, '$IW$2:$XFD$1'),
+ (0, 0, 1048576, 16384, '$A$1:$XFE$1048577'),
+ ]
+
+ for first_row, first_col, last_row, last_col, cell_range in tests:
+ exp = cell_range
+ got = xl_range_abs(first_row, first_col, last_row, last_col)
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/utility/test_xl_rowcol_to_cell.py b/xlsxwriter/test/utility/test_xl_rowcol_to_cell.py
new file mode 100644
index 0000000..107c06a
--- /dev/null
+++ b/xlsxwriter/test/utility/test_xl_rowcol_to_cell.py
@@ -0,0 +1,87 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...utility import xl_rowcol_to_cell
+from ...utility import xl_rowcol_to_cell_fast
+
+
+class TestUtility(unittest.TestCase):
+ """
+ Test xl_rowcol_to_cell() utility function.
+
+ """
+
+ def test_xl_rowcol_to_cell(self):
+ """Test xl_rowcol_to_cell()"""
+
+ tests = [
+ # row, col, A1 string
+ (0, 0, 'A1'),
+ (0, 1, 'B1'),
+ (0, 2, 'C1'),
+ (0, 9, 'J1'),
+ (1, 0, 'A2'),
+ (2, 0, 'A3'),
+ (9, 0, 'A10'),
+ (1, 24, 'Y2'),
+ (7, 25, 'Z8'),
+ (9, 26, 'AA10'),
+ (1, 254, 'IU2'),
+ (1, 255, 'IV2'),
+ (1, 256, 'IW2'),
+ (0, 16383, 'XFD1'),
+ (1048576, 16384, 'XFE1048577'),
+ ]
+
+ for row, col, string in tests:
+ exp = string
+ got = xl_rowcol_to_cell(row, col)
+ self.assertEqual(got, exp)
+
+ def test_xl_rowcol_to_cell_abs(self):
+ """Test xl_rowcol_to_cell() with absolute references"""
+
+ tests = [
+ # row, col, row_abs, col_abs, A1 string
+ (0, 0, 0, 0, 'A1'),
+ (0, 0, 1, 0, 'A$1'),
+ (0, 0, 0, 1, '$A1'),
+ (0, 0, 1, 1, '$A$1'),
+ ]
+
+ for row, col, row_abs, col_abs, string in tests:
+ exp = string
+ got = xl_rowcol_to_cell(row, col, row_abs, col_abs)
+ self.assertEqual(got, exp)
+
+ def test_xl_rowcol_to_cell_fast(self):
+ """Test xl_rowcol_to_cell_fast()"""
+
+ tests = [
+ # row, col, A1 string
+ (0, 0, 'A1'),
+ (0, 1, 'B1'),
+ (0, 2, 'C1'),
+ (0, 9, 'J1'),
+ (1, 0, 'A2'),
+ (2, 0, 'A3'),
+ (9, 0, 'A10'),
+ (1, 24, 'Y2'),
+ (7, 25, 'Z8'),
+ (9, 26, 'AA10'),
+ (1, 254, 'IU2'),
+ (1, 255, 'IV2'),
+ (1, 256, 'IW2'),
+ (0, 16383, 'XFD1'),
+ (1048576, 16384, 'XFE1048577'),
+ ]
+
+ for row, col, string in tests:
+ exp = string
+ got = xl_rowcol_to_cell_fast(row, col)
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/__init__.py b/xlsxwriter/test/vml/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/vml/test_vml01.py b/xlsxwriter/test/vml/test_vml01.py
new file mode 100644
index 0000000..7a320cf
--- /dev/null
+++ b/xlsxwriter/test/vml/test_vml01.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list, _vml_to_list
+from ...vml import Vml
+
+
+class TestAssembleVml(unittest.TestCase):
+ """
+ Test assembling a complete Vml file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a vml with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ vml = Vml()
+ vml._set_filehandle(fh)
+
+ vml._assemble_xml_file(1, 1024, [[1, 1, 'Some text', '', None, '#ffffe1', [2, 0, 15, 10, 4, 4, 15, 4, 143, 10, 128, 74]]], [])
+
+ exp = _vml_to_list("""
+ <xml xmlns:v="urn:schemas-microsoft-com:vml"
+ xmlns:o="urn:schemas-microsoft-com:office:office"
+ xmlns:x="urn:schemas-microsoft-com:office:excel">
+ <o:shapelayout v:ext="edit">
+ <o:idmap v:ext="edit" data="1"/>
+ </o:shapelayout><v:shapetype id="_x0000_t202" coordsize="21600,21600" o:spt="202"
+ path="m,l,21600r21600,l21600,xe">
+ <v:stroke joinstyle="miter"/>
+ <v:path gradientshapeok="t" o:connecttype="rect"/>
+ </v:shapetype><v:shape id="_x0000_s1025" type="#_x0000_t202" style='position:absolute;
+ margin-left:107.25pt;margin-top:7.5pt;width:96pt;height:55.5pt;z-index:1;
+ visibility:hidden' fillcolor="#ffffe1" o:insetmode="auto">
+ <v:fill color2="#ffffe1"/>
+ <v:shadow on="t" color="black" obscured="t"/>
+ <v:path o:connecttype="none"/>
+ <v:textbox style='mso-direction-alt:auto'>
+ <div style='text-align:left'></div>
+ </v:textbox>
+ <x:ClientData ObjectType="Note">
+ <x:MoveWithCells/>
+ <x:SizeWithCells/>
+ <x:Anchor>
+ 2, 15, 0, 10, 4, 15, 4, 4</x:Anchor>
+ <x:AutoFill>False</x:AutoFill>
+ <x:Row>1</x:Row>
+ <x:Column>1</x:Column>
+ </x:ClientData>
+ </v:shape></xml>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_vml02.py b/xlsxwriter/test/vml/test_vml02.py
new file mode 100644
index 0000000..b8c889b
--- /dev/null
+++ b/xlsxwriter/test/vml/test_vml02.py
@@ -0,0 +1,70 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list, _vml_to_list
+from ...vml import Vml
+
+
+class TestAssembleVml(unittest.TestCase):
+ """
+ Test assembling a complete Vml file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a vml with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ vml = Vml()
+ vml._set_filehandle(fh)
+
+ vml._assemble_xml_file(
+ 1,
+ 1024,
+ None,
+ [{'row': 1,
+ 'col': 2,
+ 'fillcolor': 'buttonFace [67]',
+ 'vertices': [2, 1, 0, 0, 3, 2, 0, 0, 128, 20, 64, 20],
+ 'font': {'caption': 'Button 1'},
+ 'macro': '[0]!Button1_Click'}])
+
+ exp = _vml_to_list("""
+ <xml xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel">
+ <o:shapelayout v:ext="edit">
+ <o:idmap v:ext="edit" data="1"/>
+ </o:shapelayout>
+ <v:shapetype id="_x0000_t201" coordsize="21600,21600" o:spt="201" path="m,l,21600r21600,l21600,xe">
+ <v:stroke joinstyle="miter"/>
+ <v:path shadowok="f" o:extrusionok="f" strokeok="f" fillok="f" o:connecttype="rect"/>
+ <o:lock v:ext="edit" shapetype="t"/>
+ </v:shapetype>
+ <v:shape id="_x0000_s1025" type="#_x0000_t201" style="position:absolute;margin-left:96pt;margin-top:15pt;width:48pt;height:15pt;z-index:1;mso-wrap-style:tight" o:button="t" fillcolor="buttonFace [67]" strokecolor="windowText [64]" o:insetmode="auto">
+ <v:fill color2="buttonFace [67]" o:detectmouseclick="t"/>
+ <o:lock v:ext="edit" rotation="t"/>
+ <v:textbox style="mso-direction-alt:auto" o:singleclick="f">
+ <div style="text-align:center">
+ <font face="Calibri" size="220" color="#000000">Button 1</font>
+ </div>
+ </v:textbox>
+ <x:ClientData ObjectType="Button">
+ <x:Anchor>2, 0, 1, 0, 3, 0, 2, 0</x:Anchor>
+ <x:PrintObject>False</x:PrintObject>
+ <x:AutoFill>False</x:AutoFill>
+ <x:FmlaMacro>[0]!Button1_Click</x:FmlaMacro>
+ <x:TextHAlign>Center</x:TextHAlign>
+ <x:TextVAlign>Center</x:TextVAlign>
+ </x:ClientData>
+ </v:shape>
+ </xml>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_vml03.py b/xlsxwriter/test/vml/test_vml03.py
new file mode 100644
index 0000000..fdad4c8
--- /dev/null
+++ b/xlsxwriter/test/vml/test_vml03.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list, _vml_to_list
+from ...vml import Vml
+
+
+class TestAssembleVml(unittest.TestCase):
+ """
+ Test assembling a complete Vml file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a vml with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ vml = Vml()
+ vml._set_filehandle(fh)
+
+ vml._assemble_xml_file(
+ 1,
+ 1024,
+ None,
+ None,
+ [[32, 32, 'red', 'CH', 96, 96]])
+
+ exp = _vml_to_list("""
+ <xml xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel">
+ <o:shapelayout v:ext="edit">
+ <o:idmap v:ext="edit" data="1"/>
+ </o:shapelayout>
+ <v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m at 4@5l at 4@11 at 9@11 at 9@5xe" filled="f" stroked="f">
+ <v:stroke joinstyle="miter"/>
+ <v:formulas>
+ <v:f eqn="if lineDrawn pixelLineWidth 0"/>
+ <v:f eqn="sum @0 1 0"/>
+ <v:f eqn="sum 0 0 @1"/>
+ <v:f eqn="prod @2 1 2"/>
+ <v:f eqn="prod @3 21600 pixelWidth"/>
+ <v:f eqn="prod @3 21600 pixelHeight"/>
+ <v:f eqn="sum @0 0 1"/>
+ <v:f eqn="prod @6 1 2"/>
+ <v:f eqn="prod @7 21600 pixelWidth"/>
+ <v:f eqn="sum @8 21600 0"/>
+ <v:f eqn="prod @7 21600 pixelHeight"/>
+ <v:f eqn="sum @10 21600 0"/>
+ </v:formulas>
+ <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/>
+ <o:lock v:ext="edit" aspectratio="t"/>
+ </v:shapetype>
+ <v:shape id="CH" o:spid="_x0000_s1025" type="#_x0000_t75" style="position:absolute;margin-left:0;margin-top:0;width:24pt;height:24pt;z-index:1">
+ <v:imagedata o:relid="rId1" o:title="red"/>
+ <o:lock v:ext="edit" rotation="t"/>
+ </v:shape>
+ </xml>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_anchor.py b/xlsxwriter/test/vml/test_write_anchor.py
new file mode 100644
index 0000000..d9f4aac
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_anchor.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteXAnchor(unittest.TestCase):
+ """
+ Test the Vml _write_anchor() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_anchor(self):
+ """Test the _write_anchor() method"""
+
+ self.vml._write_anchor([2, 0, 15, 10, 4, 4, 15, 4])
+
+ exp = """<x:Anchor>2, 15, 0, 10, 4, 15, 4, 4</x:Anchor>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_auto_fill.py b/xlsxwriter/test/vml/test_write_auto_fill.py
new file mode 100644
index 0000000..296b3e2
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_auto_fill.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteXAutoFill(unittest.TestCase):
+ """
+ Test the Vml _write_auto_fill() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_auto_fill(self):
+ """Test the _write_auto_fill() method"""
+
+ self.vml._write_auto_fill()
+
+ exp = """<x:AutoFill>False</x:AutoFill>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_column.py b/xlsxwriter/test/vml/test_write_column.py
new file mode 100644
index 0000000..242b28d
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_column.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteXColumn(unittest.TestCase):
+ """
+ Test the Vml _write_column() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_column(self):
+ """Test the _write_column() method"""
+
+ self.vml._write_column(2)
+
+ exp = """<x:Column>2</x:Column>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_div.py b/xlsxwriter/test/vml/test_write_div.py
new file mode 100644
index 0000000..df332e4
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_div.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteDiv(unittest.TestCase):
+ """
+ Test the Vml _write_div() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_div(self):
+ """Test the _write_div() method"""
+
+ self.vml._write_div('left')
+
+ exp = """<div style="text-align:left"></div>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_fill.py b/xlsxwriter/test/vml/test_write_fill.py
new file mode 100644
index 0000000..1960c72
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_fill.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteVfill(unittest.TestCase):
+ """
+ Test the Vml _write_fill() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_comment_fill(self):
+ """Test the _write_comment_fill() method"""
+
+ self.vml._write_comment_fill()
+
+ exp = """<v:fill color2="#ffffe1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_button_fill(self):
+ """Test the _write_button_fill() method"""
+
+ self.vml._write_button_fill()
+
+ exp = """<v:fill color2="buttonFace [67]" o:detectmouseclick="t"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_idmap.py b/xlsxwriter/test/vml/test_write_idmap.py
new file mode 100644
index 0000000..2dd0de9
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_idmap.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteOidmap(unittest.TestCase):
+ """
+ Test the Vml _write_idmap() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_idmap(self):
+ """Test the _write_idmap() method"""
+
+ self.vml._write_idmap(1)
+
+ exp = """<o:idmap v:ext="edit" data="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_move_with_cells.py b/xlsxwriter/test/vml/test_write_move_with_cells.py
new file mode 100644
index 0000000..fc9078e
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_move_with_cells.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteXMoveWithCells(unittest.TestCase):
+ """
+ Test the Vml _write_move_with_cells() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_move_with_cells(self):
+ """Test the _write_move_with_cells() method"""
+
+ self.vml._write_move_with_cells()
+
+ exp = """<x:MoveWithCells/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_path.py b/xlsxwriter/test/vml/test_write_path.py
new file mode 100644
index 0000000..0ee7448
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_path.py
@@ -0,0 +1,52 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteVpath(unittest.TestCase):
+ """
+ Test the Vml _write_path() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_comment_path_1(self):
+ """Test the _write_comment_path() method"""
+
+ self.vml._write_comment_path('t', 'rect')
+
+ exp = """<v:path gradientshapeok="t" o:connecttype="rect"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_comment_path_2(self):
+ """Test the _write_comment_path() method"""
+
+ self.vml._write_comment_path(None, 'none')
+
+ exp = """<v:path o:connecttype="none"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_button_path(self):
+ """Test the _write_button_path() method"""
+
+ self.vml._write_button_path()
+
+ exp = """<v:path shadowok="f" o:extrusionok="f" strokeok="f" fillok="f" o:connecttype="rect"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_row.py b/xlsxwriter/test/vml/test_write_row.py
new file mode 100644
index 0000000..7f559a5
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_row.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteXRow(unittest.TestCase):
+ """
+ Test the Vml _write_row() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_row(self):
+ """Test the _write_row() method"""
+
+ self.vml._write_row(2)
+
+ exp = """<x:Row>2</x:Row>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_shadow.py b/xlsxwriter/test/vml/test_write_shadow.py
new file mode 100644
index 0000000..322b63c
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_shadow.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteVshadow(unittest.TestCase):
+ """
+ Test the Vml _write_shadow() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_shadow(self):
+ """Test the _write_shadow() method"""
+
+ self.vml._write_shadow()
+
+ exp = """<v:shadow on="t" color="black" obscured="t"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_shapelayout.py b/xlsxwriter/test/vml/test_write_shapelayout.py
new file mode 100644
index 0000000..335f627
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_shapelayout.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteOshapelayout(unittest.TestCase):
+ """
+ Test the Vml _write_shapelayout() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_shapelayout(self):
+ """Test the _write_shapelayout() method"""
+
+ self.vml._write_shapelayout(1)
+
+ exp = """<o:shapelayout v:ext="edit"><o:idmap v:ext="edit" data="1"/></o:shapelayout>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_shapetype.py b/xlsxwriter/test/vml/test_write_shapetype.py
new file mode 100644
index 0000000..62048ce
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_shapetype.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteVshapetype(unittest.TestCase):
+ """
+ Test the Vml _write_shapetype() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_comment_shapetype(self):
+ """Test the _write_comment_shapetype() method"""
+
+ self.vml._write_comment_shapetype()
+
+ exp = """<v:shapetype id="_x0000_t202" coordsize="21600,21600" o:spt="202" path="m,l,21600r21600,l21600,xe"><v:stroke joinstyle="miter"/><v:path gradientshapeok="t" o:connecttype="rect"/></v:shapetype>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_button_shapetype(self):
+ """Test the _write_button_shapetype() method"""
+
+ self.vml._write_button_shapetype()
+
+ exp = """<v:shapetype id="_x0000_t201" coordsize="21600,21600" o:spt="201" path="m,l,21600r21600,l21600,xe"><v:stroke joinstyle="miter"/><v:path shadowok="f" o:extrusionok="f" strokeok="f" fillok="f" o:connecttype="rect"/><o:lock v:ext="edit" shapetype="t"/></v:shapetype>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_size_with_cells.py b/xlsxwriter/test/vml/test_write_size_with_cells.py
new file mode 100644
index 0000000..25f0788
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_size_with_cells.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteXSizeWithCells(unittest.TestCase):
+ """
+ Test the Vml _write_size_with_cells() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_size_with_cells(self):
+ """Test the _write_size_with_cells() method"""
+
+ self.vml._write_size_with_cells()
+
+ exp = """<x:SizeWithCells/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_stroke.py b/xlsxwriter/test/vml/test_write_stroke.py
new file mode 100644
index 0000000..fd56e64
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_stroke.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteVstroke(unittest.TestCase):
+ """
+ Test the Vml _write_stroke() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_stroke(self):
+ """Test the _write_stroke() method"""
+
+ self.vml._write_stroke()
+
+ exp = """<v:stroke joinstyle="miter"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_write_textbox.py b/xlsxwriter/test/vml/test_write_textbox.py
new file mode 100644
index 0000000..85f2877
--- /dev/null
+++ b/xlsxwriter/test/vml/test_write_textbox.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestWriteVtextbox(unittest.TestCase):
+ """
+ Test the Vml _write_textbox() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_write_comment_textbox(self):
+ """Test the _write_comment_textbox() method"""
+
+ self.vml._write_comment_textbox()
+
+ exp = """<v:textbox style="mso-direction-alt:auto"><div style="text-align:left"></div></v:textbox>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/vml/test_xml_declaration.py b/xlsxwriter/test/vml/test_xml_declaration.py
new file mode 100644
index 0000000..7658522
--- /dev/null
+++ b/xlsxwriter/test/vml/test_xml_declaration.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...vml import Vml
+
+
+class TestVmlXmlDeclaration(unittest.TestCase):
+ """
+ Test initialisation of the Vml class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.vml = Vml()
+ self.vml._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test Vml xml_declaration()"""
+
+ self.vml._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/workbook/__init__.py b/xlsxwriter/test/workbook/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/workbook/test_check_sheetname.py b/xlsxwriter/test/workbook/test_check_sheetname.py
new file mode 100644
index 0000000..347a6bd
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_check_sheetname.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...workbook import Workbook
+
+
+class TestCheckSheetname(unittest.TestCase):
+ """
+ Test the Workbook _check_sheetname() method.
+
+ """
+
+ def setUp(self):
+ self.workbook = Workbook()
+
+ def test_check_sheetname(self):
+ """Test the _check_sheetname() method"""
+
+ got = self.workbook._check_sheetname('name')
+ exp = 'name'
+ self.assertEqual(got, exp)
+
+ got = self.workbook._check_sheetname('Sheet1')
+ exp = 'Sheet1'
+ self.assertEqual(got, exp)
+
+ got = self.workbook._check_sheetname(None)
+ exp = 'Sheet3'
+ self.assertEqual(got, exp)
+
+ def test_check_sheetname_with_exception1(self):
+ """Test the _check_sheetname() method with exception"""
+
+ name = 'name_that_is_longer_than_thirty_one_characters'
+ self.assertRaises(Exception, self.workbook._check_sheetname, name)
+
+ def test_check_sheetname_with_exception2(self):
+ """Test the _check_sheetname() method with exception"""
+
+ name = 'name_with_special_character_?'
+ self.assertRaises(Exception, self.workbook._check_sheetname, name)
+
+ def test_check_sheetname_with_exception3(self):
+ """Test the _check_sheetname() method with exception"""
+
+ name1 = 'Duplicate_name'
+ name2 = name1.lower()
+
+ self.workbook.add_worksheet(name1)
+ self.assertRaises(Exception, self.workbook.add_worksheet, name2)
+
+ def tearDown(self):
+ self.workbook.fileclosed = 1
diff --git a/xlsxwriter/test/workbook/test_get_worksheet_by_name.py b/xlsxwriter/test/workbook/test_get_worksheet_by_name.py
new file mode 100644
index 0000000..a3c3b8d
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_get_worksheet_by_name.py
@@ -0,0 +1,78 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...workbook import Workbook
+
+
+class TestAssembleWorkbook(unittest.TestCase):
+ """
+ Test assembling a complete Workbook file.
+
+ """
+ def test_get_worksheet_by_name01(self):
+ """Test get_worksheet_by_name()"""
+ fh = StringIO()
+ workbook = Workbook()
+ workbook._set_filehandle(fh)
+
+ exp = workbook.add_worksheet()
+ got = workbook.get_worksheet_by_name('Sheet1')
+ workbook.fileclosed = 1
+
+ self.assertEqual(got, exp)
+
+ def test_get_worksheet_by_name02(self):
+ """Test get_worksheet_by_name()"""
+ fh = StringIO()
+ workbook = Workbook()
+ workbook._set_filehandle(fh)
+
+ workbook.add_worksheet()
+ exp = workbook.add_worksheet()
+ got = workbook.get_worksheet_by_name('Sheet2')
+ workbook.fileclosed = 1
+
+ self.assertEqual(got, exp)
+
+ def test_get_worksheet_by_name03(self):
+ """Test get_worksheet_by_name()"""
+ fh = StringIO()
+ workbook = Workbook()
+ workbook._set_filehandle(fh)
+
+ exp = workbook.add_worksheet('Sheet 3')
+ got = workbook.get_worksheet_by_name('Sheet 3')
+ workbook.fileclosed = 1
+
+ self.assertEqual(got, exp)
+
+ def test_get_worksheet_by_name04(self):
+ """Test get_worksheet_by_name()"""
+ fh = StringIO()
+ workbook = Workbook()
+ workbook._set_filehandle(fh)
+
+ exp = workbook.add_worksheet("Sheet '4")
+ got = workbook.get_worksheet_by_name("Sheet '4")
+ workbook.fileclosed = 1
+
+ self.assertEqual(got, exp)
+
+ def test_get_worksheet_by_name05(self):
+ """Test get_worksheet_by_name()"""
+ fh = StringIO()
+ workbook = Workbook()
+ workbook._set_filehandle(fh)
+
+ exp = None
+ got = workbook.get_worksheet_by_name("Sheet 5")
+ workbook.fileclosed = 1
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/workbook/test_initialisation.py b/xlsxwriter/test/workbook/test_initialisation.py
new file mode 100644
index 0000000..0c8ab6b
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_initialisation.py
@@ -0,0 +1,35 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...workbook import Workbook
+
+
+class TestInitialisation(unittest.TestCase):
+ """
+ Test initialisation of the Workbook class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.workbook = Workbook()
+ self.workbook._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test Workbook xml_declaration()"""
+
+ self.workbook._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def tearDown(self):
+ self.workbook.fileclosed = 1
diff --git a/xlsxwriter/test/workbook/test_workbook01.py b/xlsxwriter/test/workbook/test_workbook01.py
new file mode 100644
index 0000000..34037c2
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_workbook01.py
@@ -0,0 +1,49 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...workbook import Workbook
+
+
+class TestAssembleWorkbook(unittest.TestCase):
+ """
+ Test assembling a complete Workbook file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a workbook with 1 worksheet."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ workbook = Workbook()
+ workbook._set_filehandle(fh)
+
+ workbook.add_worksheet()
+
+ workbook._assemble_xml_file()
+ workbook.fileclosed = 1
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <fileVersion appName="xl" lastEdited="4" lowestEdited="4" rupBuild="4505"/>
+ <workbookPr defaultThemeVersion="124226"/>
+ <bookViews>
+ <workbookView xWindow="240" yWindow="15" windowWidth="16095" windowHeight="9660"/>
+ </bookViews>
+ <sheets>
+ <sheet name="Sheet1" sheetId="1" r:id="rId1"/>
+ </sheets>
+ <calcPr calcId="124519" fullCalcOnLoad="1"/>
+ </workbook>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/workbook/test_workbook02.py b/xlsxwriter/test/workbook/test_workbook02.py
new file mode 100644
index 0000000..b3ef060
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_workbook02.py
@@ -0,0 +1,51 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...workbook import Workbook
+
+
+class TestAssembleWorkbook(unittest.TestCase):
+ """
+ Test assembling a complete Workbook file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a workbook with 2 worksheets."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ workbook = Workbook()
+ workbook._set_filehandle(fh)
+
+ workbook.add_worksheet()
+ workbook.add_worksheet()
+
+ workbook._assemble_xml_file()
+ workbook.fileclosed = 1
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <fileVersion appName="xl" lastEdited="4" lowestEdited="4" rupBuild="4505"/>
+ <workbookPr defaultThemeVersion="124226"/>
+ <bookViews>
+ <workbookView xWindow="240" yWindow="15" windowWidth="16095" windowHeight="9660"/>
+ </bookViews>
+ <sheets>
+ <sheet name="Sheet1" sheetId="1" r:id="rId1"/>
+ <sheet name="Sheet2" sheetId="2" r:id="rId2"/>
+ </sheets>
+ <calcPr calcId="124519" fullCalcOnLoad="1"/>
+ </workbook>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/workbook/test_workbook03.py b/xlsxwriter/test/workbook/test_workbook03.py
new file mode 100644
index 0000000..b98767e
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_workbook03.py
@@ -0,0 +1,51 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...workbook import Workbook
+
+
+class TestAssembleWorkbook(unittest.TestCase):
+ """
+ Test assembling a complete Workbook file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a workbook with user specified names."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ workbook = Workbook()
+ workbook._set_filehandle(fh)
+
+ workbook.add_worksheet('Non Default Name')
+ workbook.add_worksheet('Another Name')
+
+ workbook._assemble_xml_file()
+ workbook.fileclosed = 1
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <fileVersion appName="xl" lastEdited="4" lowestEdited="4" rupBuild="4505"/>
+ <workbookPr defaultThemeVersion="124226"/>
+ <bookViews>
+ <workbookView xWindow="240" yWindow="15" windowWidth="16095" windowHeight="9660"/>
+ </bookViews>
+ <sheets>
+ <sheet name="Non Default Name" sheetId="1" r:id="rId1"/>
+ <sheet name="Another Name" sheetId="2" r:id="rId2"/>
+ </sheets>
+ <calcPr calcId="124519" fullCalcOnLoad="1"/>
+ </workbook>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/workbook/test_write_book_views.py b/xlsxwriter/test/workbook/test_write_book_views.py
new file mode 100644
index 0000000..546b974
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_write_book_views.py
@@ -0,0 +1,35 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...workbook import Workbook
+
+
+class TestWriteBookViews(unittest.TestCase):
+ """
+ Test the Workbook _write_book_views() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.workbook = Workbook()
+ self.workbook._set_filehandle(self.fh)
+
+ def test_write_book_views(self):
+ """Test the _write_book_views() method"""
+
+ self.workbook._write_book_views()
+
+ exp = """<bookViews><workbookView xWindow="240" yWindow="15" windowWidth="16095" windowHeight="9660"/></bookViews>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def tearDown(self):
+ self.workbook.fileclosed = 1
diff --git a/xlsxwriter/test/workbook/test_write_calc_pr.py b/xlsxwriter/test/workbook/test_write_calc_pr.py
new file mode 100644
index 0000000..36cd4e5
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_write_calc_pr.py
@@ -0,0 +1,76 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...workbook import Workbook
+
+
+class TestWriteCalcPr(unittest.TestCase):
+ """
+ Test the Workbook _write_calc_pr() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.workbook = Workbook()
+ self.workbook._set_filehandle(self.fh)
+
+ def test_write_calc_pr(self):
+ """Test the _write_calc_pr() method."""
+
+ self.workbook._write_calc_pr()
+
+ exp = """<calcPr calcId="124519" fullCalcOnLoad="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_calc_mode_auto_except_tables(self):
+ """
+ Test the _write_calc_pr() method with the calculation mode set
+ to auto_except_tables.
+
+ """
+
+ self.workbook.set_calc_mode("auto_except_tables")
+ self.workbook._write_calc_pr()
+
+ exp = """<calcPr calcId="124519" calcMode="autoNoTable" fullCalcOnLoad="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_calc_mode_manual(self):
+ """
+ Test the _write_calc_pr() method with the calculation mode set to
+ manual.
+
+ """
+
+ self.workbook.set_calc_mode("manual")
+ self.workbook._write_calc_pr()
+
+ exp = """<calcPr calcId="124519" calcMode="manual" calcOnSave="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_calc_pr(self):
+ """Test the _write_calc_pr() method with non-default calc id."""
+
+ self.workbook.set_calc_mode("auto", 12345)
+ self.workbook._write_calc_pr()
+
+ exp = """<calcPr calcId="12345" fullCalcOnLoad="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def tearDown(self):
+ self.workbook.fileclosed = 1
diff --git a/xlsxwriter/test/workbook/test_write_defined_name.py b/xlsxwriter/test/workbook/test_write_defined_name.py
new file mode 100644
index 0000000..959e0d6
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_write_defined_name.py
@@ -0,0 +1,35 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...workbook import Workbook
+
+
+class TestWriteDefinedName(unittest.TestCase):
+ """
+ Test the Workbook _write_defined_name() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.workbook = Workbook()
+ self.workbook._set_filehandle(self.fh)
+
+ def test_write_defined_name(self):
+ """Test the _write_defined_name() method"""
+
+ self.workbook._write_defined_name(['_xlnm.Print_Titles', 0, 'Sheet1!$1:$1', 0])
+
+ exp = """<definedName name="_xlnm.Print_Titles" localSheetId="0">Sheet1!$1:$1</definedName>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def tearDown(self):
+ self.workbook.fileclosed = 1
diff --git a/xlsxwriter/test/workbook/test_write_defined_names.py b/xlsxwriter/test/workbook/test_write_defined_names.py
new file mode 100644
index 0000000..3213f29
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_write_defined_names.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...workbook import Workbook
+
+
+class TestWriteDefinedNames(unittest.TestCase):
+ """
+ Test the Workbook _write_defined_names() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.workbook = Workbook()
+ self.workbook._set_filehandle(self.fh)
+
+ def test_write_defined_names_1(self):
+ """Test the _write_defined_names() method"""
+
+ self.workbook.defined_names = [['_xlnm.Print_Titles', 0, 'Sheet1!$1:$1', 0]]
+
+ self.workbook._write_defined_names()
+
+ exp = """<definedNames><definedName name="_xlnm.Print_Titles" localSheetId="0">Sheet1!$1:$1</definedName></definedNames>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_defined_names_2(self):
+ """Test the _write_defined_names() method"""
+
+ self.workbook.add_worksheet()
+ self.workbook.add_worksheet()
+ self.workbook.add_worksheet('Sheet 3')
+
+ self.workbook.define_name("""'Sheet 3'!Bar""", """='Sheet 3'!$A$1""")
+ self.workbook.define_name("""Abc""", """=Sheet1!$A$1""")
+ self.workbook.define_name("""Baz""", """=0.98""")
+ self.workbook.define_name("""Sheet1!Bar""", """=Sheet1!$A$1""")
+ self.workbook.define_name("""Sheet2!Bar""", """=Sheet2!$A$1""")
+ self.workbook.define_name("""Sheet2!aaa""", """=Sheet2!$A$1""")
+ self.workbook.define_name("""'Sheet 3'!car""", '="Saab 900"')
+ self.workbook.define_name("""_Egg""", """=Sheet1!$A$1""")
+ self.workbook.define_name("""_Fog""", """=Sheet1!$A$1""")
+
+ self.workbook._prepare_defined_names()
+ self.workbook._write_defined_names()
+
+ exp = """<definedNames><definedName name="_Egg">Sheet1!$A$1</definedName><definedName name="_Fog">Sheet1!$A$1</definedName><definedName name="aaa" localSheetId="1">Sheet2!$A$1</definedName><definedName name="Abc">Sheet1!$A$1</definedName><definedName name="Bar" localSheetId="2">'Sheet 3'!$A$1</definedName><definedName name="Bar" localSheetId="0">Sheet1!$A$1</definedName><definedName name="Bar" localSheetId="1">Sheet2!$A$1</definedName><definedName name="Baz">0.98</definedName><de [...]
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def tearDown(self):
+ self.workbook.fileclosed = 1
diff --git a/xlsxwriter/test/workbook/test_write_file_version.py b/xlsxwriter/test/workbook/test_write_file_version.py
new file mode 100644
index 0000000..9ea4dc5
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_write_file_version.py
@@ -0,0 +1,35 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...workbook import Workbook
+
+
+class TestWriteFileVersion(unittest.TestCase):
+ """
+ Test the Workbook _write_file_version() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.workbook = Workbook()
+ self.workbook._set_filehandle(self.fh)
+
+ def test_write_file_version(self):
+ """Test the _write_file_version() method"""
+
+ self.workbook._write_file_version()
+
+ exp = """<fileVersion appName="xl" lastEdited="4" lowestEdited="4" rupBuild="4505"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def tearDown(self):
+ self.workbook.fileclosed = 1
diff --git a/xlsxwriter/test/workbook/test_write_sheet.py b/xlsxwriter/test/workbook/test_write_sheet.py
new file mode 100644
index 0000000..f4c06f1
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_write_sheet.py
@@ -0,0 +1,55 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...workbook import Workbook
+
+
+class TestWriteSheet(unittest.TestCase):
+ """
+ Test the Workbook _write_sheet() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.workbook = Workbook()
+ self.workbook._set_filehandle(self.fh)
+
+ def test_write_sheet1(self):
+ """Test the _write_sheet() method"""
+
+ self.workbook._write_sheet('Sheet1', 1, 0)
+
+ exp = """<sheet name="Sheet1" sheetId="1" r:id="rId1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet2(self):
+ """Test the _write_sheet() method"""
+
+ self.workbook._write_sheet('Sheet1', 1, 1)
+
+ exp = """<sheet name="Sheet1" sheetId="1" state="hidden" r:id="rId1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet3(self):
+ """Test the _write_sheet() method"""
+
+ self.workbook._write_sheet('Bits & Bobs', 1, 0)
+
+ exp = """<sheet name="Bits & Bobs" sheetId="1" r:id="rId1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def tearDown(self):
+ self.workbook.fileclosed = 1
diff --git a/xlsxwriter/test/workbook/test_write_sheets.py b/xlsxwriter/test/workbook/test_write_sheets.py
new file mode 100644
index 0000000..97ffdb9
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_write_sheets.py
@@ -0,0 +1,36 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...workbook import Workbook
+
+
+class TestWriteSheets(unittest.TestCase):
+ """
+ Test the Workbook _write_sheets() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.workbook = Workbook()
+ self.workbook._set_filehandle(self.fh)
+
+ def test_write_sheets(self):
+ """Test the _write_sheets() method"""
+
+ self.workbook.add_worksheet('Sheet2')
+ self.workbook._write_sheets()
+
+ exp = """<sheets><sheet name="Sheet2" sheetId="1" r:id="rId1"/></sheets>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def tearDown(self):
+ self.workbook.fileclosed = 1
diff --git a/xlsxwriter/test/workbook/test_write_workbook.py b/xlsxwriter/test/workbook/test_write_workbook.py
new file mode 100644
index 0000000..e10d322
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_write_workbook.py
@@ -0,0 +1,35 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...workbook import Workbook
+
+
+class TestWriteWorkbook(unittest.TestCase):
+ """
+ Test the Workbook _write_workbook() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.workbook = Workbook()
+ self.workbook._set_filehandle(self.fh)
+
+ def test_write_workbook(self):
+ """Test the _write_workbook() method"""
+
+ self.workbook._write_workbook()
+
+ exp = """<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def tearDown(self):
+ self.workbook.fileclosed = 1
diff --git a/xlsxwriter/test/workbook/test_write_workbook_pr.py b/xlsxwriter/test/workbook/test_write_workbook_pr.py
new file mode 100644
index 0000000..f25e1d2
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_write_workbook_pr.py
@@ -0,0 +1,35 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...workbook import Workbook
+
+
+class TestWriteWorkbookPr(unittest.TestCase):
+ """
+ Test the Workbook _write_workbook_pr() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.workbook = Workbook()
+ self.workbook._set_filehandle(self.fh)
+
+ def test_write_workbook_pr(self):
+ """Test the _write_workbook_pr() method"""
+
+ self.workbook._write_workbook_pr()
+
+ exp = """<workbookPr defaultThemeVersion="124226"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def tearDown(self):
+ self.workbook.fileclosed = 1
diff --git a/xlsxwriter/test/workbook/test_write_workbook_view.py b/xlsxwriter/test/workbook/test_write_workbook_view.py
new file mode 100644
index 0000000..8ceddf1
--- /dev/null
+++ b/xlsxwriter/test/workbook/test_write_workbook_view.py
@@ -0,0 +1,113 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...workbook import Workbook
+
+
+class TestWriteWorkbookView(unittest.TestCase):
+ """
+ Test the Workbook _write_workbook_view() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.workbook = Workbook()
+ self.workbook._set_filehandle(self.fh)
+
+ def test_write_workbook_view1(self):
+ """Test the _write_workbook_view() method"""
+
+ self.workbook._write_workbook_view()
+
+ exp = """<workbookView xWindow="240" yWindow="15" windowWidth="16095" windowHeight="9660"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_workbook_view2(self):
+ """Test the _write_workbook_view() method"""
+
+ self.workbook.worksheet_meta.activesheet = 1
+ self.workbook._write_workbook_view()
+
+ exp = """<workbookView xWindow="240" yWindow="15" windowWidth="16095" windowHeight="9660" activeTab="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_workbook_view3(self):
+ """Test the _write_workbook_view() method"""
+
+ self.workbook.worksheet_meta.firstsheet = 1
+ self.workbook.worksheet_meta.activesheet = 1
+ self.workbook._write_workbook_view()
+
+ exp = """<workbookView xWindow="240" yWindow="15" windowWidth="16095" windowHeight="9660" firstSheet="2" activeTab="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_workbook_view4(self):
+ """Test the _write_workbook_view() method"""
+
+ self.workbook.set_size(0, 0)
+ self.workbook._write_workbook_view()
+
+ exp = """<workbookView xWindow="240" yWindow="15" windowWidth="16095" windowHeight="9660"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_workbook_view5(self):
+ """Test the _write_workbook_view() method"""
+
+ self.workbook.set_size(None, None)
+ self.workbook._write_workbook_view()
+
+ exp = """<workbookView xWindow="240" yWindow="15" windowWidth="16095" windowHeight="9660"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_workbook_view5(self):
+ """Test the _write_workbook_view() method"""
+
+ self.workbook.set_size(1073, 644)
+ self.workbook._write_workbook_view()
+
+ exp = """<workbookView xWindow="240" yWindow="15" windowWidth="16095" windowHeight="9660"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_workbook_view6(self):
+ """Test the _write_workbook_view() method"""
+
+ self.workbook.set_size(123, 70)
+ self.workbook._write_workbook_view()
+
+ exp = """<workbookView xWindow="240" yWindow="15" windowWidth="1845" windowHeight="1050"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_workbook_view7(self):
+ """Test the _write_workbook_view() method"""
+
+ self.workbook.set_size(719, 490)
+ self.workbook._write_workbook_view()
+
+ exp = """<workbookView xWindow="240" yWindow="15" windowWidth="10785" windowHeight="7350"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def tearDown(self):
+ self.workbook.fileclosed = 1
diff --git a/xlsxwriter/test/worksheet/__init__.py b/xlsxwriter/test/worksheet/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/worksheet/test_calcuate_spans.py b/xlsxwriter/test/worksheet/test_calcuate_spans.py
new file mode 100644
index 0000000..5417040
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_calcuate_spans.py
@@ -0,0 +1,326 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestCalculateSpans(unittest.TestCase):
+ """
+ Test the _calculate_spans Worksheet method for different cell ranges.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_calculate_spans_0(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 0
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ exp = {0: '1:16', 1: '17:17'}
+ got = self.worksheet.row_spans
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_1(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 0
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:16', 1: '17:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_2(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 1
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:15', 1: '16:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_3(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 2
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:14', 1: '15:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_4(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 3
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:13', 1: '14:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_5(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 4
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:12', 1: '13:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_6(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 5
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:11', 1: '12:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_7(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 6
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:10', 1: '11:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_8(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 7
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:9', 1: '10:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_9(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 8
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:8', 1: '9:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_10(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 9
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:7', 1: '8:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_11(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 10
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:6', 1: '7:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_12(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 11
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:5', 1: '6:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_13(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 12
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:4', 1: '5:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_14(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 13
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:3', 1: '4:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_15(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 14
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:2', 1: '3:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_16(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 15
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {0: '1:1', 1: '2:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_17(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 16
+ col = 0
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {1: '1:16', 2: '17:17'}
+
+ self.assertEqual(got, exp)
+
+ def test_calculate_spans_18(self):
+ """Test Worksheet _calculate_spans()"""
+ row = 16
+ col = 1
+
+ for i in range(row, row + 17):
+ self.worksheet.write_number(i, col, 1)
+ col = col + 1
+
+ self.worksheet._calculate_spans()
+
+ got = self.worksheet.row_spans
+ exp = {1: '2:17', 2: '18:18'}
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format01.py b/xlsxwriter/test/worksheet/test_cond_format01.py
new file mode 100644
index 0000000..1190023
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format01.py
@@ -0,0 +1,81 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1', {'type': 'cell',
+ 'criteria': 'greater than',
+ 'value': 5,
+ 'format': None})
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>20</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>30</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>40</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1">
+ <cfRule type="cellIs" priority="1" operator="greaterThan">
+ <formula>5</formula>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format02.py b/xlsxwriter/test/worksheet/test_cond_format02.py
new file mode 100644
index 0000000..d4d6790
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format02.py
@@ -0,0 +1,88 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.write('B1', 5)
+
+ worksheet.conditional_format('A1:A1',
+ {'type': 'cell',
+ 'format': None,
+ 'criteria': 'greater than',
+ 'value': '$B$1',
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:B4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:2">
+ <c r="A1">
+ <v>10</v>
+ </c>
+ <c r="B1">
+ <v>5</v>
+ </c>
+ </row>
+ <row r="2" spans="1:2">
+ <c r="A2">
+ <v>20</v>
+ </c>
+ </row>
+ <row r="3" spans="1:2">
+ <c r="A3">
+ <v>30</v>
+ </c>
+ </row>
+ <row r="4" spans="1:2">
+ <c r="A4">
+ <v>40</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1">
+ <cfRule type="cellIs" priority="1" operator="greaterThan">
+ <formula>$B$1</formula>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format03.py b/xlsxwriter/test/worksheet/test_cond_format03.py
new file mode 100644
index 0000000..a4bfe61
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format03.py
@@ -0,0 +1,97 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'cell',
+ 'format': None,
+ 'criteria': 'between',
+ 'minimum': 20,
+ 'maximum': 30,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'cell',
+ 'format': None,
+ 'criteria': 'not between',
+ 'minimum': 20,
+ 'maximum': 30,
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>20</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>30</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>40</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A4">
+ <cfRule type="cellIs" priority="1" operator="between">
+ <formula>20</formula>
+ <formula>30</formula>
+ </cfRule>
+ <cfRule type="cellIs" priority="2" operator="notBetween">
+ <formula>20</formula>
+ <formula>30</formula>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format04.py b/xlsxwriter/test/worksheet/test_cond_format04.py
new file mode 100644
index 0000000..4d29db2
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format04.py
@@ -0,0 +1,85 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'duplicate',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'unique',
+ 'format': None,
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>20</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>30</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>40</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A4">
+ <cfRule type="duplicateValues" priority="1"/>
+ <cfRule type="uniqueValues" priority="2"/>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format05.py b/xlsxwriter/test/worksheet/test_cond_format05.py
new file mode 100644
index 0000000..8c1652e
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format05.py
@@ -0,0 +1,143 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'average',
+ 'format': None,
+ 'criteria': 'above',
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'average',
+ 'format': None,
+ 'criteria': 'below',
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'average',
+ 'format': None,
+ 'criteria': 'equal or above',
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'average',
+ 'format': None,
+ 'criteria': 'equal or below',
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'average',
+ 'format': None,
+ 'criteria': '1 std dev above',
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'average',
+ 'format': None,
+ 'criteria': '1 std dev below',
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'average',
+ 'format': None,
+ 'criteria': '2 std dev above',
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'average',
+ 'format': None,
+ 'criteria': '2 std dev below',
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'average',
+ 'format': None,
+ 'criteria': '3 std dev above',
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'average',
+ 'format': None,
+ 'criteria': '3 std dev below',
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>20</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>30</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>40</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A4">
+ <cfRule type="aboveAverage" priority="1"/>
+ <cfRule type="aboveAverage" priority="2" aboveAverage="0"/>
+ <cfRule type="aboveAverage" priority="3" equalAverage="1"/>
+ <cfRule type="aboveAverage" priority="4" aboveAverage="0" equalAverage="1"/>
+ <cfRule type="aboveAverage" priority="5" stdDev="1"/>
+ <cfRule type="aboveAverage" priority="6" aboveAverage="0" stdDev="1"/>
+ <cfRule type="aboveAverage" priority="7" stdDev="2"/>
+ <cfRule type="aboveAverage" priority="8" aboveAverage="0" stdDev="2"/>
+ <cfRule type="aboveAverage" priority="9" stdDev="3"/>
+ <cfRule type="aboveAverage" priority="10" aboveAverage="0" stdDev="3"/>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format06.py b/xlsxwriter/test/worksheet/test_cond_format06.py
new file mode 100644
index 0000000..8e7b556
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format06.py
@@ -0,0 +1,103 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'top',
+ 'value': 10,
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'bottom',
+ 'value': 10,
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'top',
+ 'criteria': '%',
+ 'value': 10,
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'bottom',
+ 'criteria': '%',
+ 'value': 10,
+ 'format': None,
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>20</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>30</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>40</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A4">
+ <cfRule type="top10" priority="1" rank="10"/>
+ <cfRule type="top10" priority="2" bottom="1" rank="10"/>
+ <cfRule type="top10" priority="3" percent="1" rank="10"/>
+ <cfRule type="top10" priority="4" percent="1" bottom="1" rank="10"/>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format07.py b/xlsxwriter/test/worksheet/test_cond_format07.py
new file mode 100644
index 0000000..8e45dd4
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format07.py
@@ -0,0 +1,112 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'text',
+ 'criteria': 'containing',
+ 'value': 'foo',
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'text',
+ 'criteria': 'not containing',
+ 'value': 'foo',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'text',
+ 'criteria': 'begins with',
+ 'value': 'b',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'text',
+ 'criteria': 'ends with',
+ 'value': 'b',
+ 'format': None,
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>20</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>30</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>40</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A4">
+ <cfRule type="containsText" priority="1" operator="containsText" text="foo">
+ <formula>NOT(ISERROR(SEARCH("foo",A1)))</formula>
+ </cfRule>
+ <cfRule type="notContainsText" priority="2" operator="notContains" text="foo">
+ <formula>ISERROR(SEARCH("foo",A1))</formula>
+ </cfRule>
+ <cfRule type="beginsWith" priority="3" operator="beginsWith" text="b">
+ <formula>LEFT(A1,1)="b"</formula>
+ </cfRule>
+ <cfRule type="endsWith" priority="4" operator="endsWith" text="b">
+ <formula>RIGHT(A1,1)="b"</formula>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format08.py b/xlsxwriter/test/worksheet/test_cond_format08.py
new file mode 100644
index 0000000..f3c7c2f
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format08.py
@@ -0,0 +1,162 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'time_period',
+ 'criteria': 'yesterday',
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'time_period',
+ 'criteria': 'today',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'time_period',
+ 'criteria': 'tomorrow',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'time_period',
+ 'criteria': 'last 7 days',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'time_period',
+ 'criteria': 'last week',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'time_period',
+ 'criteria': 'this week',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'time_period',
+ 'criteria': 'continue week',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'time_period',
+ 'criteria': 'last month',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'time_period',
+ 'criteria': 'this month',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'time_period',
+ 'criteria': 'continue month',
+ 'format': None,
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>20</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>30</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>40</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A4">
+ <cfRule type="timePeriod" priority="1" timePeriod="yesterday">
+ <formula>FLOOR(A1,1)=TODAY()-1</formula>
+ </cfRule>
+ <cfRule type="timePeriod" priority="2" timePeriod="today">
+ <formula>FLOOR(A1,1)=TODAY()</formula>
+ </cfRule>
+ <cfRule type="timePeriod" priority="3" timePeriod="tomorrow">
+ <formula>FLOOR(A1,1)=TODAY()+1</formula>
+ </cfRule>
+ <cfRule type="timePeriod" priority="4" timePeriod="last7Days">
+ <formula>AND(TODAY()-FLOOR(A1,1)<=6,FLOOR(A1,1)<=TODAY())</formula>
+ </cfRule>
+ <cfRule type="timePeriod" priority="5" timePeriod="lastWeek">
+ <formula>AND(TODAY()-ROUNDDOWN(A1,0)>=(WEEKDAY(TODAY())),TODAY()-ROUNDDOWN(A1,0)<(WEEKDAY(TODAY())+7))</formula>
+ </cfRule>
+ <cfRule type="timePeriod" priority="6" timePeriod="thisWeek">
+ <formula>AND(TODAY()-ROUNDDOWN(A1,0)<=WEEKDAY(TODAY())-1,ROUNDDOWN(A1,0)-TODAY()<=7-WEEKDAY(TODAY()))</formula>
+ </cfRule>
+ <cfRule type="timePeriod" priority="7" timePeriod="continueWeek">
+ <formula>AND(ROUNDDOWN(A1,0)-TODAY()>(7-WEEKDAY(TODAY())),ROUNDDOWN(A1,0)-TODAY()<(15-WEEKDAY(TODAY())))</formula>
+ </cfRule>
+ <cfRule type="timePeriod" priority="8" timePeriod="lastMonth">
+ <formula>AND(MONTH(A1)=MONTH(TODAY())-1,OR(YEAR(A1)=YEAR(TODAY()),AND(MONTH(A1)=1,YEAR(A1)=YEAR(TODAY())-1)))</formula>
+ </cfRule>
+ <cfRule type="timePeriod" priority="9" timePeriod="thisMonth">
+ <formula>AND(MONTH(A1)=MONTH(TODAY()),YEAR(A1)=YEAR(TODAY()))</formula>
+ </cfRule>
+ <cfRule type="timePeriod" priority="10" timePeriod="continueMonth">
+ <formula>AND(MONTH(A1)=MONTH(TODAY())+1,OR(YEAR(A1)=YEAR(TODAY()),AND(MONTH(A1)=12,YEAR(A1)=YEAR(TODAY())+1)))</formula>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format09.py b/xlsxwriter/test/worksheet/test_cond_format09.py
new file mode 100644
index 0000000..9ef068d
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format09.py
@@ -0,0 +1,104 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'blanks',
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'no_blanks',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'errors',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'no_errors',
+ 'format': None,
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>20</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>30</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>40</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A4">
+ <cfRule type="containsBlanks" priority="1">
+ <formula>LEN(TRIM(A1))=0</formula>
+ </cfRule>
+ <cfRule type="notContainsBlanks" priority="2">
+ <formula>LEN(TRIM(A1))>0</formula>
+ </cfRule>
+ <cfRule type="containsErrors" priority="3">
+ <formula>ISERROR(A1)</formula>
+ </cfRule>
+ <cfRule type="notContainsErrors" priority="4">
+ <formula>NOT(ISERROR(A1))</formula>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format10.py b/xlsxwriter/test/worksheet/test_cond_format10.py
new file mode 100644
index 0000000..973ca0b
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format10.py
@@ -0,0 +1,86 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from datetime import datetime
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ date = datetime.strptime('2011-01-01', "%Y-%m-%d")
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'date',
+ 'criteria': 'greater than',
+ 'value': date,
+ 'format': None,
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>20</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>30</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>40</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A4">
+ <cfRule type="cellIs" priority="1" operator="greaterThan">
+ <formula>40544</formula>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format11.py b/xlsxwriter/test/worksheet/test_cond_format11.py
new file mode 100644
index 0000000..7cadff3
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format11.py
@@ -0,0 +1,86 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from datetime import datetime
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'date',
+ 'criteria': 'between',
+ 'minimum': datetime.strptime('2011-01-01', "%Y-%m-%d"),
+ 'maximum': datetime.strptime('2011-12-31', "%Y-%m-%d"),
+ 'format': None,
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>20</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>30</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>40</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A4">
+ <cfRule type="cellIs" priority="1" operator="between">
+ <formula>40544</formula>
+ <formula>40908</formula>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format12.py b/xlsxwriter/test/worksheet/test_cond_format12.py
new file mode 100644
index 0000000..3b41d14
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format12.py
@@ -0,0 +1,131 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 1)
+ worksheet.write('A2', 2)
+ worksheet.write('A3', 3)
+ worksheet.write('A4', 4)
+ worksheet.write('A5', 5)
+ worksheet.write('A6', 6)
+ worksheet.write('A7', 7)
+ worksheet.write('A8', 8)
+ worksheet.write('A9', 9)
+ worksheet.write('A10', 10)
+ worksheet.write('A11', 11)
+ worksheet.write('A12', 12)
+
+ worksheet.conditional_format('A1:A12', {'type': '2_color_scale'})
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A12"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>1</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>2</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>3</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>4</v>
+ </c>
+ </row>
+ <row r="5" spans="1:1">
+ <c r="A5">
+ <v>5</v>
+ </c>
+ </row>
+ <row r="6" spans="1:1">
+ <c r="A6">
+ <v>6</v>
+ </c>
+ </row>
+ <row r="7" spans="1:1">
+ <c r="A7">
+ <v>7</v>
+ </c>
+ </row>
+ <row r="8" spans="1:1">
+ <c r="A8">
+ <v>8</v>
+ </c>
+ </row>
+ <row r="9" spans="1:1">
+ <c r="A9">
+ <v>9</v>
+ </c>
+ </row>
+ <row r="10" spans="1:1">
+ <c r="A10">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="11" spans="1:1">
+ <c r="A11">
+ <v>11</v>
+ </c>
+ </row>
+ <row r="12" spans="1:1">
+ <c r="A12">
+ <v>12</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A12">
+ <cfRule type="colorScale" priority="1">
+ <colorScale>
+ <cfvo type="min" val="0"/>
+ <cfvo type="max" val="0"/>
+ <color rgb="FFFF7128"/>
+ <color rgb="FFFFEF9C"/>
+ </colorScale>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format13.py b/xlsxwriter/test/worksheet/test_cond_format13.py
new file mode 100644
index 0000000..bcfc683
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format13.py
@@ -0,0 +1,133 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 1)
+ worksheet.write('A2', 2)
+ worksheet.write('A3', 3)
+ worksheet.write('A4', 4)
+ worksheet.write('A5', 5)
+ worksheet.write('A6', 6)
+ worksheet.write('A7', 7)
+ worksheet.write('A8', 8)
+ worksheet.write('A9', 9)
+ worksheet.write('A10', 10)
+ worksheet.write('A11', 11)
+ worksheet.write('A12', 12)
+
+ worksheet.conditional_format('A1:A12', {'type': '3_color_scale'})
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A12"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>1</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>2</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>3</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>4</v>
+ </c>
+ </row>
+ <row r="5" spans="1:1">
+ <c r="A5">
+ <v>5</v>
+ </c>
+ </row>
+ <row r="6" spans="1:1">
+ <c r="A6">
+ <v>6</v>
+ </c>
+ </row>
+ <row r="7" spans="1:1">
+ <c r="A7">
+ <v>7</v>
+ </c>
+ </row>
+ <row r="8" spans="1:1">
+ <c r="A8">
+ <v>8</v>
+ </c>
+ </row>
+ <row r="9" spans="1:1">
+ <c r="A9">
+ <v>9</v>
+ </c>
+ </row>
+ <row r="10" spans="1:1">
+ <c r="A10">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="11" spans="1:1">
+ <c r="A11">
+ <v>11</v>
+ </c>
+ </row>
+ <row r="12" spans="1:1">
+ <c r="A12">
+ <v>12</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A12">
+ <cfRule type="colorScale" priority="1">
+ <colorScale>
+ <cfvo type="min" val="0"/>
+ <cfvo type="percentile" val="50"/>
+ <cfvo type="max" val="0"/>
+ <color rgb="FFF8696B"/>
+ <color rgb="FFFFEB84"/>
+ <color rgb="FF63BE7B"/>
+ </colorScale>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format14.py b/xlsxwriter/test/worksheet/test_cond_format14.py
new file mode 100644
index 0000000..f770462
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format14.py
@@ -0,0 +1,130 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 1)
+ worksheet.write('A2', 2)
+ worksheet.write('A3', 3)
+ worksheet.write('A4', 4)
+ worksheet.write('A5', 5)
+ worksheet.write('A6', 6)
+ worksheet.write('A7', 7)
+ worksheet.write('A8', 8)
+ worksheet.write('A9', 9)
+ worksheet.write('A10', 10)
+ worksheet.write('A11', 11)
+ worksheet.write('A12', 12)
+
+ worksheet.conditional_format('A1:A12', {'type': 'data_bar'})
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A12"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>1</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>2</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>3</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>4</v>
+ </c>
+ </row>
+ <row r="5" spans="1:1">
+ <c r="A5">
+ <v>5</v>
+ </c>
+ </row>
+ <row r="6" spans="1:1">
+ <c r="A6">
+ <v>6</v>
+ </c>
+ </row>
+ <row r="7" spans="1:1">
+ <c r="A7">
+ <v>7</v>
+ </c>
+ </row>
+ <row r="8" spans="1:1">
+ <c r="A8">
+ <v>8</v>
+ </c>
+ </row>
+ <row r="9" spans="1:1">
+ <c r="A9">
+ <v>9</v>
+ </c>
+ </row>
+ <row r="10" spans="1:1">
+ <c r="A10">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="11" spans="1:1">
+ <c r="A11">
+ <v>11</v>
+ </c>
+ </row>
+ <row r="12" spans="1:1">
+ <c r="A12">
+ <v>12</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A12">
+ <cfRule type="dataBar" priority="1">
+ <dataBar>
+ <cfvo type="min" val="0"/>
+ <cfvo type="max" val="0"/>
+ <color rgb="FF638EC6"/>
+ </dataBar>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format15.py b/xlsxwriter/test/worksheet/test_cond_format15.py
new file mode 100644
index 0000000..6a35ea3
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format15.py
@@ -0,0 +1,111 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ cell_format = None
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'formula',
+ 'criteria': '=A1>5',
+ 'format': cell_format,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'formula',
+ 'criteria': '=A2<80',
+ 'format': cell_format,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'formula',
+ 'criteria': '"1+2"',
+ 'format': cell_format,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'formula',
+ 'criteria': '=A3>A4',
+ 'format': cell_format,
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>20</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>30</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>40</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A4">
+ <cfRule type="expression" priority="1">
+ <formula>A1>5</formula>
+ </cfRule>
+ <cfRule type="expression" priority="2">
+ <formula>A2<80</formula>
+ </cfRule>
+ <cfRule type="expression" priority="3">
+ <formula>"1+2"</formula>
+ </cfRule>
+ <cfRule type="expression" priority="4">
+ <formula>A3>A4</formula>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format16.py b/xlsxwriter/test/worksheet/test_cond_format16.py
new file mode 100644
index 0000000..d1eb3ae
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format16.py
@@ -0,0 +1,138 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 1)
+ worksheet.write('A2', 2)
+ worksheet.write('A3', 3)
+ worksheet.write('A4', 4)
+ worksheet.write('A5', 5)
+ worksheet.write('A6', 6)
+ worksheet.write('A7', 7)
+ worksheet.write('A8', 8)
+ worksheet.write('A9', 9)
+ worksheet.write('A10', 10)
+ worksheet.write('A11', 11)
+ worksheet.write('A12', 12)
+
+ worksheet.conditional_format('A1:A12',
+ {'type': '3_color_scale',
+ 'min_color': "#C5D9F1",
+ 'mid_color': "#8DB4E3",
+ 'max_color': "#538ED5",
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A12"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>1</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>2</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>3</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>4</v>
+ </c>
+ </row>
+ <row r="5" spans="1:1">
+ <c r="A5">
+ <v>5</v>
+ </c>
+ </row>
+ <row r="6" spans="1:1">
+ <c r="A6">
+ <v>6</v>
+ </c>
+ </row>
+ <row r="7" spans="1:1">
+ <c r="A7">
+ <v>7</v>
+ </c>
+ </row>
+ <row r="8" spans="1:1">
+ <c r="A8">
+ <v>8</v>
+ </c>
+ </row>
+ <row r="9" spans="1:1">
+ <c r="A9">
+ <v>9</v>
+ </c>
+ </row>
+ <row r="10" spans="1:1">
+ <c r="A10">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="11" spans="1:1">
+ <c r="A11">
+ <v>11</v>
+ </c>
+ </row>
+ <row r="12" spans="1:1">
+ <c r="A12">
+ <v>12</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A12">
+ <cfRule type="colorScale" priority="1">
+ <colorScale>
+ <cfvo type="min" val="0"/>
+ <cfvo type="percentile" val="50"/>
+ <cfvo type="max" val="0"/>
+ <color rgb="FFC5D9F1"/>
+ <color rgb="FF8DB4E3"/>
+ <color rgb="FF538ED5"/>
+ </colorScale>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format17.py b/xlsxwriter/test/worksheet/test_cond_format17.py
new file mode 100644
index 0000000..e084ab1
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format17.py
@@ -0,0 +1,141 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 1)
+ worksheet.write('A2', 2)
+ worksheet.write('A3', 3)
+ worksheet.write('A4', 4)
+ worksheet.write('A5', 5)
+ worksheet.write('A6', 6)
+ worksheet.write('A7', 7)
+ worksheet.write('A8', 8)
+ worksheet.write('A9', 9)
+ worksheet.write('A10', 10)
+ worksheet.write('A11', 11)
+ worksheet.write('A12', 12)
+
+ worksheet.conditional_format('A1:A12',
+ {'type': '3_color_scale',
+ 'min_value': 'A10',
+ 'mid_value': 52,
+ 'max_value': 99,
+ 'min_type': 'num',
+ 'mid_type': 'percent',
+ 'max_type': 'percentile',
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A12"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>1</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>2</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>3</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>4</v>
+ </c>
+ </row>
+ <row r="5" spans="1:1">
+ <c r="A5">
+ <v>5</v>
+ </c>
+ </row>
+ <row r="6" spans="1:1">
+ <c r="A6">
+ <v>6</v>
+ </c>
+ </row>
+ <row r="7" spans="1:1">
+ <c r="A7">
+ <v>7</v>
+ </c>
+ </row>
+ <row r="8" spans="1:1">
+ <c r="A8">
+ <v>8</v>
+ </c>
+ </row>
+ <row r="9" spans="1:1">
+ <c r="A9">
+ <v>9</v>
+ </c>
+ </row>
+ <row r="10" spans="1:1">
+ <c r="A10">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="11" spans="1:1">
+ <c r="A11">
+ <v>11</v>
+ </c>
+ </row>
+ <row r="12" spans="1:1">
+ <c r="A12">
+ <v>12</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A12">
+ <cfRule type="colorScale" priority="1">
+ <colorScale>
+ <cfvo type="num" val="A10"/>
+ <cfvo type="percent" val="52"/>
+ <cfvo type="percentile" val="99"/>
+ <color rgb="FFF8696B"/>
+ <color rgb="FFFFEB84"/>
+ <color rgb="FF63BE7B"/>
+ </colorScale>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format18.py b/xlsxwriter/test/worksheet/test_cond_format18.py
new file mode 100644
index 0000000..23e6303
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format18.py
@@ -0,0 +1,136 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 1)
+ worksheet.write('A2', 2)
+ worksheet.write('A3', 3)
+ worksheet.write('A4', 4)
+ worksheet.write('A5', 5)
+ worksheet.write('A6', 6)
+ worksheet.write('A7', 7)
+ worksheet.write('A8', 8)
+ worksheet.write('A9', 9)
+ worksheet.write('A10', 10)
+ worksheet.write('A11', 11)
+ worksheet.write('A12', 12)
+
+ worksheet.conditional_format('A1:A12',
+ {'type': '3_color_scale',
+ 'multi_range': '$A$3:$A$4 A1 A6:$A$8 $A10 A$12',
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A12"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>1</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>2</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>3</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>4</v>
+ </c>
+ </row>
+ <row r="5" spans="1:1">
+ <c r="A5">
+ <v>5</v>
+ </c>
+ </row>
+ <row r="6" spans="1:1">
+ <c r="A6">
+ <v>6</v>
+ </c>
+ </row>
+ <row r="7" spans="1:1">
+ <c r="A7">
+ <v>7</v>
+ </c>
+ </row>
+ <row r="8" spans="1:1">
+ <c r="A8">
+ <v>8</v>
+ </c>
+ </row>
+ <row r="9" spans="1:1">
+ <c r="A9">
+ <v>9</v>
+ </c>
+ </row>
+ <row r="10" spans="1:1">
+ <c r="A10">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="11" spans="1:1">
+ <c r="A11">
+ <v>11</v>
+ </c>
+ </row>
+ <row r="12" spans="1:1">
+ <c r="A12">
+ <v>12</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A3:A4 A1 A6:A8 A10 A12">
+ <cfRule type="colorScale" priority="1">
+ <colorScale>
+ <cfvo type="min" val="0"/>
+ <cfvo type="percentile" val="50"/>
+ <cfvo type="max" val="0"/>
+ <color rgb="FFF8696B"/>
+ <color rgb="FFFFEB84"/>
+ <color rgb="FF63BE7B"/>
+ </colorScale>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format19.py b/xlsxwriter/test/worksheet/test_cond_format19.py
new file mode 100644
index 0000000..d2fa019
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format19.py
@@ -0,0 +1,139 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 1)
+ worksheet.write('A2', 2)
+ worksheet.write('A3', 3)
+ worksheet.write('A4', 4)
+ worksheet.write('A5', 5)
+ worksheet.write('A6', 6)
+ worksheet.write('A7', 7)
+ worksheet.write('A8', 8)
+ worksheet.write('A9', 9)
+ worksheet.write('A10', 10)
+ worksheet.write('A11', 11)
+ worksheet.write('A12', 12)
+
+ worksheet.conditional_format('A1:A12',
+ {'type': 'data_bar',
+ 'min_value': 5,
+ 'mid_value': 52, # Should be ignored.
+ 'max_value': 90,
+ 'min_type': 'num',
+ 'mid_type': 'percentile', # Should be ignored.
+ 'max_type': 'percent',
+ 'bar_color': '#8DB4E3',
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A12"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>1</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>2</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>3</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>4</v>
+ </c>
+ </row>
+ <row r="5" spans="1:1">
+ <c r="A5">
+ <v>5</v>
+ </c>
+ </row>
+ <row r="6" spans="1:1">
+ <c r="A6">
+ <v>6</v>
+ </c>
+ </row>
+ <row r="7" spans="1:1">
+ <c r="A7">
+ <v>7</v>
+ </c>
+ </row>
+ <row r="8" spans="1:1">
+ <c r="A8">
+ <v>8</v>
+ </c>
+ </row>
+ <row r="9" spans="1:1">
+ <c r="A9">
+ <v>9</v>
+ </c>
+ </row>
+ <row r="10" spans="1:1">
+ <c r="A10">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="11" spans="1:1">
+ <c r="A11">
+ <v>11</v>
+ </c>
+ </row>
+ <row r="12" spans="1:1">
+ <c r="A12">
+ <v>12</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A12">
+ <cfRule type="dataBar" priority="1">
+ <dataBar>
+ <cfvo type="num" val="5"/>
+ <cfvo type="percent" val="90"/>
+ <color rgb="FF8DB4E3"/>
+ </dataBar>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format20.py b/xlsxwriter/test/worksheet/test_cond_format20.py
new file mode 100644
index 0000000..3a356f0
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format20.py
@@ -0,0 +1,113 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 10)
+ worksheet.write('A2', 20)
+ worksheet.write('A3', 30)
+ worksheet.write('A4', 40)
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'text',
+ 'criteria': 'begins with',
+ 'value': 'b',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'text',
+ 'criteria': 'begins with',
+ 'value': 'bc',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'text',
+ 'criteria': 'ends with',
+ 'value': 'z',
+ 'format': None,
+ })
+
+ worksheet.conditional_format('A1:A4',
+ {'type': 'text',
+ 'criteria': 'ends with',
+ 'value': 'yz',
+ 'format': None,
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>20</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>30</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>40</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A4">
+ <cfRule type="beginsWith" priority="1" operator="beginsWith" text="b">
+ <formula>LEFT(A1,1)="b"</formula>
+ </cfRule>
+ <cfRule type="beginsWith" priority="2" operator="beginsWith" text="bc">
+ <formula>LEFT(A1,2)="bc"</formula>
+ </cfRule>
+ <cfRule type="endsWith" priority="3" operator="endsWith" text="z">
+ <formula>RIGHT(A1,1)="z"</formula>
+ </cfRule>
+ <cfRule type="endsWith" priority="4" operator="endsWith" text="yz">
+ <formula>RIGHT(A1,2)="yz"</formula>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_cond_format21.py b/xlsxwriter/test/worksheet/test_cond_format21.py
new file mode 100644
index 0000000..4428b18
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_cond_format21.py
@@ -0,0 +1,141 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with conditional formatting."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+
+ worksheet.write('A1', 1)
+ worksheet.write('A2', 2)
+ worksheet.write('A3', 3)
+ worksheet.write('A4', 4)
+ worksheet.write('A5', 5)
+ worksheet.write('A6', 6)
+ worksheet.write('A7', 7)
+ worksheet.write('A8', 8)
+ worksheet.write('A9', 9)
+ worksheet.write('A10', 10)
+ worksheet.write('A11', 11)
+ worksheet.write('A12', 12)
+
+ worksheet.conditional_format('A1:A12',
+ {'type': 'data_bar',
+ 'min_value': 5,
+ 'mid_value': 52, # Should be ignored.
+ 'max_value': 90,
+ 'min_length': 5,
+ 'max_length': 95,
+ 'min_type': 'num',
+ 'mid_type': 'percentile', # Should be ignored.
+ 'max_type': 'percent',
+ 'bar_color': '#8DB4E3',
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:A12"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:1">
+ <c r="A1">
+ <v>1</v>
+ </c>
+ </row>
+ <row r="2" spans="1:1">
+ <c r="A2">
+ <v>2</v>
+ </c>
+ </row>
+ <row r="3" spans="1:1">
+ <c r="A3">
+ <v>3</v>
+ </c>
+ </row>
+ <row r="4" spans="1:1">
+ <c r="A4">
+ <v>4</v>
+ </c>
+ </row>
+ <row r="5" spans="1:1">
+ <c r="A5">
+ <v>5</v>
+ </c>
+ </row>
+ <row r="6" spans="1:1">
+ <c r="A6">
+ <v>6</v>
+ </c>
+ </row>
+ <row r="7" spans="1:1">
+ <c r="A7">
+ <v>7</v>
+ </c>
+ </row>
+ <row r="8" spans="1:1">
+ <c r="A8">
+ <v>8</v>
+ </c>
+ </row>
+ <row r="9" spans="1:1">
+ <c r="A9">
+ <v>9</v>
+ </c>
+ </row>
+ <row r="10" spans="1:1">
+ <c r="A10">
+ <v>10</v>
+ </c>
+ </row>
+ <row r="11" spans="1:1">
+ <c r="A11">
+ <v>11</v>
+ </c>
+ </row>
+ <row r="12" spans="1:1">
+ <c r="A12">
+ <v>12</v>
+ </c>
+ </row>
+ </sheetData>
+ <conditionalFormatting sqref="A1:A12">
+ <cfRule type="dataBar" priority="1">
+ <dataBar minLength="5" maxLength="95">
+ <cfvo type="num" val="5"/>
+ <cfvo type="percent" val="90"/>
+ <color rgb="FF8DB4E3"/>
+ </dataBar>
+ </cfRule>
+ </conditionalFormatting>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_date_time_01.py b/xlsxwriter/test/worksheet/test_date_time_01.py
new file mode 100644
index 0000000..6d94681
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_date_time_01.py
@@ -0,0 +1,140 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from datetime import datetime
+from ...worksheet import Worksheet
+
+
+class TestConvertDateTime(unittest.TestCase):
+ """
+ Test the Worksheet _convert_date_time() method against dates extracted
+ from Excel.
+
+ """
+ def setUp(self):
+ self.worksheet = Worksheet()
+
+ def test_convert_date_time(self):
+ """Test the _convert_date_time() method."""
+
+ # Dates and corresponding numbers from an Excel file.
+ excel_dates = [
+ ('1899-12-31T00:00:00.000', 0),
+ ('1982-08-25T00:15:20.213', 30188.010650613425),
+ ('2065-04-19T00:16:48.290', 60376.011670023145),
+ ('2147-12-15T00:55:25.446', 90565.038488958337),
+ ('2230-08-10T01:02:46.891', 120753.04359827546),
+ ('2313-04-06T01:04:15.597', 150942.04462496529),
+ ('2395-11-30T01:09:40.889', 181130.04838991899),
+ ('2478-07-25T01:11:32.560', 211318.04968240741),
+ ('2561-03-21T01:30:19.169', 241507.06272186342),
+ ('2643-11-15T01:48:25.580', 271695.07529606484),
+ ('2726-07-12T02:03:31.919', 301884.08578609955),
+ ('2809-03-06T02:11:11.986', 332072.09111094906),
+ ('2891-10-31T02:24:37.095', 362261.10042934027),
+ ('2974-06-26T02:35:07.220', 392449.10772245371),
+ ('3057-02-19T02:45:12.109', 422637.1147234838),
+ ('3139-10-17T03:06:39.990', 452826.12962951389),
+ ('3222-06-11T03:08:08.251', 483014.13065105322),
+ ('3305-02-05T03:19:12.576', 513203.13834),
+ ('3387-10-01T03:29:42.574', 543391.14563164348),
+ ('3470-05-27T03:37:30.813', 573579.15105107636),
+ ('3553-01-21T04:14:38.231', 603768.17683137732),
+ ('3635-09-16T04:16:28.559', 633956.17810832174),
+ ('3718-05-13T04:17:58.222', 664145.17914608796),
+ ('3801-01-06T04:21:41.794', 694333.18173372687),
+ ('3883-09-02T04:56:35.792', 724522.20596981479),
+ ('3966-04-28T05:25:14.885', 754710.2258667245),
+ ('4048-12-21T05:26:05.724', 784898.22645513888),
+ ('4131-08-18T05:46:44.068', 815087.24078782403),
+ ('4214-04-13T05:48:01.141', 845275.24167987274),
+ ('4296-12-07T05:53:52.315', 875464.24574438657),
+ ('4379-08-03T06:14:48.580', 905652.26028449077),
+ ('4462-03-28T06:46:15.738', 935840.28212659725),
+ ('4544-11-22T07:31:20.407', 966029.31343063654),
+ ('4627-07-19T07:58:33.754', 996217.33233511576),
+ ('4710-03-15T08:07:43.130', 1026406.3386936343),
+ ('4792-11-07T08:29:11.091', 1056594.3536005903),
+ ('4875-07-04T09:08:15.328', 1086783.3807329629),
+ ('4958-02-27T09:30:41.781', 1116971.3963169097),
+ ('5040-10-23T09:34:04.462', 1147159.3986627546),
+ ('5123-06-20T09:37:23.945', 1177348.4009715857),
+ ('5206-02-12T09:37:56.655', 1207536.4013501736),
+ ('5288-10-08T09:45:12.230', 1237725.406391551),
+ ('5371-06-04T09:54:14.782', 1267913.412671088),
+ ('5454-01-28T09:54:22.108', 1298101.4127558796),
+ ('5536-09-24T10:01:36.151', 1328290.4177795255),
+ ('5619-05-20T12:09:48.602', 1358478.5068125231),
+ ('5702-01-14T12:34:08.549', 1388667.5237100578),
+ ('5784-09-08T12:56:06.495', 1418855.5389640625),
+ ('5867-05-06T12:58:58.217', 1449044.5409515856),
+ ('5949-12-30T12:59:54.263', 1479232.5416002662),
+ ('6032-08-24T13:34:41.331', 1509420.5657561459),
+ ('6115-04-21T13:58:28.601', 1539609.5822754744),
+ ('6197-12-14T14:02:16.899', 1569797.5849178126),
+ ('6280-08-10T14:36:17.444', 1599986.6085352316),
+ ('6363-04-06T14:37:57.451', 1630174.60969272),
+ ('6445-11-30T14:57:42.757', 1660363.6234115392),
+ ('6528-07-26T15:10:48.307', 1690551.6325035533),
+ ('6611-03-22T15:14:39.890', 1720739.635183912),
+ ('6693-11-15T15:19:47.988', 1750928.6387498612),
+ ('6776-07-11T16:04:24.344', 1781116.6697262037),
+ ('6859-03-07T16:22:23.952', 1811305.6822216667),
+ ('6941-10-31T16:29:55.999', 1841493.6874536921),
+ ('7024-06-26T16:58:20.259', 1871681.7071789235),
+ ('7107-02-21T17:04:02.415', 1901870.7111390624),
+ ('7189-10-16T17:18:29.630', 1932058.7211762732),
+ ('7272-06-11T17:47:21.323', 1962247.7412190163),
+ ('7355-02-05T17:53:29.866', 1992435.7454845603),
+ ('7437-10-02T17:53:41.076', 2022624.7456143056),
+ ('7520-05-28T17:55:06.044', 2052812.7465977315),
+ ('7603-01-21T18:14:49.151', 2083000.7602910995),
+ ('7685-09-16T18:17:45.738', 2113189.7623349307),
+ ('7768-05-12T18:29:59.700', 2143377.7708298611),
+ ('7851-01-07T18:33:21.233', 2173566.773162419),
+ ('7933-09-02T19:14:24.673', 2203754.8016744559),
+ ('8016-04-27T19:17:12.816', 2233942.8036205554),
+ ('8098-12-22T19:23:36.418', 2264131.8080603937),
+ ('8181-08-17T19:46:25.908', 2294319.8239109721),
+ ('8264-04-13T20:07:47.314', 2324508.8387420601),
+ ('8346-12-08T20:31:37.603', 2354696.855296331),
+ ('8429-08-03T20:39:57.770', 2384885.8610853008),
+ ('8512-03-29T20:50:17.067', 2415073.8682530904),
+ ('8594-11-22T21:02:57.827', 2445261.8770581828),
+ ('8677-07-19T21:23:05.519', 2475450.8910360998),
+ ('8760-03-14T21:34:49.572', 2505638.8991848612),
+ ('8842-11-08T21:39:05.944', 2535827.9021521294),
+ ('8925-07-04T21:39:18.426', 2566015.9022965971),
+ ('9008-02-28T21:46:07.769', 2596203.9070343636),
+ ('9090-10-24T21:57:55.662', 2626392.9152275696),
+ ('9173-06-19T22:19:11.732', 2656580.9299968979),
+ ('9256-02-13T22:23:51.376', 2686769.9332335186),
+ ('9338-10-09T22:27:58.771', 2716957.9360968866),
+ ('9421-06-05T22:43:30.392', 2747146.9468795368),
+ ('9504-01-30T22:48:25.834', 2777334.9502990046),
+ ('9586-09-24T22:53:51.727', 2807522.9540709145),
+ ('9669-05-20T23:12:56.536', 2837711.9673210187),
+ ('9752-01-14T23:15:54.109', 2867899.9693762613),
+ ('9834-09-10T23:17:12.632', 2898088.9702850925),
+ ('9999-12-31T23:59:59.000', 2958465.999988426),
+ ]
+
+ epoch = datetime(1899, 12, 31)
+
+ for excel_date in excel_dates:
+ date = datetime.strptime(excel_date[0], "%Y-%m-%dT%H:%M:%S.%f")
+
+ got = self.worksheet._convert_date_time(date)
+ exp = excel_date[1]
+ self.assertEqual(got, exp)
+
+ # Also test time deltas.
+ delta = date - epoch
+ got = self.worksheet._convert_date_time(delta)
+ exp = excel_date[1]
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_date_time_02.py b/xlsxwriter/test/worksheet/test_date_time_02.py
new file mode 100644
index 0000000..44da4d3
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_date_time_02.py
@@ -0,0 +1,465 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+import datetime
+from ...worksheet import Worksheet
+
+
+class TestConvertDateTime(unittest.TestCase):
+ """
+ Test the Worksheet _convert_date_time() method against dates extracted
+ from Excel.
+
+ """
+ def setUp(self):
+ self.worksheet = Worksheet()
+
+ # Dates and corresponding numbers from an Excel file.
+ self.excel_dates = [
+ ('1899-12-31T', 0),
+ # ('1900-01-01T', 1), # Fails due to special case for seconds only.
+ ('1900-02-27T', 58),
+ ('1900-02-28T', 59),
+ ('1900-03-01T', 61),
+ ('1900-03-02T', 62),
+ ('1900-03-11T', 71),
+ ('1900-04-08T', 99),
+ ('1900-09-12T', 256),
+ ('1901-05-03T', 489),
+ ('1901-10-13T', 652),
+ ('1902-02-15T', 777),
+ ('1902-06-06T', 888),
+ ('1902-09-25T', 999),
+ ('1902-09-27T', 1001),
+ ('1903-04-26T', 1212),
+ ('1903-08-05T', 1313),
+ ('1903-12-31T', 1461),
+ ('1904-01-01T', 1462),
+ ('1904-02-28T', 1520),
+ ('1904-02-29T', 1521),
+ ('1904-03-01T', 1522),
+ ('1907-02-27T', 2615),
+ ('1907-02-28T', 2616),
+ ('1907-03-01T', 2617),
+ ('1907-03-02T', 2618),
+ ('1907-03-03T', 2619),
+ ('1907-03-04T', 2620),
+ ('1907-03-05T', 2621),
+ ('1907-03-06T', 2622),
+ ('1999-01-01T', 36161),
+ ('1999-01-31T', 36191),
+ ('1999-02-01T', 36192),
+ ('1999-02-28T', 36219),
+ ('1999-03-01T', 36220),
+ ('1999-03-31T', 36250),
+ ('1999-04-01T', 36251),
+ ('1999-04-30T', 36280),
+ ('1999-05-01T', 36281),
+ ('1999-05-31T', 36311),
+ ('1999-06-01T', 36312),
+ ('1999-06-30T', 36341),
+ ('1999-07-01T', 36342),
+ ('1999-07-31T', 36372),
+ ('1999-08-01T', 36373),
+ ('1999-08-31T', 36403),
+ ('1999-09-01T', 36404),
+ ('1999-09-30T', 36433),
+ ('1999-10-01T', 36434),
+ ('1999-10-31T', 36464),
+ ('1999-11-01T', 36465),
+ ('1999-11-30T', 36494),
+ ('1999-12-01T', 36495),
+ ('1999-12-31T', 36525),
+ ('2000-01-01T', 36526),
+ ('2000-01-31T', 36556),
+ ('2000-02-01T', 36557),
+ ('2000-02-29T', 36585),
+ ('2000-03-01T', 36586),
+ ('2000-03-31T', 36616),
+ ('2000-04-01T', 36617),
+ ('2000-04-30T', 36646),
+ ('2000-05-01T', 36647),
+ ('2000-05-31T', 36677),
+ ('2000-06-01T', 36678),
+ ('2000-06-30T', 36707),
+ ('2000-07-01T', 36708),
+ ('2000-07-31T', 36738),
+ ('2000-08-01T', 36739),
+ ('2000-08-31T', 36769),
+ ('2000-09-01T', 36770),
+ ('2000-09-30T', 36799),
+ ('2000-10-01T', 36800),
+ ('2000-10-31T', 36830),
+ ('2000-11-01T', 36831),
+ ('2000-11-30T', 36860),
+ ('2000-12-01T', 36861),
+ ('2000-12-31T', 36891),
+ ('2001-01-01T', 36892),
+ ('2001-01-31T', 36922),
+ ('2001-02-01T', 36923),
+ ('2001-02-28T', 36950),
+ ('2001-03-01T', 36951),
+ ('2001-03-31T', 36981),
+ ('2001-04-01T', 36982),
+ ('2001-04-30T', 37011),
+ ('2001-05-01T', 37012),
+ ('2001-05-31T', 37042),
+ ('2001-06-01T', 37043),
+ ('2001-06-30T', 37072),
+ ('2001-07-01T', 37073),
+ ('2001-07-31T', 37103),
+ ('2001-08-01T', 37104),
+ ('2001-08-31T', 37134),
+ ('2001-09-01T', 37135),
+ ('2001-09-30T', 37164),
+ ('2001-10-01T', 37165),
+ ('2001-10-31T', 37195),
+ ('2001-11-01T', 37196),
+ ('2001-11-30T', 37225),
+ ('2001-12-01T', 37226),
+ ('2001-12-31T', 37256),
+ ('2400-01-01T', 182623),
+ ('2400-01-31T', 182653),
+ ('2400-02-01T', 182654),
+ ('2400-02-29T', 182682),
+ ('2400-03-01T', 182683),
+ ('2400-03-31T', 182713),
+ ('2400-04-01T', 182714),
+ ('2400-04-30T', 182743),
+ ('2400-05-01T', 182744),
+ ('2400-05-31T', 182774),
+ ('2400-06-01T', 182775),
+ ('2400-06-30T', 182804),
+ ('2400-07-01T', 182805),
+ ('2400-07-31T', 182835),
+ ('2400-08-01T', 182836),
+ ('2400-08-31T', 182866),
+ ('2400-09-01T', 182867),
+ ('2400-09-30T', 182896),
+ ('2400-10-01T', 182897),
+ ('2400-10-31T', 182927),
+ ('2400-11-01T', 182928),
+ ('2400-11-30T', 182957),
+ ('2400-12-01T', 182958),
+ ('2400-12-31T', 182988),
+ ('4000-01-01T', 767011),
+ ('4000-01-31T', 767041),
+ ('4000-02-01T', 767042),
+ ('4000-02-29T', 767070),
+ ('4000-03-01T', 767071),
+ ('4000-03-31T', 767101),
+ ('4000-04-01T', 767102),
+ ('4000-04-30T', 767131),
+ ('4000-05-01T', 767132),
+ ('4000-05-31T', 767162),
+ ('4000-06-01T', 767163),
+ ('4000-06-30T', 767192),
+ ('4000-07-01T', 767193),
+ ('4000-07-31T', 767223),
+ ('4000-08-01T', 767224),
+ ('4000-08-31T', 767254),
+ ('4000-09-01T', 767255),
+ ('4000-09-30T', 767284),
+ ('4000-10-01T', 767285),
+ ('4000-10-31T', 767315),
+ ('4000-11-01T', 767316),
+ ('4000-11-30T', 767345),
+ ('4000-12-01T', 767346),
+ ('4000-12-31T', 767376),
+ ('4321-01-01T', 884254),
+ ('4321-01-31T', 884284),
+ ('4321-02-01T', 884285),
+ ('4321-02-28T', 884312),
+ ('4321-03-01T', 884313),
+ ('4321-03-31T', 884343),
+ ('4321-04-01T', 884344),
+ ('4321-04-30T', 884373),
+ ('4321-05-01T', 884374),
+ ('4321-05-31T', 884404),
+ ('4321-06-01T', 884405),
+ ('4321-06-30T', 884434),
+ ('4321-07-01T', 884435),
+ ('4321-07-31T', 884465),
+ ('4321-08-01T', 884466),
+ ('4321-08-31T', 884496),
+ ('4321-09-01T', 884497),
+ ('4321-09-30T', 884526),
+ ('4321-10-01T', 884527),
+ ('4321-10-31T', 884557),
+ ('4321-11-01T', 884558),
+ ('4321-11-30T', 884587),
+ ('4321-12-01T', 884588),
+ ('4321-12-31T', 884618),
+ ('9999-01-01T', 2958101),
+ ('9999-01-31T', 2958131),
+ ('9999-02-01T', 2958132),
+ ('9999-02-28T', 2958159),
+ ('9999-03-01T', 2958160),
+ ('9999-03-31T', 2958190),
+ ('9999-04-01T', 2958191),
+ ('9999-04-30T', 2958220),
+ ('9999-05-01T', 2958221),
+ ('9999-05-31T', 2958251),
+ ('9999-06-01T', 2958252),
+ ('9999-06-30T', 2958281),
+ ('9999-07-01T', 2958282),
+ ('9999-07-31T', 2958312),
+ ('9999-08-01T', 2958313),
+ ('9999-08-31T', 2958343),
+ ('9999-09-01T', 2958344),
+ ('9999-09-30T', 2958373),
+ ('9999-10-01T', 2958374),
+ ('9999-10-31T', 2958404),
+ ('9999-11-01T', 2958405),
+ ('9999-11-30T', 2958434),
+ ('9999-12-01T', 2958435),
+ ('9999-12-31T', 2958465),
+ ]
+
+ # Dates and corresponding numbers from an Excel file.
+ self.excel_1904_dates = [
+ ('1904-01-01T', 0),
+ ('1904-01-31T', 30),
+ ('1904-02-01T', 31),
+ ('1904-02-29T', 59),
+ ('1904-03-01T', 60),
+ ('1904-03-31T', 90),
+ ('1904-04-01T', 91),
+ ('1904-04-30T', 120),
+ ('1904-05-01T', 121),
+ ('1904-05-31T', 151),
+ ('1904-06-01T', 152),
+ ('1904-06-30T', 181),
+ ('1904-07-01T', 182),
+ ('1904-07-31T', 212),
+ ('1904-08-01T', 213),
+ ('1904-08-31T', 243),
+ ('1904-09-01T', 244),
+ ('1904-09-30T', 273),
+ ('1904-10-01T', 274),
+ ('1904-10-31T', 304),
+ ('1904-11-01T', 305),
+ ('1904-11-30T', 334),
+ ('1904-12-01T', 335),
+ ('1904-12-31T', 365),
+ ('1907-02-27T', 1153),
+ ('1907-02-28T', 1154),
+ ('1907-03-01T', 1155),
+ ('1907-03-02T', 1156),
+ ('1907-03-03T', 1157),
+ ('1907-03-04T', 1158),
+ ('1907-03-05T', 1159),
+ ('1907-03-06T', 1160),
+ ('1999-01-01T', 34699),
+ ('1999-01-31T', 34729),
+ ('1999-02-01T', 34730),
+ ('1999-02-28T', 34757),
+ ('1999-03-01T', 34758),
+ ('1999-03-31T', 34788),
+ ('1999-04-01T', 34789),
+ ('1999-04-30T', 34818),
+ ('1999-05-01T', 34819),
+ ('1999-05-31T', 34849),
+ ('1999-06-01T', 34850),
+ ('1999-06-30T', 34879),
+ ('1999-07-01T', 34880),
+ ('1999-07-31T', 34910),
+ ('1999-08-01T', 34911),
+ ('1999-08-31T', 34941),
+ ('1999-09-01T', 34942),
+ ('1999-09-30T', 34971),
+ ('1999-10-01T', 34972),
+ ('1999-10-31T', 35002),
+ ('1999-11-01T', 35003),
+ ('1999-11-30T', 35032),
+ ('1999-12-01T', 35033),
+ ('1999-12-31T', 35063),
+ ('2000-01-01T', 35064),
+ ('2000-01-31T', 35094),
+ ('2000-02-01T', 35095),
+ ('2000-02-29T', 35123),
+ ('2000-03-01T', 35124),
+ ('2000-03-31T', 35154),
+ ('2000-04-01T', 35155),
+ ('2000-04-30T', 35184),
+ ('2000-05-01T', 35185),
+ ('2000-05-31T', 35215),
+ ('2000-06-01T', 35216),
+ ('2000-06-30T', 35245),
+ ('2000-07-01T', 35246),
+ ('2000-07-31T', 35276),
+ ('2000-08-01T', 35277),
+ ('2000-08-31T', 35307),
+ ('2000-09-01T', 35308),
+ ('2000-09-30T', 35337),
+ ('2000-10-01T', 35338),
+ ('2000-10-31T', 35368),
+ ('2000-11-01T', 35369),
+ ('2000-11-30T', 35398),
+ ('2000-12-01T', 35399),
+ ('2000-12-31T', 35429),
+ ('2001-01-01T', 35430),
+ ('2001-01-31T', 35460),
+ ('2001-02-01T', 35461),
+ ('2001-02-28T', 35488),
+ ('2001-03-01T', 35489),
+ ('2001-03-31T', 35519),
+ ('2001-04-01T', 35520),
+ ('2001-04-30T', 35549),
+ ('2001-05-01T', 35550),
+ ('2001-05-31T', 35580),
+ ('2001-06-01T', 35581),
+ ('2001-06-30T', 35610),
+ ('2001-07-01T', 35611),
+ ('2001-07-31T', 35641),
+ ('2001-08-01T', 35642),
+ ('2001-08-31T', 35672),
+ ('2001-09-01T', 35673),
+ ('2001-09-30T', 35702),
+ ('2001-10-01T', 35703),
+ ('2001-10-31T', 35733),
+ ('2001-11-01T', 35734),
+ ('2001-11-30T', 35763),
+ ('2001-12-01T', 35764),
+ ('2001-12-31T', 35794),
+ ('2400-01-01T', 181161),
+ ('2400-01-31T', 181191),
+ ('2400-02-01T', 181192),
+ ('2400-02-29T', 181220),
+ ('2400-03-01T', 181221),
+ ('2400-03-31T', 181251),
+ ('2400-04-01T', 181252),
+ ('2400-04-30T', 181281),
+ ('2400-05-01T', 181282),
+ ('2400-05-31T', 181312),
+ ('2400-06-01T', 181313),
+ ('2400-06-30T', 181342),
+ ('2400-07-01T', 181343),
+ ('2400-07-31T', 181373),
+ ('2400-08-01T', 181374),
+ ('2400-08-31T', 181404),
+ ('2400-09-01T', 181405),
+ ('2400-09-30T', 181434),
+ ('2400-10-01T', 181435),
+ ('2400-10-31T', 181465),
+ ('2400-11-01T', 181466),
+ ('2400-11-30T', 181495),
+ ('2400-12-01T', 181496),
+ ('2400-12-31T', 181526),
+ ('4000-01-01T', 765549),
+ ('4000-01-31T', 765579),
+ ('4000-02-01T', 765580),
+ ('4000-02-29T', 765608),
+ ('4000-03-01T', 765609),
+ ('4000-03-31T', 765639),
+ ('4000-04-01T', 765640),
+ ('4000-04-30T', 765669),
+ ('4000-05-01T', 765670),
+ ('4000-05-31T', 765700),
+ ('4000-06-01T', 765701),
+ ('4000-06-30T', 765730),
+ ('4000-07-01T', 765731),
+ ('4000-07-31T', 765761),
+ ('4000-08-01T', 765762),
+ ('4000-08-31T', 765792),
+ ('4000-09-01T', 765793),
+ ('4000-09-30T', 765822),
+ ('4000-10-01T', 765823),
+ ('4000-10-31T', 765853),
+ ('4000-11-01T', 765854),
+ ('4000-11-30T', 765883),
+ ('4000-12-01T', 765884),
+ ('4000-12-31T', 765914),
+ ('4321-01-01T', 882792),
+ ('4321-01-31T', 882822),
+ ('4321-02-01T', 882823),
+ ('4321-02-28T', 882850),
+ ('4321-03-01T', 882851),
+ ('4321-03-31T', 882881),
+ ('4321-04-01T', 882882),
+ ('4321-04-30T', 882911),
+ ('4321-05-01T', 882912),
+ ('4321-05-31T', 882942),
+ ('4321-06-01T', 882943),
+ ('4321-06-30T', 882972),
+ ('4321-07-01T', 882973),
+ ('4321-07-31T', 883003),
+ ('4321-08-01T', 883004),
+ ('4321-08-31T', 883034),
+ ('4321-09-01T', 883035),
+ ('4321-09-30T', 883064),
+ ('4321-10-01T', 883065),
+ ('4321-10-31T', 883095),
+ ('4321-11-01T', 883096),
+ ('4321-11-30T', 883125),
+ ('4321-12-01T', 883126),
+ ('4321-12-31T', 883156),
+ ('9999-01-01T', 2956639),
+ ('9999-01-31T', 2956669),
+ ('9999-02-01T', 2956670),
+ ('9999-02-28T', 2956697),
+ ('9999-03-01T', 2956698),
+ ('9999-03-31T', 2956728),
+ ('9999-04-01T', 2956729),
+ ('9999-04-30T', 2956758),
+ ('9999-05-01T', 2956759),
+ ('9999-05-31T', 2956789),
+ ('9999-06-01T', 2956790),
+ ('9999-06-30T', 2956819),
+ ('9999-07-01T', 2956820),
+ ('9999-07-31T', 2956850),
+ ('9999-08-01T', 2956851),
+ ('9999-08-31T', 2956881),
+ ('9999-09-01T', 2956882),
+ ('9999-09-30T', 2956911),
+ ('9999-10-01T', 2956912),
+ ('9999-10-31T', 2956942),
+ ('9999-11-01T', 2956943),
+ ('9999-11-30T', 2956972),
+ ('9999-12-01T', 2956973),
+ ('9999-12-31T', 2957003),
+ ]
+
+ def test_convert_date_time_datetime(self):
+ """Test the _convert_date_time() method with datetime objects."""
+
+ for excel_date in self.excel_dates:
+ test_date = datetime.datetime.strptime(excel_date[0], "%Y-%m-%dT")
+
+ got = self.worksheet._convert_date_time(test_date)
+ exp = excel_date[1]
+
+ self.assertEqual(got, exp)
+
+ def test_convert_date_time_date(self):
+ """Test the _convert_date_time() method with date objects."""
+
+ for excel_date in self.excel_dates:
+ date_str = excel_date[0].rstrip('T')
+ (year, month, day) = date_str.split('-')
+
+ test_date = datetime.date(int(year), int(month), int(day))
+
+ got = self.worksheet._convert_date_time(test_date)
+ exp = excel_date[1]
+
+ self.assertEqual(got, exp)
+
+ def test_convert_date_time_1904(self):
+ """Test the _convert_date_time() method with 1904 date system."""
+
+ self.worksheet.date_1904 = True
+ self.worksheet.epoch = datetime.datetime(1904, 1, 1)
+
+ for excel_date in self.excel_1904_dates:
+ date = datetime.datetime.strptime(excel_date[0], "%Y-%m-%dT")
+
+ got = self.worksheet._convert_date_time(date)
+ exp = excel_date[1]
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_date_time_03.py b/xlsxwriter/test/worksheet/test_date_time_03.py
new file mode 100644
index 0000000..6311498
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_date_time_03.py
@@ -0,0 +1,259 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+import datetime
+from ...worksheet import Worksheet
+
+
+class TestConvertDateTime(unittest.TestCase):
+ """
+ Test the Worksheet _convert_date_time() method against dates extracted
+ from Excel.
+
+ """
+ def setUp(self):
+ self.worksheet = Worksheet()
+
+ # Dates and corresponding numbers from an Excel file.
+ self.excel_seconds = [
+ ('00:00:00.000', 0),
+ ('00:15:20.213', 1.0650613425925924E-2),
+ ('00:16:48.290', 1.1670023148148148E-2),
+ ('00:55:25.446', 3.8488958333333337E-2),
+ ('01:02:46.891', 4.3598275462962965E-2),
+ ('01:04:15.597', 4.4624965277777782E-2),
+ ('01:09:40.889', 4.8389918981481483E-2),
+ ('01:11:32.560', 4.9682407407407404E-2),
+ ('01:30:19.169', 6.2721863425925936E-2),
+ ('01:48:25.580', 7.5296064814814809E-2),
+ ('02:03:31.919', 8.5786099537037031E-2),
+ ('02:11:11.986', 9.1110949074074077E-2),
+ ('02:24:37.095', 0.10042934027777778),
+ ('02:35:07.220', 0.1077224537037037),
+ ('02:45:12.109', 0.11472348379629631),
+ ('03:06:39.990', 0.12962951388888888),
+ ('03:08:08.251', 0.13065105324074075),
+ ('03:19:12.576', 0.13833999999999999),
+ ('03:29:42.574', 0.14563164351851851),
+ ('03:37:30.813', 0.1510510763888889),
+ ('04:14:38.231', 0.1768313773148148),
+ ('04:16:28.559', 0.17810832175925925),
+ ('04:17:58.222', 0.17914608796296297),
+ ('04:21:41.794', 0.18173372685185185),
+ ('04:56:35.792', 0.2059698148148148),
+ ('05:25:14.885', 0.22586672453703704),
+ ('05:26:05.724', 0.22645513888888891),
+ ('05:46:44.068', 0.24078782407407406),
+ ('05:48:01.141', 0.2416798726851852),
+ ('05:53:52.315', 0.24574438657407408),
+ ('06:14:48.580', 0.26028449074074073),
+ ('06:46:15.738', 0.28212659722222222),
+ ('07:31:20.407', 0.31343063657407405),
+ ('07:58:33.754', 0.33233511574074076),
+ ('08:07:43.130', 0.33869363425925925),
+ ('08:29:11.091', 0.35360059027777774),
+ ('09:08:15.328', 0.380732962962963),
+ ('09:30:41.781', 0.39631690972222228),
+ ('09:34:04.462', 0.39866275462962958),
+ ('09:37:23.945', 0.40097158564814817),
+ ('09:37:56.655', 0.40135017361111114),
+ ('09:45:12.230', 0.40639155092592594),
+ ('09:54:14.782', 0.41267108796296298),
+ ('09:54:22.108', 0.41275587962962962),
+ ('10:01:36.151', 0.41777952546296299),
+ ('12:09:48.602', 0.50681252314814818),
+ ('12:34:08.549', 0.52371005787037039),
+ ('12:56:06.495', 0.53896406249999995),
+ ('12:58:58.217', 0.54095158564814816),
+ ('12:59:54.263', 0.54160026620370372),
+ ('13:34:41.331', 0.56575614583333333),
+ ('13:58:28.601', 0.58227547453703699),
+ ('14:02:16.899', 0.58491781249999997),
+ ('14:36:17.444', 0.60853523148148148),
+ ('14:37:57.451', 0.60969271990740748),
+ ('14:57:42.757', 0.6234115393518519),
+ ('15:10:48.307', 0.6325035532407407),
+ ('15:14:39.890', 0.63518391203703706),
+ ('15:19:47.988', 0.63874986111111109),
+ ('16:04:24.344', 0.66972620370370362),
+ ('16:22:23.952', 0.68222166666666662),
+ ('16:29:55.999', 0.6874536921296297),
+ ('16:58:20.259', 0.70717892361111112),
+ ('17:04:02.415', 0.71113906250000003),
+ ('17:18:29.630', 0.72117627314814825),
+ ('17:47:21.323', 0.74121901620370367),
+ ('17:53:29.866', 0.74548456018518516),
+ ('17:53:41.076', 0.74561430555555563),
+ ('17:55:06.044', 0.74659773148148145),
+ ('18:14:49.151', 0.760291099537037),
+ ('18:17:45.738', 0.76233493055555546),
+ ('18:29:59.700', 0.77082986111111118),
+ ('18:33:21.233', 0.77316241898148153),
+ ('19:14:24.673', 0.80167445601851861),
+ ('19:17:12.816', 0.80362055555555545),
+ ('19:23:36.418', 0.80806039351851855),
+ ('19:46:25.908', 0.82391097222222232),
+ ('20:07:47.314', 0.83874206018518516),
+ ('20:31:37.603', 0.85529633101851854),
+ ('20:39:57.770', 0.86108530092592594),
+ ('20:50:17.067', 0.86825309027777775),
+ ('21:02:57.827', 0.87705818287037041),
+ ('21:23:05.519', 0.891036099537037),
+ ('21:34:49.572', 0.89918486111111118),
+ ('21:39:05.944', 0.90215212962962965),
+ ('21:39:18.426', 0.90229659722222222),
+ ('21:46:07.769', 0.90703436342592603),
+ ('21:57:55.662', 0.91522756944444439),
+ ('22:19:11.732', 0.92999689814814823),
+ ('22:23:51.376', 0.93323351851851843),
+ ('22:27:58.771', 0.93609688657407408),
+ ('22:43:30.392', 0.94687953703703709),
+ ('22:48:25.834', 0.95029900462962968),
+ ('22:53:51.727', 0.95407091435185187),
+ ('23:12:56.536', 0.96732101851851848),
+ ('23:15:54.109', 0.96937626157407408),
+ ('23:17:12.632', 0.97028509259259266),
+ ('23:59:59.999', 0.99999998842592586),
+ ]
+
+ # Dates and corresponding numbers from an Excel file.
+ self.excel_dates = [
+ ('1899-12-31T00:00:00.000', 0),
+ ('1899-12-31T00:15:20.213', 1.0650613425925924E-2),
+ ('1899-12-31T00:16:48.290', 1.1670023148148148E-2),
+ ('1899-12-31T00:55:25.446', 3.8488958333333337E-2),
+ ('1899-12-31T01:02:46.891', 4.3598275462962965E-2),
+ ('1899-12-31T01:04:15.597', 4.4624965277777782E-2),
+ ('1899-12-31T01:09:40.889', 4.8389918981481483E-2),
+ ('1899-12-31T01:11:32.560', 4.9682407407407404E-2),
+ ('1899-12-31T01:30:19.169', 6.2721863425925936E-2),
+ ('1899-12-31T01:48:25.580', 7.5296064814814809E-2),
+ ('1899-12-31T02:03:31.919', 8.5786099537037031E-2),
+ ('1899-12-31T02:11:11.986', 9.1110949074074077E-2),
+ ('1899-12-31T02:24:37.095', 0.10042934027777778),
+ ('1899-12-31T02:35:07.220', 0.1077224537037037),
+ ('1899-12-31T02:45:12.109', 0.11472348379629631),
+ ('1899-12-31T03:06:39.990', 0.12962951388888888),
+ ('1899-12-31T03:08:08.251', 0.13065105324074075),
+ ('1899-12-31T03:19:12.576', 0.13833999999999999),
+ ('1899-12-31T03:29:42.574', 0.14563164351851851),
+ ('1899-12-31T03:37:30.813', 0.1510510763888889),
+ ('1899-12-31T04:14:38.231', 0.1768313773148148),
+ ('1899-12-31T04:16:28.559', 0.17810832175925925),
+ ('1899-12-31T04:17:58.222', 0.17914608796296297),
+ ('1899-12-31T04:21:41.794', 0.18173372685185185),
+ ('1899-12-31T04:56:35.792', 0.2059698148148148),
+ ('1899-12-31T05:25:14.885', 0.22586672453703704),
+ ('1899-12-31T05:26:05.724', 0.22645513888888891),
+ ('1899-12-31T05:46:44.068', 0.24078782407407406),
+ ('1899-12-31T05:48:01.141', 0.2416798726851852),
+ ('1899-12-31T05:53:52.315', 0.24574438657407408),
+ ('1899-12-31T06:14:48.580', 0.26028449074074073),
+ ('1899-12-31T06:46:15.738', 0.28212659722222222),
+ ('1899-12-31T07:31:20.407', 0.31343063657407405),
+ ('1899-12-31T07:58:33.754', 0.33233511574074076),
+ ('1899-12-31T08:07:43.130', 0.33869363425925925),
+ ('1899-12-31T08:29:11.091', 0.35360059027777774),
+ ('1899-12-31T09:08:15.328', 0.380732962962963),
+ ('1899-12-31T09:30:41.781', 0.39631690972222228),
+ ('1899-12-31T09:34:04.462', 0.39866275462962958),
+ ('1899-12-31T09:37:23.945', 0.40097158564814817),
+ ('1899-12-31T09:37:56.655', 0.40135017361111114),
+ ('1899-12-31T09:45:12.230', 0.40639155092592594),
+ ('1899-12-31T09:54:14.782', 0.41267108796296298),
+ ('1899-12-31T09:54:22.108', 0.41275587962962962),
+ ('1899-12-31T10:01:36.151', 0.41777952546296299),
+ ('1899-12-31T12:09:48.602', 0.50681252314814818),
+ ('1899-12-31T12:34:08.549', 0.52371005787037039),
+ ('1899-12-31T12:56:06.495', 0.53896406249999995),
+ ('1899-12-31T12:58:58.217', 0.54095158564814816),
+ ('1899-12-31T12:59:54.263', 0.54160026620370372),
+ ('1899-12-31T13:34:41.331', 0.56575614583333333),
+ ('1899-12-31T13:58:28.601', 0.58227547453703699),
+ ('1899-12-31T14:02:16.899', 0.58491781249999997),
+ ('1899-12-31T14:36:17.444', 0.60853523148148148),
+ ('1899-12-31T14:37:57.451', 0.60969271990740748),
+ ('1899-12-31T14:57:42.757', 0.6234115393518519),
+ ('1899-12-31T15:10:48.307', 0.6325035532407407),
+ ('1899-12-31T15:14:39.890', 0.63518391203703706),
+ ('1899-12-31T15:19:47.988', 0.63874986111111109),
+ ('1899-12-31T16:04:24.344', 0.66972620370370362),
+ ('1899-12-31T16:22:23.952', 0.68222166666666662),
+ ('1899-12-31T16:29:55.999', 0.6874536921296297),
+ ('1899-12-31T16:58:20.259', 0.70717892361111112),
+ ('1899-12-31T17:04:02.415', 0.71113906250000003),
+ ('1899-12-31T17:18:29.630', 0.72117627314814825),
+ ('1899-12-31T17:47:21.323', 0.74121901620370367),
+ ('1899-12-31T17:53:29.866', 0.74548456018518516),
+ ('1899-12-31T17:53:41.076', 0.74561430555555563),
+ ('1899-12-31T17:55:06.044', 0.74659773148148145),
+ ('1899-12-31T18:14:49.151', 0.760291099537037),
+ ('1899-12-31T18:17:45.738', 0.76233493055555546),
+ ('1899-12-31T18:29:59.700', 0.77082986111111118),
+ ('1899-12-31T18:33:21.233', 0.77316241898148153),
+ ('1899-12-31T19:14:24.673', 0.80167445601851861),
+ ('1899-12-31T19:17:12.816', 0.80362055555555545),
+ ('1899-12-31T19:23:36.418', 0.80806039351851855),
+ ('1899-12-31T19:46:25.908', 0.82391097222222232),
+ ('1899-12-31T20:07:47.314', 0.83874206018518516),
+ ('1899-12-31T20:31:37.603', 0.85529633101851854),
+ ('1899-12-31T20:39:57.770', 0.86108530092592594),
+ ('1899-12-31T20:50:17.067', 0.86825309027777775),
+ ('1899-12-31T21:02:57.827', 0.87705818287037041),
+ ('1899-12-31T21:23:05.519', 0.891036099537037),
+ ('1899-12-31T21:34:49.572', 0.89918486111111118),
+ ('1899-12-31T21:39:05.944', 0.90215212962962965),
+ ('1899-12-31T21:39:18.426', 0.90229659722222222),
+ ('1899-12-31T21:46:07.769', 0.90703436342592603),
+ ('1899-12-31T21:57:55.662', 0.91522756944444439),
+ ('1899-12-31T22:19:11.732', 0.92999689814814823),
+ ('1899-12-31T22:23:51.376', 0.93323351851851843),
+ ('1899-12-31T22:27:58.771', 0.93609688657407408),
+ ('1899-12-31T22:43:30.392', 0.94687953703703709),
+ ('1899-12-31T22:48:25.834', 0.95029900462962968),
+ ('1899-12-31T22:53:51.727', 0.95407091435185187),
+ ('1899-12-31T23:12:56.536', 0.96732101851851848),
+ ('1899-12-31T23:15:54.109', 0.96937626157407408),
+ ('1899-12-31T23:17:12.632', 0.97028509259259266),
+ ('1899-12-31T23:59:59.999', 0.99999998842592586),
+ ]
+
+ def test_convert_date_time(self):
+ """Test the _convert_date_time() method for seconds."""
+
+ for excel_date in self.excel_dates:
+ date = datetime.datetime.strptime(excel_date[0], "%Y-%m-%dT%H:%M:%S.%f")
+
+ got = self.worksheet._convert_date_time(date)
+ exp = excel_date[1]
+
+ self.assertAlmostEqual(got, exp, places=15)
+
+ def test_convert_date_time_seconds_only(self):
+ """Test the _convert_date_time() method for datetime seconds."""
+
+ for excel_date in self.excel_seconds:
+ date = datetime.datetime.strptime(excel_date[0], "%H:%M:%S.%f")
+
+ got = self.worksheet._convert_date_time(date)
+ exp = excel_date[1]
+
+ self.assertAlmostEqual(got, exp, places=15)
+
+ def test_convert_date_time_seconds_only_time(self):
+ """Test the _convert_date_time() method for time seconds."""
+
+ for excel_date in self.excel_seconds:
+ date = datetime.datetime.strptime(excel_date[0], "%H:%M:%S.%f")
+
+ time = datetime.time(date.hour, date.minute, date.second, date.microsecond)
+
+ got = self.worksheet._convert_date_time(time)
+ exp = excel_date[1]
+
+ self.assertAlmostEqual(got, exp, places=15)
diff --git a/xlsxwriter/test/worksheet/test_extract_filter_tokens.py b/xlsxwriter/test/worksheet/test_extract_filter_tokens.py
new file mode 100644
index 0000000..3b7d21f
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_extract_filter_tokens.py
@@ -0,0 +1,125 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestExtractFilterTokens(unittest.TestCase):
+ """
+ Test the Worksheet _extract_filter_tokens() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_extract_filter_tokens(self):
+ """Test the _extract_filter_tokens() method"""
+
+ testcases = [
+ [
+ None,
+ [],
+ ],
+
+ [
+ '',
+ [],
+ ],
+
+ [
+ '0 < 2001',
+ ['0', '<', '2001'],
+ ],
+
+ [
+ 'x < 2000',
+ ['x', '<', '2000'],
+ ],
+
+ [
+ 'x > 2000',
+ ['x', '>', '2000'],
+ ],
+
+ [
+ 'x == 2000',
+ ['x', '==', '2000'],
+ ],
+
+ [
+ 'x > 2000 and x < 5000',
+ ['x', '>', '2000', 'and', 'x', '<', '5000'],
+ ],
+
+ [
+ 'x = "goo"',
+ ['x', '=', 'goo'],
+ ],
+
+ [
+ 'x = moo',
+ ['x', '=', 'moo'],
+ ],
+
+ [
+ 'x = "foo baz"',
+ ['x', '=', 'foo baz'],
+ ],
+
+ [
+ 'x = "moo "" bar"',
+ ['x', '=', 'moo " bar'],
+ ],
+
+ [
+ 'x = "foo bar" or x = "bar foo"',
+ ['x', '=', 'foo bar', 'or', 'x', '=', 'bar foo'],
+ ],
+
+ [
+ 'x = "foo "" bar" or x = "bar "" foo"',
+ ['x', '=', 'foo " bar', 'or', 'x', '=', 'bar " foo'],
+ ],
+
+ [
+ 'x = """"""""',
+ ['x', '=', '"""'],
+ ],
+
+ [
+ 'x = Blanks',
+ ['x', '=', 'Blanks'],
+ ],
+
+ [
+ 'x = NonBlanks',
+ ['x', '=', 'NonBlanks'],
+ ],
+
+ [
+ 'top 10 %',
+ ['top', '10', '%'],
+ ],
+
+ [
+ 'top 10 items',
+ ['top', '10', 'items'],
+ ],
+ ]
+
+ for testcase in testcases:
+ expression = testcase[0]
+
+ exp = testcase[1]
+ got = self.worksheet._extract_filter_tokens(expression)
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_initialisation.py b/xlsxwriter/test/worksheet/test_initialisation.py
new file mode 100644
index 0000000..8d1f1e3
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_initialisation.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestInitialisation(unittest.TestCase):
+ """
+ Test initialisation of the Worksheet class and call a method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test Worksheet xml_declaration()"""
+
+ self.worksheet._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_merge_range01.py b/xlsxwriter/test/worksheet/test_merge_range01.py
new file mode 100644
index 0000000..4d22cfb
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_merge_range01.py
@@ -0,0 +1,138 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+from ...format import Format
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test merged cell range"""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.str_table = SharedStringTable()
+ worksheet.select()
+ cell_format = Format({'xf_index': 1})
+
+ worksheet.merge_range('B3:C3', 'Foo', cell_format)
+
+ worksheet.select()
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="B3:C3"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="3" spans="2:3">
+ <c r="B3" s="1" t="s">
+ <v>0</v>
+ </c>
+ <c r="C3" s="1"/>
+ </row>
+ </sheetData>
+ <mergeCells count="1">
+ <mergeCell ref="B3:C3"/>
+ </mergeCells>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
+
+ def test_assemble_xml_file_write(self):
+ """Test writing a worksheet with a blank cell with write() method."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ cell_format = Format({'xf_index': 1})
+
+ # No format. Should be ignored.
+ worksheet.write(0, 0, None)
+
+ worksheet.write(1, 2, None, cell_format)
+
+ worksheet.select()
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="C2"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="2" spans="3:3">
+ <c r="C2" s="1"/>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
+
+ def test_assemble_xml_file_A1(self):
+ """Test writing a worksheet with a blank cell with A1 notation."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ cell_format = Format({'xf_index': 1})
+
+ # No format. Should be ignored.
+ worksheet.write_blank('A1', None)
+
+ worksheet.write_blank('C2', None, cell_format)
+
+ worksheet.select()
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="C2"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="2" spans="3:3">
+ <c r="C2" s="1"/>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_merge_range02.py b/xlsxwriter/test/worksheet/test_merge_range02.py
new file mode 100644
index 0000000..4f51a91
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_merge_range02.py
@@ -0,0 +1,147 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+from ...format import Format
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test merged cell range"""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.str_table = SharedStringTable()
+ worksheet.select()
+ cell_format1 = Format({'xf_index': 1})
+ cell_format2 = Format({'xf_index': 2})
+
+ worksheet.merge_range('B3:C3', 'Foo', cell_format1)
+ worksheet.merge_range('A2:D2', '', cell_format2)
+
+ worksheet.select()
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A2:D3"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="2" spans="1:4">
+ <c r="A2" s="2"/>
+ <c r="B2" s="2"/>
+ <c r="C2" s="2"/>
+ <c r="D2" s="2"/>
+ </row>
+ <row r="3" spans="1:4">
+ <c r="B3" s="1" t="s">
+ <v>0</v>
+ </c>
+ <c r="C3" s="1"/>
+ </row>
+ </sheetData>
+ <mergeCells count="2">
+ <mergeCell ref="B3:C3"/>
+ <mergeCell ref="A2:D2"/>
+ </mergeCells>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
+
+ def test_assemble_xml_file_write(self):
+ """Test writing a worksheet with a blank cell with write() method."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ cell_format = Format({'xf_index': 1})
+
+ # No format. Should be ignored.
+ worksheet.write(0, 0, None)
+
+ worksheet.write(1, 2, None, cell_format)
+
+ worksheet.select()
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="C2"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="2" spans="3:3">
+ <c r="C2" s="1"/>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
+
+ def test_assemble_xml_file_A1(self):
+ """Test writing a worksheet with a blank cell with A1 notation."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ cell_format = Format({'xf_index': 1})
+
+ # No format. Should be ignored.
+ worksheet.write_blank('A1', None)
+
+ worksheet.write_blank('C2', None, cell_format)
+
+ worksheet.select()
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="C2"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="2" spans="3:3">
+ <c r="C2" s="1"/>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_parse_filter_expression.py b/xlsxwriter/test/worksheet/test_parse_filter_expression.py
new file mode 100644
index 0000000..6029c0b
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_parse_filter_expression.py
@@ -0,0 +1,157 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestParseFilterExpression(unittest.TestCase):
+ """
+ Test the Worksheet _parse_filter_expression() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_parse_filter_expression(self):
+ """Test the _parse_filter_expression() method"""
+
+ testcases = [
+
+ [
+ 'x = 2000',
+ [2, '2000'],
+ ],
+
+ [
+ 'x == 2000',
+ [2, '2000'],
+ ],
+
+ [
+ 'x =~ 2000',
+ [2, '2000'],
+ ],
+
+ [
+ 'x eq 2000',
+ [2, '2000'],
+ ],
+
+ [
+ 'x <> 2000',
+ [5, '2000'],
+ ],
+
+ [
+ 'x != 2000',
+ [5, '2000'],
+ ],
+
+ [
+ 'x ne 2000',
+ [5, '2000'],
+ ],
+
+ [
+ 'x !~ 2000',
+ [5, '2000'],
+ ],
+
+ [
+ 'x > 2000',
+ [4, '2000'],
+ ],
+
+ [
+ 'x < 2000',
+ [1, '2000'],
+ ],
+
+ [
+ 'x >= 2000',
+ [6, '2000'],
+ ],
+
+ [
+ 'x <= 2000',
+ [3, '2000'],
+ ],
+
+ [
+ 'x > 2000 and x < 5000',
+ [4, '2000', 0, 1, '5000'],
+ ],
+
+ [
+ 'x > 2000 && x < 5000',
+ [4, '2000', 0, 1, '5000'],
+ ],
+
+ [
+ 'x > 2000 or x < 5000',
+ [4, '2000', 1, 1, '5000'],
+ ],
+
+ [
+ 'x > 2000 || x < 5000',
+ [4, '2000', 1, 1, '5000'],
+ ],
+
+ [
+ 'x = Blanks',
+ [2, 'blanks'],
+ ],
+
+ [
+ 'x = NonBlanks',
+ [5, ' '],
+ ],
+
+ [
+ 'x <> Blanks',
+ [5, ' '],
+ ],
+
+ [
+ 'x <> NonBlanks',
+ [2, 'blanks'],
+ ],
+
+ [
+ 'Top 10 Items',
+ [30, '10'],
+ ],
+
+ [
+ 'Top 20 %',
+ [31, '20'],
+ ],
+
+ [
+ 'Bottom 5 Items',
+ [32, '5'],
+ ],
+
+ [
+ 'Bottom 101 %',
+ [33, '101'],
+ ],
+ ]
+
+ for testcase in testcases:
+ expression = testcase[0]
+ tokens = self.worksheet._extract_filter_tokens(expression)
+
+ exp = testcase[1]
+ got = self.worksheet._parse_filter_expression(expression, tokens)
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_sparkline01.py b/xlsxwriter/test/worksheet/test_sparkline01.py
new file mode 100644
index 0000000..5e792fb
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_sparkline01.py
@@ -0,0 +1,69 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+ worksheet.excel_version = 2010
+
+ data = [-2, 2, 3, -1, 0]
+ worksheet.write_row('A1', data)
+
+ # No sparkline in the first testcase.
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
+ <dimension ref="A1:E1"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
+ <sheetData>
+ <row r="1" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A1">
+ <v>-2</v>
+ </c>
+ <c r="B1">
+ <v>2</v>
+ </c>
+ <c r="C1">
+ <v>3</v>
+ </c>
+ <c r="D1">
+ <v>-1</v>
+ </c>
+ <c r="E1">
+ <v>0</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_sparkline02.py b/xlsxwriter/test/worksheet/test_sparkline02.py
new file mode 100644
index 0000000..cacb6ff
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_sparkline02.py
@@ -0,0 +1,93 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+ worksheet.name = 'Sheet1'
+ worksheet.excel_version = 2010
+
+ data = [-2, 2, 3, -1, 0]
+ worksheet.write_row('A1', data)
+
+ # Set up sparklines.
+ worksheet.add_sparkline('F1', {'range': 'Sheet1!A1:E1'})
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
+ <dimension ref="A1:E1"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
+ <sheetData>
+ <row r="1" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A1">
+ <v>-2</v>
+ </c>
+ <c r="B1">
+ <v>2</v>
+ </c>
+ <c r="C1">
+ <v>3</v>
+ </c>
+ <c r="D1">
+ <v>-1</v>
+ </c>
+ <c r="E1">
+ <v>0</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ <extLst>
+ <ext xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">
+ <x14:sparklineGroups xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A1:E1</xm:f>
+ <xm:sqref>F1</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ </x14:sparklineGroups>
+ </ext>
+ </extLst>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_sparkline03.py b/xlsxwriter/test/worksheet/test_sparkline03.py
new file mode 100644
index 0000000..99c1555
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_sparkline03.py
@@ -0,0 +1,128 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+ worksheet.name = 'Sheet1'
+ worksheet.excel_version = 2010
+
+ data = [-2, 2, 3, -1, 0]
+ worksheet.write_row('A1', data)
+ worksheet.write_row('A2', data)
+
+ # Set up sparklines.
+ worksheet.add_sparkline('F1', {'range': 'Sheet1!A1:E1'})
+ worksheet.add_sparkline('F2', {'range': 'Sheet1!A2:E2'})
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
+ <dimension ref="A1:E2"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
+ <sheetData>
+ <row r="1" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A1">
+ <v>-2</v>
+ </c>
+ <c r="B1">
+ <v>2</v>
+ </c>
+ <c r="C1">
+ <v>3</v>
+ </c>
+ <c r="D1">
+ <v>-1</v>
+ </c>
+ <c r="E1">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="2" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A2">
+ <v>-2</v>
+ </c>
+ <c r="B2">
+ <v>2</v>
+ </c>
+ <c r="C2">
+ <v>3</v>
+ </c>
+ <c r="D2">
+ <v>-1</v>
+ </c>
+ <c r="E2">
+ <v>0</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ <extLst>
+ <ext xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">
+ <x14:sparklineGroups xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A2:E2</xm:f>
+ <xm:sqref>F2</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A1:E1</xm:f>
+ <xm:sqref>F1</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ </x14:sparklineGroups>
+ </ext>
+ </extLst>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_sparkline04.py b/xlsxwriter/test/worksheet/test_sparkline04.py
new file mode 100644
index 0000000..5d24b5e
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_sparkline04.py
@@ -0,0 +1,95 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+ worksheet.name = 'Sheet1'
+ worksheet.excel_version = 2010
+
+ data = [-2, 2, 3, -1, 0]
+ worksheet.write_row('A1', data)
+
+ # Set up sparklines.
+ worksheet.add_sparkline('F1', {'range': 'Sheet1!A1:E1',
+ 'type': 'column',
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
+ <dimension ref="A1:E1"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
+ <sheetData>
+ <row r="1" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A1">
+ <v>-2</v>
+ </c>
+ <c r="B1">
+ <v>2</v>
+ </c>
+ <c r="C1">
+ <v>3</v>
+ </c>
+ <c r="D1">
+ <v>-1</v>
+ </c>
+ <c r="E1">
+ <v>0</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ <extLst>
+ <ext xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">
+ <x14:sparklineGroups xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
+ <x14:sparklineGroup type="column" displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A1:E1</xm:f>
+ <xm:sqref>F1</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ </x14:sparklineGroups>
+ </ext>
+ </extLst>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_sparkline05.py b/xlsxwriter/test/worksheet/test_sparkline05.py
new file mode 100644
index 0000000..251bc5b
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_sparkline05.py
@@ -0,0 +1,96 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+ worksheet.name = 'Sheet1'
+ worksheet.excel_version = 2010
+
+ data = [-2, 2, 3, -1, 0]
+ worksheet.write_row('A1', data)
+
+ # Set up sparklines.
+ worksheet.add_sparkline('F1', {'location': ['$F$1'],
+ 'range': ['$A$1:$E$1'],
+ 'type': 'win_loss',
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
+ <dimension ref="A1:E1"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
+ <sheetData>
+ <row r="1" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A1">
+ <v>-2</v>
+ </c>
+ <c r="B1">
+ <v>2</v>
+ </c>
+ <c r="C1">
+ <v>3</v>
+ </c>
+ <c r="D1">
+ <v>-1</v>
+ </c>
+ <c r="E1">
+ <v>0</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ <extLst>
+ <ext xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">
+ <x14:sparklineGroups xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
+ <x14:sparklineGroup type="stacked" displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A1:E1</xm:f>
+ <xm:sqref>F1</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ </x14:sparklineGroups>
+ </ext>
+ </extLst>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_sparkline06.py b/xlsxwriter/test/worksheet/test_sparkline06.py
new file mode 100644
index 0000000..42f3e39
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_sparkline06.py
@@ -0,0 +1,117 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+ worksheet.name = 'Sheet1'
+ worksheet.excel_version = 2010
+
+ data = [-2, 2, 3, -1, 0]
+ worksheet.write_row('A1', data)
+ worksheet.write_row('A2', data)
+
+ # Set up sparklines.
+ worksheet.add_sparkline('A1', {'location': ['F1', 'F2'],
+ 'range': ['A1:E1', 'A2:E2'],
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
+ <dimension ref="A1:E2"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
+ <sheetData>
+ <row r="1" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A1">
+ <v>-2</v>
+ </c>
+ <c r="B1">
+ <v>2</v>
+ </c>
+ <c r="C1">
+ <v>3</v>
+ </c>
+ <c r="D1">
+ <v>-1</v>
+ </c>
+ <c r="E1">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="2" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A2">
+ <v>-2</v>
+ </c>
+ <c r="B2">
+ <v>2</v>
+ </c>
+ <c r="C2">
+ <v>3</v>
+ </c>
+ <c r="D2">
+ <v>-1</v>
+ </c>
+ <c r="E2">
+ <v>0</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ <extLst>
+ <ext xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">
+ <x14:sparklineGroups xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A1:E1</xm:f>
+ <xm:sqref>F1</xm:sqref>
+ </x14:sparkline>
+ <x14:sparkline>
+ <xm:f>Sheet1!A2:E2</xm:f>
+ <xm:sqref>F2</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ </x14:sparklineGroups>
+ </ext>
+ </extLst>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_sparkline07.py b/xlsxwriter/test/worksheet/test_sparkline07.py
new file mode 100644
index 0000000..530dc92
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_sparkline07.py
@@ -0,0 +1,328 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+ worksheet.name = 'Sheet1'
+ worksheet.excel_version = 2010
+
+ data = [-2, 2, 3, -1, 0]
+ worksheet.write_row('A1', data)
+ worksheet.write_row('A2', data)
+ worksheet.write_row('A3', data)
+ worksheet.write_row('A4', data)
+ worksheet.write_row('A5', data)
+ worksheet.write_row('A6', data)
+ worksheet.write_row('A7', data)
+
+ # Set up sparklines.
+ worksheet.add_sparkline('F1', {'range': 'A1:E1',
+ 'type': 'column',
+ 'high_point': 1})
+
+ worksheet.add_sparkline('F2', {'range': 'A2:E2',
+ 'type': 'column',
+ 'low_point': 1})
+
+ worksheet.add_sparkline('F3', {'range': 'A3:E3',
+ 'type': 'column',
+ 'negative_points': 1})
+
+ worksheet.add_sparkline('F4', {'range': 'A4:E4',
+ 'type': 'column',
+ 'first_point': 1})
+
+ worksheet.add_sparkline('F5', {'range': 'A5:E5',
+ 'type': 'column',
+ 'last_point': 1})
+
+ worksheet.add_sparkline('F6', {'range': 'A6:E6',
+ 'type': 'column',
+ 'markers': 1})
+
+ worksheet.add_sparkline('F7', {'range': 'A7:E7',
+ 'type': 'column',
+ 'high_point': 1,
+ 'low_point': 1,
+ 'negative_points': 1,
+ 'first_point': 1,
+ 'last_point': 1,
+ 'markers': 1})
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
+ <dimension ref="A1:E7"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
+ <sheetData>
+ <row r="1" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A1">
+ <v>-2</v>
+ </c>
+ <c r="B1">
+ <v>2</v>
+ </c>
+ <c r="C1">
+ <v>3</v>
+ </c>
+ <c r="D1">
+ <v>-1</v>
+ </c>
+ <c r="E1">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="2" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A2">
+ <v>-2</v>
+ </c>
+ <c r="B2">
+ <v>2</v>
+ </c>
+ <c r="C2">
+ <v>3</v>
+ </c>
+ <c r="D2">
+ <v>-1</v>
+ </c>
+ <c r="E2">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="3" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A3">
+ <v>-2</v>
+ </c>
+ <c r="B3">
+ <v>2</v>
+ </c>
+ <c r="C3">
+ <v>3</v>
+ </c>
+ <c r="D3">
+ <v>-1</v>
+ </c>
+ <c r="E3">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="4" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A4">
+ <v>-2</v>
+ </c>
+ <c r="B4">
+ <v>2</v>
+ </c>
+ <c r="C4">
+ <v>3</v>
+ </c>
+ <c r="D4">
+ <v>-1</v>
+ </c>
+ <c r="E4">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="5" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A5">
+ <v>-2</v>
+ </c>
+ <c r="B5">
+ <v>2</v>
+ </c>
+ <c r="C5">
+ <v>3</v>
+ </c>
+ <c r="D5">
+ <v>-1</v>
+ </c>
+ <c r="E5">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="6" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A6">
+ <v>-2</v>
+ </c>
+ <c r="B6">
+ <v>2</v>
+ </c>
+ <c r="C6">
+ <v>3</v>
+ </c>
+ <c r="D6">
+ <v>-1</v>
+ </c>
+ <c r="E6">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="7" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A7">
+ <v>-2</v>
+ </c>
+ <c r="B7">
+ <v>2</v>
+ </c>
+ <c r="C7">
+ <v>3</v>
+ </c>
+ <c r="D7">
+ <v>-1</v>
+ </c>
+ <c r="E7">
+ <v>0</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ <extLst>
+ <ext xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">
+ <x14:sparklineGroups xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
+ <x14:sparklineGroup type="column" displayEmptyCellsAs="gap" markers="1" high="1" low="1" first="1" last="1" negative="1">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A7:E7</xm:f>
+ <xm:sqref>F7</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup type="column" displayEmptyCellsAs="gap" markers="1">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A6:E6</xm:f>
+ <xm:sqref>F6</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup type="column" displayEmptyCellsAs="gap" last="1">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A5:E5</xm:f>
+ <xm:sqref>F5</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup type="column" displayEmptyCellsAs="gap" first="1">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A4:E4</xm:f>
+ <xm:sqref>F4</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup type="column" displayEmptyCellsAs="gap" negative="1">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A3:E3</xm:f>
+ <xm:sqref>F3</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup type="column" displayEmptyCellsAs="gap" low="1">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A2:E2</xm:f>
+ <xm:sqref>F2</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup type="column" displayEmptyCellsAs="gap" high="1">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A1:E1</xm:f>
+ <xm:sqref>F1</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ </x14:sparklineGroups>
+ </ext>
+ </extLst>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_sparkline08.py b/xlsxwriter/test/worksheet/test_sparkline08.py
new file mode 100644
index 0000000..3e23b33
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_sparkline08.py
@@ -0,0 +1,166 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+ worksheet.name = 'Sheet1'
+ worksheet.excel_version = 2010
+
+ data = [-2, 2, 3, -1, 0]
+ worksheet.write_row('A1', data)
+ worksheet.write_row('A2', data)
+ worksheet.write_row('A3', data)
+
+ # Set up sparklines.
+ worksheet.add_sparkline('F1', {'range': 'Sheet1!A1:E1',
+ 'style': 1})
+ worksheet.add_sparkline('F2', {'range': 'Sheet1!A2:E2',
+ 'style': 2})
+ worksheet.add_sparkline('F3', {'range': 'Sheet1!A3:E3',
+ 'series_color': '#FF0000'})
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
+ <dimension ref="A1:E3"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
+ <sheetData>
+ <row r="1" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A1">
+ <v>-2</v>
+ </c>
+ <c r="B1">
+ <v>2</v>
+ </c>
+ <c r="C1">
+ <v>3</v>
+ </c>
+ <c r="D1">
+ <v>-1</v>
+ </c>
+ <c r="E1">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="2" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A2">
+ <v>-2</v>
+ </c>
+ <c r="B2">
+ <v>2</v>
+ </c>
+ <c r="C2">
+ <v>3</v>
+ </c>
+ <c r="D2">
+ <v>-1</v>
+ </c>
+ <c r="E2">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="3" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A3">
+ <v>-2</v>
+ </c>
+ <c r="B3">
+ <v>2</v>
+ </c>
+ <c r="C3">
+ <v>3</v>
+ </c>
+ <c r="D3">
+ <v>-1</v>
+ </c>
+ <c r="E3">
+ <v>0</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ <extLst>
+ <ext xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">
+ <x14:sparklineGroups xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries rgb="FFFF0000"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A3:E3</xm:f>
+ <xm:sqref>F3</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="5" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="6"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="5" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="5" tint="0.39997558519241921"/>
+ <x14:colorLast theme="5" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="5"/>
+ <x14:colorLow theme="5"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A2:E2</xm:f>
+ <xm:sqref>F2</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A1:E1</xm:f>
+ <xm:sqref>F1</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ </x14:sparklineGroups>
+ </ext>
+ </extLst>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_sparkline09.py b/xlsxwriter/test/worksheet/test_sparkline09.py
new file mode 100644
index 0000000..8718f16
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_sparkline09.py
@@ -0,0 +1,1255 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+ worksheet.name = 'Sheet1'
+ worksheet.excel_version = 2010
+
+ data = [-2, 2, 3, -1, 0]
+ worksheet.write_row('A1', data)
+
+ # Set up sparklines.
+
+ # Test all the styles.
+ for i in range(36):
+ row = i + 1
+ sparkrange = 'Sheet1!A%d:E%d' % (row, row)
+ worksheet.write_row(i, 0, data)
+ worksheet.add_sparkline(i, 5, {'range': sparkrange,
+ 'style': row})
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
+ <dimension ref="A1:E36"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
+ <sheetData>
+ <row r="1" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A1">
+ <v>-2</v>
+ </c>
+ <c r="B1">
+ <v>2</v>
+ </c>
+ <c r="C1">
+ <v>3</v>
+ </c>
+ <c r="D1">
+ <v>-1</v>
+ </c>
+ <c r="E1">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="2" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A2">
+ <v>-2</v>
+ </c>
+ <c r="B2">
+ <v>2</v>
+ </c>
+ <c r="C2">
+ <v>3</v>
+ </c>
+ <c r="D2">
+ <v>-1</v>
+ </c>
+ <c r="E2">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="3" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A3">
+ <v>-2</v>
+ </c>
+ <c r="B3">
+ <v>2</v>
+ </c>
+ <c r="C3">
+ <v>3</v>
+ </c>
+ <c r="D3">
+ <v>-1</v>
+ </c>
+ <c r="E3">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="4" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A4">
+ <v>-2</v>
+ </c>
+ <c r="B4">
+ <v>2</v>
+ </c>
+ <c r="C4">
+ <v>3</v>
+ </c>
+ <c r="D4">
+ <v>-1</v>
+ </c>
+ <c r="E4">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="5" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A5">
+ <v>-2</v>
+ </c>
+ <c r="B5">
+ <v>2</v>
+ </c>
+ <c r="C5">
+ <v>3</v>
+ </c>
+ <c r="D5">
+ <v>-1</v>
+ </c>
+ <c r="E5">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="6" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A6">
+ <v>-2</v>
+ </c>
+ <c r="B6">
+ <v>2</v>
+ </c>
+ <c r="C6">
+ <v>3</v>
+ </c>
+ <c r="D6">
+ <v>-1</v>
+ </c>
+ <c r="E6">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="7" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A7">
+ <v>-2</v>
+ </c>
+ <c r="B7">
+ <v>2</v>
+ </c>
+ <c r="C7">
+ <v>3</v>
+ </c>
+ <c r="D7">
+ <v>-1</v>
+ </c>
+ <c r="E7">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="8" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A8">
+ <v>-2</v>
+ </c>
+ <c r="B8">
+ <v>2</v>
+ </c>
+ <c r="C8">
+ <v>3</v>
+ </c>
+ <c r="D8">
+ <v>-1</v>
+ </c>
+ <c r="E8">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="9" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A9">
+ <v>-2</v>
+ </c>
+ <c r="B9">
+ <v>2</v>
+ </c>
+ <c r="C9">
+ <v>3</v>
+ </c>
+ <c r="D9">
+ <v>-1</v>
+ </c>
+ <c r="E9">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="10" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A10">
+ <v>-2</v>
+ </c>
+ <c r="B10">
+ <v>2</v>
+ </c>
+ <c r="C10">
+ <v>3</v>
+ </c>
+ <c r="D10">
+ <v>-1</v>
+ </c>
+ <c r="E10">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="11" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A11">
+ <v>-2</v>
+ </c>
+ <c r="B11">
+ <v>2</v>
+ </c>
+ <c r="C11">
+ <v>3</v>
+ </c>
+ <c r="D11">
+ <v>-1</v>
+ </c>
+ <c r="E11">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="12" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A12">
+ <v>-2</v>
+ </c>
+ <c r="B12">
+ <v>2</v>
+ </c>
+ <c r="C12">
+ <v>3</v>
+ </c>
+ <c r="D12">
+ <v>-1</v>
+ </c>
+ <c r="E12">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="13" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A13">
+ <v>-2</v>
+ </c>
+ <c r="B13">
+ <v>2</v>
+ </c>
+ <c r="C13">
+ <v>3</v>
+ </c>
+ <c r="D13">
+ <v>-1</v>
+ </c>
+ <c r="E13">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="14" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A14">
+ <v>-2</v>
+ </c>
+ <c r="B14">
+ <v>2</v>
+ </c>
+ <c r="C14">
+ <v>3</v>
+ </c>
+ <c r="D14">
+ <v>-1</v>
+ </c>
+ <c r="E14">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="15" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A15">
+ <v>-2</v>
+ </c>
+ <c r="B15">
+ <v>2</v>
+ </c>
+ <c r="C15">
+ <v>3</v>
+ </c>
+ <c r="D15">
+ <v>-1</v>
+ </c>
+ <c r="E15">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="16" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A16">
+ <v>-2</v>
+ </c>
+ <c r="B16">
+ <v>2</v>
+ </c>
+ <c r="C16">
+ <v>3</v>
+ </c>
+ <c r="D16">
+ <v>-1</v>
+ </c>
+ <c r="E16">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="17" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A17">
+ <v>-2</v>
+ </c>
+ <c r="B17">
+ <v>2</v>
+ </c>
+ <c r="C17">
+ <v>3</v>
+ </c>
+ <c r="D17">
+ <v>-1</v>
+ </c>
+ <c r="E17">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="18" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A18">
+ <v>-2</v>
+ </c>
+ <c r="B18">
+ <v>2</v>
+ </c>
+ <c r="C18">
+ <v>3</v>
+ </c>
+ <c r="D18">
+ <v>-1</v>
+ </c>
+ <c r="E18">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="19" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A19">
+ <v>-2</v>
+ </c>
+ <c r="B19">
+ <v>2</v>
+ </c>
+ <c r="C19">
+ <v>3</v>
+ </c>
+ <c r="D19">
+ <v>-1</v>
+ </c>
+ <c r="E19">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="20" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A20">
+ <v>-2</v>
+ </c>
+ <c r="B20">
+ <v>2</v>
+ </c>
+ <c r="C20">
+ <v>3</v>
+ </c>
+ <c r="D20">
+ <v>-1</v>
+ </c>
+ <c r="E20">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="21" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A21">
+ <v>-2</v>
+ </c>
+ <c r="B21">
+ <v>2</v>
+ </c>
+ <c r="C21">
+ <v>3</v>
+ </c>
+ <c r="D21">
+ <v>-1</v>
+ </c>
+ <c r="E21">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="22" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A22">
+ <v>-2</v>
+ </c>
+ <c r="B22">
+ <v>2</v>
+ </c>
+ <c r="C22">
+ <v>3</v>
+ </c>
+ <c r="D22">
+ <v>-1</v>
+ </c>
+ <c r="E22">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="23" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A23">
+ <v>-2</v>
+ </c>
+ <c r="B23">
+ <v>2</v>
+ </c>
+ <c r="C23">
+ <v>3</v>
+ </c>
+ <c r="D23">
+ <v>-1</v>
+ </c>
+ <c r="E23">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="24" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A24">
+ <v>-2</v>
+ </c>
+ <c r="B24">
+ <v>2</v>
+ </c>
+ <c r="C24">
+ <v>3</v>
+ </c>
+ <c r="D24">
+ <v>-1</v>
+ </c>
+ <c r="E24">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="25" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A25">
+ <v>-2</v>
+ </c>
+ <c r="B25">
+ <v>2</v>
+ </c>
+ <c r="C25">
+ <v>3</v>
+ </c>
+ <c r="D25">
+ <v>-1</v>
+ </c>
+ <c r="E25">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="26" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A26">
+ <v>-2</v>
+ </c>
+ <c r="B26">
+ <v>2</v>
+ </c>
+ <c r="C26">
+ <v>3</v>
+ </c>
+ <c r="D26">
+ <v>-1</v>
+ </c>
+ <c r="E26">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="27" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A27">
+ <v>-2</v>
+ </c>
+ <c r="B27">
+ <v>2</v>
+ </c>
+ <c r="C27">
+ <v>3</v>
+ </c>
+ <c r="D27">
+ <v>-1</v>
+ </c>
+ <c r="E27">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="28" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A28">
+ <v>-2</v>
+ </c>
+ <c r="B28">
+ <v>2</v>
+ </c>
+ <c r="C28">
+ <v>3</v>
+ </c>
+ <c r="D28">
+ <v>-1</v>
+ </c>
+ <c r="E28">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="29" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A29">
+ <v>-2</v>
+ </c>
+ <c r="B29">
+ <v>2</v>
+ </c>
+ <c r="C29">
+ <v>3</v>
+ </c>
+ <c r="D29">
+ <v>-1</v>
+ </c>
+ <c r="E29">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="30" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A30">
+ <v>-2</v>
+ </c>
+ <c r="B30">
+ <v>2</v>
+ </c>
+ <c r="C30">
+ <v>3</v>
+ </c>
+ <c r="D30">
+ <v>-1</v>
+ </c>
+ <c r="E30">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="31" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A31">
+ <v>-2</v>
+ </c>
+ <c r="B31">
+ <v>2</v>
+ </c>
+ <c r="C31">
+ <v>3</v>
+ </c>
+ <c r="D31">
+ <v>-1</v>
+ </c>
+ <c r="E31">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="32" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A32">
+ <v>-2</v>
+ </c>
+ <c r="B32">
+ <v>2</v>
+ </c>
+ <c r="C32">
+ <v>3</v>
+ </c>
+ <c r="D32">
+ <v>-1</v>
+ </c>
+ <c r="E32">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="33" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A33">
+ <v>-2</v>
+ </c>
+ <c r="B33">
+ <v>2</v>
+ </c>
+ <c r="C33">
+ <v>3</v>
+ </c>
+ <c r="D33">
+ <v>-1</v>
+ </c>
+ <c r="E33">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="34" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A34">
+ <v>-2</v>
+ </c>
+ <c r="B34">
+ <v>2</v>
+ </c>
+ <c r="C34">
+ <v>3</v>
+ </c>
+ <c r="D34">
+ <v>-1</v>
+ </c>
+ <c r="E34">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="35" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A35">
+ <v>-2</v>
+ </c>
+ <c r="B35">
+ <v>2</v>
+ </c>
+ <c r="C35">
+ <v>3</v>
+ </c>
+ <c r="D35">
+ <v>-1</v>
+ </c>
+ <c r="E35">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="36" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A36">
+ <v>-2</v>
+ </c>
+ <c r="B36">
+ <v>2</v>
+ </c>
+ <c r="C36">
+ <v>3</v>
+ </c>
+ <c r="D36">
+ <v>-1</v>
+ </c>
+ <c r="E36">
+ <v>0</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ <extLst>
+ <ext xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">
+ <x14:sparklineGroups xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="1"/>
+ <x14:colorNegative theme="9"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="8"/>
+ <x14:colorFirst theme="4"/>
+ <x14:colorLast theme="5"/>
+ <x14:colorHigh theme="6"/>
+ <x14:colorLow theme="7"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A36:E36</xm:f>
+ <xm:sqref>F36</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="3"/>
+ <x14:colorNegative theme="9"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="8"/>
+ <x14:colorFirst theme="4"/>
+ <x14:colorLast theme="5"/>
+ <x14:colorHigh theme="6"/>
+ <x14:colorLow theme="7"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A35:E35</xm:f>
+ <xm:sqref>F35</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries rgb="FF00B050"/>
+ <x14:colorNegative rgb="FFFF0000"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers rgb="FF0070C0"/>
+ <x14:colorFirst rgb="FFFFC000"/>
+ <x14:colorLast rgb="FFFFC000"/>
+ <x14:colorHigh rgb="FF00B050"/>
+ <x14:colorLow rgb="FFFF0000"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A34:E34</xm:f>
+ <xm:sqref>F34</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries rgb="FFC6EFCE"/>
+ <x14:colorNegative rgb="FFFFC7CE"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers rgb="FF8CADD6"/>
+ <x14:colorFirst rgb="FFFFDC47"/>
+ <x14:colorLast rgb="FFFFEB9C"/>
+ <x14:colorHigh rgb="FF60D276"/>
+ <x14:colorLow rgb="FFFF5367"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A33:E33</xm:f>
+ <xm:sqref>F33</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries rgb="FF5687C2"/>
+ <x14:colorNegative rgb="FFFFB620"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers rgb="FFD70077"/>
+ <x14:colorFirst rgb="FF777777"/>
+ <x14:colorLast rgb="FF359CEB"/>
+ <x14:colorHigh rgb="FF56BE79"/>
+ <x14:colorLow rgb="FFFF5055"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A32:E32</xm:f>
+ <xm:sqref>F32</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries rgb="FF5F5F5F"/>
+ <x14:colorNegative rgb="FFFFB620"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers rgb="FFD70077"/>
+ <x14:colorFirst rgb="FF5687C2"/>
+ <x14:colorLast rgb="FF359CEB"/>
+ <x14:colorHigh rgb="FF56BE79"/>
+ <x14:colorLow rgb="FFFF5055"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A31:E31</xm:f>
+ <xm:sqref>F31</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries rgb="FF0070C0"/>
+ <x14:colorNegative rgb="FF000000"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers rgb="FF000000"/>
+ <x14:colorFirst rgb="FF000000"/>
+ <x14:colorLast rgb="FF000000"/>
+ <x14:colorHigh rgb="FF000000"/>
+ <x14:colorLow rgb="FF000000"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A30:E30</xm:f>
+ <xm:sqref>F30</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries rgb="FF376092"/>
+ <x14:colorNegative rgb="FFD00000"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers rgb="FFD00000"/>
+ <x14:colorFirst rgb="FFD00000"/>
+ <x14:colorLast rgb="FFD00000"/>
+ <x14:colorHigh rgb="FFD00000"/>
+ <x14:colorLow rgb="FFD00000"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A29:E29</xm:f>
+ <xm:sqref>F29</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries rgb="FF000000"/>
+ <x14:colorNegative rgb="FF0070C0"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers rgb="FF0070C0"/>
+ <x14:colorFirst rgb="FF0070C0"/>
+ <x14:colorLast rgb="FF0070C0"/>
+ <x14:colorHigh rgb="FF0070C0"/>
+ <x14:colorLow rgb="FF0070C0"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A28:E28</xm:f>
+ <xm:sqref>F28</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries rgb="FF323232"/>
+ <x14:colorNegative rgb="FFD00000"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers rgb="FFD00000"/>
+ <x14:colorFirst rgb="FFD00000"/>
+ <x14:colorLast rgb="FFD00000"/>
+ <x14:colorHigh rgb="FFD00000"/>
+ <x14:colorLow rgb="FFD00000"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A27:E27</xm:f>
+ <xm:sqref>F27</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="1" tint="0.34998626667073579"/>
+ <x14:colorNegative theme="0" tint="-0.249977111117893"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="0" tint="-0.249977111117893"/>
+ <x14:colorFirst theme="0" tint="-0.249977111117893"/>
+ <x14:colorLast theme="0" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="0" tint="-0.249977111117893"/>
+ <x14:colorLow theme="0" tint="-0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A26:E26</xm:f>
+ <xm:sqref>F26</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="1" tint="0.499984740745262"/>
+ <x14:colorNegative theme="1" tint="0.249977111117893"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="1" tint="0.249977111117893"/>
+ <x14:colorFirst theme="1" tint="0.249977111117893"/>
+ <x14:colorLast theme="1" tint="0.249977111117893"/>
+ <x14:colorHigh theme="1" tint="0.249977111117893"/>
+ <x14:colorLow theme="1" tint="0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A25:E25</xm:f>
+ <xm:sqref>F25</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="9" tint="0.39997558519241921"/>
+ <x14:colorNegative theme="0" tint="-0.499984740745262"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="9" tint="0.79998168889431442"/>
+ <x14:colorFirst theme="9" tint="-0.249977111117893"/>
+ <x14:colorLast theme="9" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="9" tint="-0.499984740745262"/>
+ <x14:colorLow theme="9" tint="-0.499984740745262"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A24:E24</xm:f>
+ <xm:sqref>F24</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="8" tint="0.39997558519241921"/>
+ <x14:colorNegative theme="0" tint="-0.499984740745262"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="8" tint="0.79998168889431442"/>
+ <x14:colorFirst theme="8" tint="-0.249977111117893"/>
+ <x14:colorLast theme="8" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="8" tint="-0.499984740745262"/>
+ <x14:colorLow theme="8" tint="-0.499984740745262"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A23:E23</xm:f>
+ <xm:sqref>F23</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="7" tint="0.39997558519241921"/>
+ <x14:colorNegative theme="0" tint="-0.499984740745262"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="7" tint="0.79998168889431442"/>
+ <x14:colorFirst theme="7" tint="-0.249977111117893"/>
+ <x14:colorLast theme="7" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="7" tint="-0.499984740745262"/>
+ <x14:colorLow theme="7" tint="-0.499984740745262"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A22:E22</xm:f>
+ <xm:sqref>F22</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="6" tint="0.39997558519241921"/>
+ <x14:colorNegative theme="0" tint="-0.499984740745262"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="6" tint="0.79998168889431442"/>
+ <x14:colorFirst theme="6" tint="-0.249977111117893"/>
+ <x14:colorLast theme="6" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="6" tint="-0.499984740745262"/>
+ <x14:colorLow theme="6" tint="-0.499984740745262"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A21:E21</xm:f>
+ <xm:sqref>F21</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="5" tint="0.39997558519241921"/>
+ <x14:colorNegative theme="0" tint="-0.499984740745262"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="5" tint="0.79998168889431442"/>
+ <x14:colorFirst theme="5" tint="-0.249977111117893"/>
+ <x14:colorLast theme="5" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="5" tint="-0.499984740745262"/>
+ <x14:colorLow theme="5" tint="-0.499984740745262"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A20:E20</xm:f>
+ <xm:sqref>F20</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="4" tint="0.39997558519241921"/>
+ <x14:colorNegative theme="0" tint="-0.499984740745262"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="0.79998168889431442"/>
+ <x14:colorFirst theme="4" tint="-0.249977111117893"/>
+ <x14:colorLast theme="4" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="4" tint="-0.499984740745262"/>
+ <x14:colorLow theme="4" tint="-0.499984740745262"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A19:E19</xm:f>
+ <xm:sqref>F19</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="9"/>
+ <x14:colorNegative theme="4"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="9" tint="-0.249977111117893"/>
+ <x14:colorFirst theme="9" tint="-0.249977111117893"/>
+ <x14:colorLast theme="9" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="9" tint="-0.249977111117893"/>
+ <x14:colorLow theme="9" tint="-0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A18:E18</xm:f>
+ <xm:sqref>F18</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="8"/>
+ <x14:colorNegative theme="9"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="8" tint="-0.249977111117893"/>
+ <x14:colorFirst theme="8" tint="-0.249977111117893"/>
+ <x14:colorLast theme="8" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="8" tint="-0.249977111117893"/>
+ <x14:colorLow theme="8" tint="-0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A17:E17</xm:f>
+ <xm:sqref>F17</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="7"/>
+ <x14:colorNegative theme="8"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="7" tint="-0.249977111117893"/>
+ <x14:colorFirst theme="7" tint="-0.249977111117893"/>
+ <x14:colorLast theme="7" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="7" tint="-0.249977111117893"/>
+ <x14:colorLow theme="7" tint="-0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A16:E16</xm:f>
+ <xm:sqref>F16</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="6"/>
+ <x14:colorNegative theme="7"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="6" tint="-0.249977111117893"/>
+ <x14:colorFirst theme="6" tint="-0.249977111117893"/>
+ <x14:colorLast theme="6" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="6" tint="-0.249977111117893"/>
+ <x14:colorLow theme="6" tint="-0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A15:E15</xm:f>
+ <xm:sqref>F15</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="5"/>
+ <x14:colorNegative theme="6"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="5" tint="-0.249977111117893"/>
+ <x14:colorFirst theme="5" tint="-0.249977111117893"/>
+ <x14:colorLast theme="5" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="5" tint="-0.249977111117893"/>
+ <x14:colorLow theme="5" tint="-0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A14:E14</xm:f>
+ <xm:sqref>F14</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="4"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.249977111117893"/>
+ <x14:colorFirst theme="4" tint="-0.249977111117893"/>
+ <x14:colorLast theme="4" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="4" tint="-0.249977111117893"/>
+ <x14:colorLow theme="4" tint="-0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A13:E13</xm:f>
+ <xm:sqref>F13</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="9" tint="-0.249977111117893"/>
+ <x14:colorNegative theme="4"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.249977111117893"/>
+ <x14:colorFirst theme="4" tint="-0.249977111117893"/>
+ <x14:colorLast theme="4" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="4" tint="-0.249977111117893"/>
+ <x14:colorLow theme="4" tint="-0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A12:E12</xm:f>
+ <xm:sqref>F12</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="8" tint="-0.249977111117893"/>
+ <x14:colorNegative theme="9"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="9" tint="-0.249977111117893"/>
+ <x14:colorFirst theme="9" tint="-0.249977111117893"/>
+ <x14:colorLast theme="9" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="9" tint="-0.249977111117893"/>
+ <x14:colorLow theme="9" tint="-0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A11:E11</xm:f>
+ <xm:sqref>F11</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="7" tint="-0.249977111117893"/>
+ <x14:colorNegative theme="8"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="8" tint="-0.249977111117893"/>
+ <x14:colorFirst theme="8" tint="-0.249977111117893"/>
+ <x14:colorLast theme="8" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="8" tint="-0.249977111117893"/>
+ <x14:colorLow theme="8" tint="-0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A10:E10</xm:f>
+ <xm:sqref>F10</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="6" tint="-0.249977111117893"/>
+ <x14:colorNegative theme="7"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="7" tint="-0.249977111117893"/>
+ <x14:colorFirst theme="7" tint="-0.249977111117893"/>
+ <x14:colorLast theme="7" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="7" tint="-0.249977111117893"/>
+ <x14:colorLow theme="7" tint="-0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A9:E9</xm:f>
+ <xm:sqref>F9</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="5" tint="-0.249977111117893"/>
+ <x14:colorNegative theme="6"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="6" tint="-0.249977111117893"/>
+ <x14:colorFirst theme="6" tint="-0.249977111117893"/>
+ <x14:colorLast theme="6" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="6" tint="-0.249977111117893"/>
+ <x14:colorLow theme="6" tint="-0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A8:E8</xm:f>
+ <xm:sqref>F8</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="4" tint="-0.249977111117893"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="5" tint="-0.249977111117893"/>
+ <x14:colorFirst theme="5" tint="-0.249977111117893"/>
+ <x14:colorLast theme="5" tint="-0.249977111117893"/>
+ <x14:colorHigh theme="5" tint="-0.249977111117893"/>
+ <x14:colorLow theme="5" tint="-0.249977111117893"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A7:E7</xm:f>
+ <xm:sqref>F7</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="9" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="4"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="9" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="9" tint="0.39997558519241921"/>
+ <x14:colorLast theme="9" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="9"/>
+ <x14:colorLow theme="9"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A6:E6</xm:f>
+ <xm:sqref>F6</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="8" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="9"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="8" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="8" tint="0.39997558519241921"/>
+ <x14:colorLast theme="8" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="8"/>
+ <x14:colorLow theme="8"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A5:E5</xm:f>
+ <xm:sqref>F5</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="7" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="8"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="7" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="7" tint="0.39997558519241921"/>
+ <x14:colorLast theme="7" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="7"/>
+ <x14:colorLow theme="7"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A4:E4</xm:f>
+ <xm:sqref>F4</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="6" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="7"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="6" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="6" tint="0.39997558519241921"/>
+ <x14:colorLast theme="6" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="6"/>
+ <x14:colorLow theme="6"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A3:E3</xm:f>
+ <xm:sqref>F3</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="5" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="6"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="5" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="5" tint="0.39997558519241921"/>
+ <x14:colorLast theme="5" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="5"/>
+ <x14:colorLow theme="5"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A2:E2</xm:f>
+ <xm:sqref>F2</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup displayEmptyCellsAs="gap">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A1:E1</xm:f>
+ <xm:sqref>F1</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ </x14:sparklineGroups>
+ </ext>
+ </extLst>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_sparkline10.py b/xlsxwriter/test/worksheet/test_sparkline10.py
new file mode 100644
index 0000000..97faf73
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_sparkline10.py
@@ -0,0 +1,107 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+ worksheet.name = 'Sheet1'
+ worksheet.excel_version = 2010
+
+ data = [-2, 2, 3, -1, 0]
+ worksheet.write_row('A1', data)
+
+ # Set up sparklines.
+ worksheet.add_sparkline('F1', {'range': 'A1:E1',
+ 'high_point': True,
+ 'low_point': True,
+ 'negative_points': True,
+ 'first_point': True,
+ 'last_point': True,
+ 'markers': True,
+ 'series_color': '#C00000',
+ 'negative_color': '#FF0000',
+ 'markers_color': '#FFC000',
+ 'first_color': '#00B050',
+ 'last_color': '#00B0F0',
+ 'high_color': '#FFFF00',
+ 'low_color': '#92D050',
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
+ <dimension ref="A1:E1"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
+ <sheetData>
+ <row r="1" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A1">
+ <v>-2</v>
+ </c>
+ <c r="B1">
+ <v>2</v>
+ </c>
+ <c r="C1">
+ <v>3</v>
+ </c>
+ <c r="D1">
+ <v>-1</v>
+ </c>
+ <c r="E1">
+ <v>0</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ <extLst>
+ <ext xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">
+ <x14:sparklineGroups xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
+ <x14:sparklineGroup displayEmptyCellsAs="gap" markers="1" high="1" low="1" first="1" last="1" negative="1">
+ <x14:colorSeries rgb="FFC00000"/>
+ <x14:colorNegative rgb="FFFF0000"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers rgb="FFFFC000"/>
+ <x14:colorFirst rgb="FF00B050"/>
+ <x14:colorLast rgb="FF00B0F0"/>
+ <x14:colorHigh rgb="FFFFFF00"/>
+ <x14:colorLow rgb="FF92D050"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A1:E1</xm:f>
+ <xm:sqref>F1</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ </x14:sparklineGroups>
+ </ext>
+ </extLst>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_sparkline11.py b/xlsxwriter/test/worksheet/test_sparkline11.py
new file mode 100644
index 0000000..c483d6d
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_sparkline11.py
@@ -0,0 +1,208 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+ worksheet.name = 'Sheet1'
+ worksheet.excel_version = 2010
+
+ data = [-2, 2, 3, -1, 0]
+ worksheet.write_row('A1', data)
+ worksheet.write_row('A2', data)
+ worksheet.write_row('A3', data)
+ worksheet.write_row('A4', [1, 2, 3, 4, 5])
+
+ # Set up sparklines.
+ worksheet.add_sparkline('F1', {'range': 'A1:E1',
+ 'max': 0.5,
+ 'min': -0.5,
+ 'axis': True,
+ 'reverse': True,
+ 'empty_cells': 'zero',
+ 'weight': 0.25,
+ 'high_point': True,
+ 'low_point': True,
+ 'negative_points': True,
+ 'first_point': True,
+ 'last_point': True,
+ 'markers': True,
+ })
+
+ worksheet.add_sparkline('F2', {'range': 'A2:E2',
+ 'max': 'group',
+ 'min': 'group',
+ 'empty_cells': 'connect',
+ 'weight': 2.25,
+ })
+
+ worksheet.add_sparkline('F3', {'range': 'A3:E3',
+ 'max': 'group',
+ 'min': '0',
+ 'show_hidden': True,
+ 'weight': 6,
+ 'date_axis': 'A4:E4',
+ })
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
+ <dimension ref="A1:E4"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
+ <sheetData>
+ <row r="1" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A1">
+ <v>-2</v>
+ </c>
+ <c r="B1">
+ <v>2</v>
+ </c>
+ <c r="C1">
+ <v>3</v>
+ </c>
+ <c r="D1">
+ <v>-1</v>
+ </c>
+ <c r="E1">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="2" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A2">
+ <v>-2</v>
+ </c>
+ <c r="B2">
+ <v>2</v>
+ </c>
+ <c r="C2">
+ <v>3</v>
+ </c>
+ <c r="D2">
+ <v>-1</v>
+ </c>
+ <c r="E2">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="3" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A3">
+ <v>-2</v>
+ </c>
+ <c r="B3">
+ <v>2</v>
+ </c>
+ <c r="C3">
+ <v>3</v>
+ </c>
+ <c r="D3">
+ <v>-1</v>
+ </c>
+ <c r="E3">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="4" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A4">
+ <v>1</v>
+ </c>
+ <c r="B4">
+ <v>2</v>
+ </c>
+ <c r="C4">
+ <v>3</v>
+ </c>
+ <c r="D4">
+ <v>4</v>
+ </c>
+ <c r="E4">
+ <v>5</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ <extLst>
+ <ext xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">
+ <x14:sparklineGroups xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
+ <x14:sparklineGroup manualMin="0" lineWeight="6" dateAxis="1" displayEmptyCellsAs="gap" displayHidden="1" minAxisType="custom" maxAxisType="group">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <xm:f>Sheet1!A4:E4</xm:f>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A3:E3</xm:f>
+ <xm:sqref>F3</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup lineWeight="2.25" displayEmptyCellsAs="span" minAxisType="group" maxAxisType="group">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A2:E2</xm:f>
+ <xm:sqref>F2</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ <x14:sparklineGroup manualMax="0.5" manualMin="-0.5" lineWeight="0.25" markers="1" high="1" low="1" first="1" last="1" negative="1" displayXAxis="1" minAxisType="custom" maxAxisType="custom" rightToLeft="1">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A1:E1</xm:f>
+ <xm:sqref>F1</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ </x14:sparklineGroups>
+ </ext>
+ </extLst>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_sparkline12.py b/xlsxwriter/test/worksheet/test_sparkline12.py
new file mode 100644
index 0000000..983e599
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_sparkline12.py
@@ -0,0 +1,94 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.select()
+ worksheet.name = 'Sheet1'
+ worksheet.excel_version = 2010
+
+ data = [-2, 2, 3, -1, 0]
+ worksheet.write_row('A1', data)
+
+ # Set up sparklines.
+ worksheet.add_sparkline('F1', {'range': 'Sheet1!A1:E1',
+ 'max': 4, 'min': 0})
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
+ <dimension ref="A1:E1"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
+ <sheetData>
+ <row r="1" spans="1:5" x14ac:dyDescent="0.25">
+ <c r="A1">
+ <v>-2</v>
+ </c>
+ <c r="B1">
+ <v>2</v>
+ </c>
+ <c r="C1">
+ <v>3</v>
+ </c>
+ <c r="D1">
+ <v>-1</v>
+ </c>
+ <c r="E1">
+ <v>0</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ <extLst>
+ <ext xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">
+ <x14:sparklineGroups xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
+ <x14:sparklineGroup manualMax="4" manualMin="0" displayEmptyCellsAs="gap" minAxisType="custom" maxAxisType="custom">
+ <x14:colorSeries theme="4" tint="-0.499984740745262"/>
+ <x14:colorNegative theme="5"/>
+ <x14:colorAxis rgb="FF000000"/>
+ <x14:colorMarkers theme="4" tint="-0.499984740745262"/>
+ <x14:colorFirst theme="4" tint="0.39997558519241921"/>
+ <x14:colorLast theme="4" tint="0.39997558519241921"/>
+ <x14:colorHigh theme="4"/>
+ <x14:colorLow theme="4"/>
+ <x14:sparklines>
+ <x14:sparkline>
+ <xm:f>Sheet1!A1:E1</xm:f>
+ <xm:sqref>F1</xm:sqref>
+ </x14:sparkline>
+ </x14:sparklines>
+ </x14:sparklineGroup>
+ </x14:sparklineGroups>
+ </ext>
+ </extLst>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_worksheet01.py b/xlsxwriter/test/worksheet/test_worksheet01.py
new file mode 100644
index 0000000..44deb59
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_worksheet01.py
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with no cell data."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+
+ worksheet.select()
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData/>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_worksheet02.py b/xlsxwriter/test/worksheet/test_worksheet02.py
new file mode 100644
index 0000000..22c5434
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_worksheet02.py
@@ -0,0 +1,52 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with a number in a cell."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+
+ worksheet.select()
+ worksheet.write_number(2, 1, 123)
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="B3"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="3" spans="2:2">
+ <c r="B3">
+ <v>123</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_worksheet03.py b/xlsxwriter/test/worksheet/test_worksheet03.py
new file mode 100644
index 0000000..82cea42
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_worksheet03.py
@@ -0,0 +1,105 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+from ...format import Format
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with column formatting set."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ cell_format = Format({'xf_index': 1})
+
+ worksheet.set_column(1, 3, 5)
+ worksheet.set_column(5, 5, 8, None, {'hidden': True})
+ worksheet.set_column(7, 7, None, cell_format)
+ worksheet.set_column(9, 9, 2)
+ worksheet.set_column(11, 11, None, None, {'hidden': True})
+
+ worksheet.select()
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="F1:H1"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <cols>
+ <col min="2" max="4" width="5.7109375" customWidth="1"/>
+ <col min="6" max="6" width="8.7109375" hidden="1" customWidth="1"/>
+ <col min="8" max="8" width="9.140625" style="1"/>
+ <col min="10" max="10" width="2.7109375" customWidth="1"/>
+ <col min="12" max="12" width="0" hidden="1" customWidth="1"/>
+ </cols>
+ <sheetData/>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
+
+ def test_assemble_xml_file_A1(self):
+ """
+ Test writing a worksheet with column formatting set using
+ A1 Notation.
+ """
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ cell_format = Format({'xf_index': 1})
+
+ worksheet.set_column('B:D', 5)
+ worksheet.set_column('F:F', 8, None, {'hidden': True})
+ worksheet.set_column('H:H', None, cell_format)
+ worksheet.set_column('J:J', 2)
+ worksheet.set_column('L:L', None, None, {'hidden': True})
+
+ worksheet.select()
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="F1:H1"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <cols>
+ <col min="2" max="4" width="5.7109375" customWidth="1"/>
+ <col min="6" max="6" width="8.7109375" hidden="1" customWidth="1"/>
+ <col min="8" max="8" width="9.140625" style="1"/>
+ <col min="10" max="10" width="2.7109375" customWidth="1"/>
+ <col min="12" max="12" width="0" hidden="1" customWidth="1"/>
+ </cols>
+ <sheetData/>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_worksheet04.py b/xlsxwriter/test/worksheet/test_worksheet04.py
new file mode 100644
index 0000000..6d3b9a7
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_worksheet04.py
@@ -0,0 +1,61 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+from ...format import Format
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with row formatting set."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ cell_format = Format({'xf_index': 1})
+
+ worksheet.set_row(1, 30)
+ worksheet.set_row(3, None, None, {'hidden': 1})
+ worksheet.set_row(6, None, cell_format)
+ worksheet.set_row(9, 3)
+ worksheet.set_row(12, 24, None, {'hidden': 1})
+ worksheet.set_row(14, 0)
+
+ worksheet.select()
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A2:A15"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="2" ht="30" customHeight="1"/>
+ <row r="4" hidden="1"/>
+ <row r="7" s="1" customFormat="1"/>
+ <row r="10" ht="3" customHeight="1"/>
+ <row r="13" ht="24" hidden="1" customHeight="1"/>
+ <row r="15" hidden="1"/>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_worksheet05.py b/xlsxwriter/test/worksheet/test_worksheet05.py
new file mode 100644
index 0000000..c4b84d8
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_worksheet05.py
@@ -0,0 +1,67 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with strings in cells."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.str_table = SharedStringTable()
+
+ worksheet.select()
+
+ # Write some strings.
+ worksheet.write_string(0, 0, 'Foo')
+ worksheet.write_string(2, 0, 'Bar')
+ worksheet.write_string(2, 3, 'Baz')
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:D3"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:4">
+ <c r="A1" t="s">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="3" spans="1:4">
+ <c r="A3" t="s">
+ <v>1</v>
+ </c>
+ <c r="D3" t="s">
+ <v>2</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_worksheet06.py b/xlsxwriter/test/worksheet/test_worksheet06.py
new file mode 100644
index 0000000..bcf24e9
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_worksheet06.py
@@ -0,0 +1,138 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with data out of bounds."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.str_table = SharedStringTable()
+ worksheet.select()
+
+ max_row = 1048576
+ max_col = 16384
+ bound_error = -1
+
+ # Test some out of bound values.
+ got = worksheet.write_string(max_row, 0, 'Foo')
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_string(0, max_col, 'Foo')
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_string(max_row, max_col, 'Foo')
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_number(max_row, 0, 123)
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_number(0, max_col, 123)
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_number(max_row, max_col, 123)
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_blank(max_row, 0, None, 'format')
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_blank(0, max_col, None, 'format')
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_blank(max_row, max_col, None, 'format')
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_formula(max_row, 0, '=A1')
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_formula(0, max_col, '=A1')
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_formula(max_row, max_col, '=A1')
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_array_formula(0, 0, 0, max_col, '=A1')
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_array_formula(0, 0, max_row, 0, '=A1')
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_array_formula(0, max_col, 0, 0, '=A1')
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_array_formula(max_row, 0, 0, 0, '=A1')
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.write_array_formula(max_row, max_col, max_row, max_col, '=A1')
+ self.assertEqual(got, bound_error)
+
+ # Column out of bounds.
+ got = worksheet.set_column(6, max_col, 17)
+ self.assertEqual(got, bound_error)
+
+ got = worksheet.set_column(max_col, 6, 17)
+ self.assertEqual(got, bound_error)
+
+ # Row out of bounds.
+ worksheet.set_row(max_row, 30)
+
+ # Reverse man and min column numbers
+ worksheet.set_column(0, 3, 17)
+
+ # Write some valid strings.
+ worksheet.write_string(0, 0, 'Foo')
+ worksheet.write_string(2, 0, 'Bar')
+ worksheet.write_string(2, 3, 'Baz')
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:D3"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <cols>
+ <col min="1" max="4" width="17.7109375" customWidth="1"/>
+ </cols>
+ <sheetData>
+ <row r="1" spans="1:4">
+ <c r="A1" t="s">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="3" spans="1:4">
+ <c r="A3" t="s">
+ <v>1</v>
+ </c>
+ <c r="D3" t="s">
+ <v>2</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_worksheet07.py b/xlsxwriter/test/worksheet/test_worksheet07.py
new file mode 100644
index 0000000..823689d
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_worksheet07.py
@@ -0,0 +1,76 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with formulas in cells."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.str_table = SharedStringTable()
+ worksheet.select()
+
+ # Write some data and formulas.
+ worksheet.write_number(0, 0, 1)
+ worksheet.write_number(1, 0, 2)
+ worksheet.write_formula(2, 2, '=A1+A2', None, 3)
+ worksheet.write_formula(4, 1, """="<&>" & ";"" '\"""", None, """<&>;" '""")
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:C5"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:3">
+ <c r="A1">
+ <v>1</v>
+ </c>
+ </row>
+ <row r="2" spans="1:3">
+ <c r="A2">
+ <v>2</v>
+ </c>
+ </row>
+ <row r="3" spans="1:3">
+ <c r="C3">
+ <f>A1+A2</f>
+ <v>3</v>
+ </c>
+ </row>
+ <row r="5" spans="1:3">
+ <c r="B5" t="str">
+ <f>"<&>" & ";"" '"</f>
+ <v><&>;" '</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_worksheet08.py b/xlsxwriter/test/worksheet/test_worksheet08.py
new file mode 100644
index 0000000..9a63405
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_worksheet08.py
@@ -0,0 +1,91 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+from ...sharedstrings import SharedStringTable
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with an array formulas in cells."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ worksheet.str_table = SharedStringTable()
+ worksheet.select()
+
+ # Write some data and formulas.
+ worksheet.write_array_formula(0, 0, 2, 0, '{=SUM(B1:C1*B2:C2)}')
+ worksheet.write_number(0, 1, 0)
+ worksheet.write_number(1, 1, 0)
+ worksheet.write_number(2, 1, 0)
+ worksheet.write_number(0, 2, 0)
+ worksheet.write_number(1, 2, 0)
+ worksheet.write_number(2, 2, 0)
+
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="A1:C3"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="1" spans="1:3">
+ <c r="A1">
+ <f t="array" ref="A1:A3">SUM(B1:C1*B2:C2)</f>
+ <v>0</v>
+ </c>
+ <c r="B1">
+ <v>0</v>
+ </c>
+ <c r="C1">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="2" spans="1:3">
+ <c r="A2">
+ <v>0</v>
+ </c>
+ <c r="B2">
+ <v>0</v>
+ </c>
+ <c r="C2">
+ <v>0</v>
+ </c>
+ </row>
+ <row r="3" spans="1:3">
+ <c r="A3">
+ <v>0</v>
+ </c>
+ <c r="B3">
+ <v>0</v>
+ </c>
+ <c r="C3">
+ <v>0</v>
+ </c>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_worksheet09.py b/xlsxwriter/test/worksheet/test_worksheet09.py
new file mode 100644
index 0000000..7cc9c11
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_worksheet09.py
@@ -0,0 +1,132 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ..helperfunctions import _xml_to_list
+from ...worksheet import Worksheet
+from ...format import Format
+
+
+class TestAssembleWorksheet(unittest.TestCase):
+ """
+ Test assembling a complete Worksheet file.
+
+ """
+ def test_assemble_xml_file(self):
+ """Test writing a worksheet with a blank cell."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ cell_format = Format({'xf_index': 1})
+
+ # No format. Should be ignored.
+ worksheet.write_blank(0, 0, None)
+
+ worksheet.write_blank(1, 2, None, cell_format)
+
+ worksheet.select()
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="C2"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="2" spans="3:3">
+ <c r="C2" s="1"/>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
+
+ def test_assemble_xml_file_write(self):
+ """Test writing a worksheet with a blank cell with write() method."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ cell_format = Format({'xf_index': 1})
+
+ # No format. Should be ignored.
+ worksheet.write(0, 0, None)
+
+ worksheet.write(1, 2, None, cell_format)
+
+ worksheet.select()
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="C2"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="2" spans="3:3">
+ <c r="C2" s="1"/>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
+
+ def test_assemble_xml_file_A1(self):
+ """Test writing a worksheet with a blank cell with A1 notation."""
+ self.maxDiff = None
+
+ fh = StringIO()
+ worksheet = Worksheet()
+ worksheet._set_filehandle(fh)
+ cell_format = Format({'xf_index': 1})
+
+ # No format. Should be ignored.
+ worksheet.write_blank('A1', None)
+
+ worksheet.write_blank('C2', None, cell_format)
+
+ worksheet.select()
+ worksheet._assemble_xml_file()
+
+ exp = _xml_to_list("""
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+ <dimension ref="C2"/>
+ <sheetViews>
+ <sheetView tabSelected="1" workbookViewId="0"/>
+ </sheetViews>
+ <sheetFormatPr defaultRowHeight="15"/>
+ <sheetData>
+ <row r="2" spans="3:3">
+ <c r="C2" s="1"/>
+ </row>
+ </sheetData>
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
+ </worksheet>
+ """)
+
+ got = _xml_to_list(fh.getvalue())
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_auto_filter.py b/xlsxwriter/test/worksheet/test_write_auto_filter.py
new file mode 100644
index 0000000..798033b
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_auto_filter.py
@@ -0,0 +1,294 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteAutoFilter(unittest.TestCase):
+ """
+ Test the Worksheet _write_auto_filter() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+ self.worksheet.name = 'Sheet1'
+ self.worksheet.autofilter('A1:D51')
+
+ def test_write_auto_filter_1(self):
+ """Test the _write_auto_filter() method"""
+
+ self.worksheet._write_auto_filter()
+
+ exp = """<autoFilter ref="A1:D51"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_2(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x == East'
+ exp = """<autoFilter ref="A1:D51"><filterColumn colId="0"><filters><filter val="East"/></filters></filterColumn></autoFilter>"""
+
+ self.worksheet.filter_column(0, filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_3(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x == East or x == North'
+ exp = """<autoFilter ref="A1:D51"><filterColumn colId="0"><filters><filter val="East"/><filter val="North"/></filters></filterColumn></autoFilter>"""
+
+ self.worksheet.filter_column('A', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_4(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x == East and x == North'
+ exp = """<autoFilter ref="A1:D51"><filterColumn colId="0"><customFilters and="1"><customFilter val="East"/><customFilter val="North"/></customFilters></filterColumn></autoFilter>"""
+
+ self.worksheet.filter_column('A', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_5(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x != East'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="0"><customFilters><customFilter operator="notEqual" val="East"/></customFilters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column('A', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_6(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x == S*'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="0"><customFilters><customFilter val="S*"/></customFilters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column('A', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_7(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x != S*'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="0"><customFilters><customFilter operator="notEqual" val="S*"/></customFilters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column('A', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_8(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x == *h'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="0"><customFilters><customFilter val="*h"/></customFilters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column('A', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_9(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x != *h'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="0"><customFilters><customFilter operator="notEqual" val="*h"/></customFilters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column('A', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_10(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x =~ *o*'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="0"><customFilters><customFilter val="*o*"/></customFilters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column('A', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_11(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x !~ *r*'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="0"><customFilters><customFilter operator="notEqual" val="*r*"/></customFilters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column('A', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_12(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x == 1000'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="2"><filters><filter val="1000"/></filters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column(2, filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_13(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x != 2000'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="2"><customFilters><customFilter operator="notEqual" val="2000"/></customFilters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column('C', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_14(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x > 3000'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="2"><customFilters><customFilter operator="greaterThan" val="3000"/></customFilters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column('C', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_15(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x >= 4000'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="2"><customFilters><customFilter operator="greaterThanOrEqual" val="4000"/></customFilters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column('C', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_16(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x < 5000'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="2"><customFilters><customFilter operator="lessThan" val="5000"/></customFilters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column('C', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_17(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x <= 6000'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="2"><customFilters><customFilter operator="lessThanOrEqual" val="6000"/></customFilters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column('C', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_18(self):
+ """Test the _write_auto_filter() method"""
+
+ filter_condition = 'x >= 1000 and x <= 2000'
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="2"><customFilters and="1"><customFilter operator="greaterThanOrEqual" val="1000"/><customFilter operator="lessThanOrEqual" val="2000"/></customFilters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column('C', filter_condition)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_19(self):
+ """Test the _write_auto_filter() method"""
+
+ matches = ['East']
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="0"><filters><filter val="East"/></filters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column_list('A', matches)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_20(self):
+ """Test the _write_auto_filter() method"""
+
+ matches = ['East', 'North']
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="0"><filters><filter val="East"/><filter val="North"/></filters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column_list('A', matches)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_auto_filter_21(self):
+ """Test the _write_auto_filter() method"""
+
+ matches = ['February', 'January', 'July', 'June']
+ exp = '<autoFilter ref="A1:D51"><filterColumn colId="3"><filters><filter val="February"/><filter val="January"/><filter val="July"/><filter val="June"/></filters></filterColumn></autoFilter>'
+
+ self.worksheet.filter_column_list(3, matches)
+ self.worksheet._write_auto_filter()
+
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_cell.py b/xlsxwriter/test/worksheet/test_write_cell.py
new file mode 100644
index 0000000..f44ff68
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_cell.py
@@ -0,0 +1,75 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...compat_collections import namedtuple
+from ...worksheet import Worksheet
+
+
+class TestWriteCell(unittest.TestCase):
+ """
+ Test the Worksheet _write_cell() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_cell_number(self):
+ """Test the _write_cell() method for numbers."""
+
+ cell_tuple = namedtuple('Number', 'number, format')
+ cell = cell_tuple(1, None)
+
+ self.worksheet._write_cell(0, 0, cell)
+
+ exp = """<c r="A1"><v>1</v></c>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_cell_string(self):
+ """Test the _write_cell() method for strings."""
+
+ cell_tuple = namedtuple('String', 'string, format')
+ cell = cell_tuple(0, None)
+
+ self.worksheet._write_cell(3, 1, cell)
+
+ exp = """<c r="B4" t="s"><v>0</v></c>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_cell_formula01(self):
+ """Test the _write_cell() method for formulas."""
+
+ cell_tuple = namedtuple('Formula', 'formula, format, value')
+ cell = cell_tuple('A3+A5', None, 0)
+
+ self.worksheet._write_cell(1, 2, cell)
+
+ exp = """<c r="C2"><f>A3+A5</f><v>0</v></c>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_cell_formula02(self):
+ """Test the _write_cell() method for formulas."""
+
+ cell_tuple = namedtuple('Formula', 'formula, format, value')
+ cell = cell_tuple('A3+A5', None, 7)
+
+ self.worksheet._write_cell(1, 2, cell)
+
+ exp = """<c r="C2"><f>A3+A5</f><v>7</v></c>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_col_breaks.py b/xlsxwriter/test/worksheet/test_write_col_breaks.py
new file mode 100644
index 0000000..e829fba
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_col_breaks.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteColBreaks(unittest.TestCase):
+ """
+ Test the Worksheet _write_col_breaks() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_col_breaks_1(self):
+ """Test the _write_col_breaks() method"""
+
+ self.worksheet.vbreaks = [1]
+
+ self.worksheet._write_col_breaks()
+
+ exp = """<colBreaks count="1" manualBreakCount="1"><brk id="1" max="1048575" man="1"/></colBreaks>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_col_breaks_2(self):
+ """Test the _write_col_breaks() method"""
+
+ self.worksheet.vbreaks = [8, 3, 1, 0]
+
+ self.worksheet._write_col_breaks()
+
+ exp = """<colBreaks count="3" manualBreakCount="3"><brk id="1" max="1048575" man="1"/><brk id="3" max="1048575" man="1"/><brk id="8" max="1048575" man="1"/></colBreaks>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_custom_filter.py b/xlsxwriter/test/worksheet/test_write_custom_filter.py
new file mode 100644
index 0000000..3cdc679
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_custom_filter.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteCustomFilter(unittest.TestCase):
+ """
+ Test the Worksheet _write_custom_filter() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_custom_filter(self):
+ """Test the _write_custom_filter() method"""
+
+ self.worksheet._write_custom_filter(4, 3000)
+
+ exp = """<customFilter operator="greaterThan" val="3000"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_custom_filters.py b/xlsxwriter/test/worksheet/test_write_custom_filters.py
new file mode 100644
index 0000000..f604c65
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_custom_filters.py
@@ -0,0 +1,42 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteCustomFilters(unittest.TestCase):
+ """
+ Test the Worksheet _write_custom_filters() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_custom_filters_1(self):
+ """Test the _write_custom_filters() method"""
+
+ self.worksheet._write_custom_filters([4, 4000])
+
+ exp = """<customFilters><customFilter operator="greaterThan" val="4000"/></customFilters>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_custom_filters_2(self):
+ """Test the _write_custom_filters() method"""
+
+ self.worksheet._write_custom_filters([4, 3000, 0, 1, 8000])
+
+ exp = """<customFilters and="1"><customFilter operator="greaterThan" val="3000"/><customFilter operator="lessThan" val="8000"/></customFilters>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_data_validations01.py b/xlsxwriter/test/worksheet/test_write_data_validations01.py
new file mode 100644
index 0000000..12ee8ad
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_data_validations01.py
@@ -0,0 +1,174 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from datetime import date
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+from ..helperfunctions import _xml_to_list
+
+
+class TestWriteDataValidations(unittest.TestCase):
+ """
+ Test the Worksheet _write_data_validations() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_data_validations_1(self):
+ """Test the _write_data_validations() method. Data validation example 1 from docs"""
+
+ self.worksheet.data_validation('A1', {'validate': 'integer',
+ 'criteria': '>',
+ 'value': 0,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = """<dataValidations count="1"><dataValidation type="whole" operator="greaterThan" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="A1"><formula1>0</formula1></dataValidation></dataValidations>"""
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_1b(self):
+ """Test the _write_data_validations() method. Data validation example 1 from docs (with options turned off)"""
+
+ self.worksheet.data_validation('A1', {'validate': 'integer',
+ 'criteria': '>',
+ 'value': 0,
+ 'ignore_blank': 0,
+ 'show_input': 0,
+ 'show_error': 0,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = """<dataValidations count="1"><dataValidation type="whole" operator="greaterThan" sqref="A1"><formula1>0</formula1></dataValidation></dataValidations>"""
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_2(self):
+ """Test the _write_data_validations() method. Data validation example 2 from docs"""
+
+ self.worksheet.data_validation('A2', {'validate': 'integer',
+ 'criteria': '>',
+ 'value': '=E3',
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = """<dataValidations count="1"><dataValidation type="whole" operator="greaterThan" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="A2"><formula1>E3</formula1></dataValidation></dataValidations>"""
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_3(self):
+ """Test the _write_data_validations() method. Data validation example 3 from docs"""
+
+ self.worksheet.data_validation('A3', {'validate': 'decimal',
+ 'criteria': 'between',
+ 'minimum': 0.1,
+ 'maximum': 0.5,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = """<dataValidations count="1"><dataValidation type="decimal" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="A3"><formula1>0.1</formula1><formula2>0.5</formula2></dataValidation></dataValidations>"""
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_4(self):
+ """Test the _write_data_validations() method. Data validation example 4 from docs"""
+
+ self.worksheet.data_validation('A4', {'validate': 'list',
+ 'source': ['open', 'high', 'close'],
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = """<dataValidations count="1"><dataValidation type="list" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="A4"><formula1>"open,high,close"</formula1></dataValidation></dataValidations>"""
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_5(self):
+ """Test the _write_data_validations() method. Data validation example 5 from docs"""
+
+ self.worksheet.data_validation('A5', {'validate': 'list',
+ 'source': '=$E$4:$G$4',
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = """<dataValidations count="1"><dataValidation type="list" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="A5"><formula1>$E$4:$G$4</formula1></dataValidation></dataValidations>"""
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_6(self):
+ """Test the _write_data_validations() method. Data validation example 6 from docs"""
+
+ self.worksheet.data_validation('A6', {'validate': 'date',
+ 'criteria': 'between',
+ 'minimum': date(2008, 1, 1),
+ 'maximum': date(2008, 12, 12),
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = """<dataValidations count="1"><dataValidation type="date" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="A6"><formula1>39448</formula1><formula2>39794</formula2></dataValidation></dataValidations>"""
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_7(self):
+ """Test the _write_data_validations() method. Data validation example 7 from docs"""
+
+ self.worksheet.data_validation('A7', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 100,
+ 'input_title': 'Enter an integer:',
+ 'input_message': 'between 1 and 100',
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = """<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showInputMessage="1" showErrorMessage="1" promptTitle="Enter an integer:" prompt="between 1 and 100" sqref="A7"><formula1>1</formula1><formula2>100</formula2></dataValidation></dataValidations>"""
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_data_validations02.py b/xlsxwriter/test/worksheet/test_write_data_validations02.py
new file mode 100644
index 0000000..58d46e4
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_data_validations02.py
@@ -0,0 +1,944 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from datetime import date, time
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+from ..helperfunctions import _xml_to_list
+
+
+class TestWriteDataValidations(unittest.TestCase):
+ """
+ Test the Worksheet _write_data_validations() method.
+
+ """
+
+ def setUp(self):
+ self.maxDiff = None
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_data_validations_1(self):
+ """
+ Test 1 Integer between 1 and 10.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_2(self):
+ """
+ Test 2 Integer not between 1 and 10.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'not between',
+ 'minimum': 1,
+ 'maximum': 10,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="notBetween" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_3(self):
+ """
+ Test 3,4,5 Integer == 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'equal to',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="equal" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_4(self):
+ """
+ Test 3,4,5 Integer == 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': '=',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="equal" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_5(self):
+ """
+ Test 3,4,5 Integer == 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': '==',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="equal" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_6(self):
+ """
+ Test 6,7,8 Integer != 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'not equal to',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="notEqual" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_7(self):
+ """
+ Test 6,7,8 Integer != 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': '<>',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="notEqual" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_8(self):
+ """
+ Test 6,7,8 Integer != 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': '!=',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="notEqual" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_9(self):
+ """
+ Test 9,10 Integer > 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'greater than',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="greaterThan" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_10(self):
+ """
+ Test 9,10 Integer > 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': '>',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="greaterThan" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_11(self):
+ """
+ Test 11,12 Integer < 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'less than',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="lessThan" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_12(self):
+ """
+ Test 11,12 Integer < 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': '<',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="lessThan" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_13(self):
+ """
+ Test 13,14 Integer >= 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'greater than or equal to',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="greaterThanOrEqual" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_14(self):
+ """
+ Test 13,14 Integer >= 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': '>=',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="greaterThanOrEqual" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_15(self):
+ """
+ Test 15,16 Integer <= 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'less than or equal to',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="lessThanOrEqual" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_16(self):
+ """
+ Test 15,16 Integer <= 1.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': '<=',
+ 'value': 1,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" operator="lessThanOrEqual" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_17(self):
+ """
+ Test 17 Integer between 1 and 10 (same as test 1) + Ignore blank off.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ 'ignore_blank': 0,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_18(self):
+ """
+ Test 18 Integer between 1 and 10 (same as test 1) + Error style == warning.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ 'error_type': 'warning',
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" errorStyle="warning" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_19(self):
+ """
+ Test 19 Integer between 1 and 10 (same as test 1) + Error style == info.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ 'error_type': 'information',
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" errorStyle="information" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_20(self):
+ """
+ Test 20 Integer between 1 and 10 (same as test 1)
+ + input title.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ 'input_title': 'Input title January',
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showInputMessage="1" showErrorMessage="1" promptTitle="Input title January" sqref="B5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_21(self):
+ """
+ Test 21 Integer between 1 and 10 (same as test 1)
+ + input title.
+ + input message.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ 'input_title': 'Input title January',
+ 'input_message': 'Input message February',
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showInputMessage="1" showErrorMessage="1" promptTitle="Input title January" prompt="Input message February" sqref="B5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_22(self):
+ """
+ Test 22 Integer between 1 and 10 (same as test 1)
+ + input title.
+ + input message.
+ + error title.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ 'input_title': 'Input title January',
+ 'input_message': 'Input message February',
+ 'error_title': 'Error title March',
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showInputMessage="1" showErrorMessage="1" errorTitle="Error title March" promptTitle="Input title January" prompt="Input message February" sqref="B5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_23(self):
+ """
+ Test 23 Integer between 1 and 10 (same as test 1)
+ + input title.
+ + input message.
+ + error title.
+ + error message.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ 'input_title': 'Input title January',
+ 'input_message': 'Input message February',
+ 'error_title': 'Error title March',
+ 'error_message': 'Error message April',
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showInputMessage="1" showErrorMessage="1" errorTitle="Error title March" error="Error message April" promptTitle="Input title January" prompt="Input message February" sqref="B5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_24(self):
+ """
+ Test 24 Integer between 1 and 10 (same as test 1)
+ + input title.
+ + input message.
+ + error title.
+ + error message.
+ - input message box.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ 'input_title': 'Input title January',
+ 'input_message': 'Input message February',
+ 'error_title': 'Error title March',
+ 'error_message': 'Error message April',
+ 'show_input': 0,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showErrorMessage="1" errorTitle="Error title March" error="Error message April" promptTitle="Input title January" prompt="Input message February" sqref="B5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_25(self):
+ """
+ Test 25 Integer between 1 and 10 (same as test 1)
+ + input title.
+ + input message.
+ + error title.
+ + error message.
+ - input message box.
+ - error message box.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ 'input_title': 'Input title January',
+ 'input_message': 'Input message February',
+ 'error_title': 'Error title March',
+ 'error_message': 'Error message April',
+ 'show_input': 0,
+ 'show_error': 0,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" errorTitle="Error title March" error="Error message April" promptTitle="Input title January" prompt="Input message February" sqref="B5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_26(self):
+ """
+ Test 26 'Any' shouldn't produce a DV record if there are no messages.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'any'})
+
+ self.worksheet._write_data_validations()
+
+ exp = ''
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_27(self):
+ """
+ Test 27 Decimal = 1.2345
+ """
+ self.worksheet.data_validation('B5', {'validate': 'decimal',
+ 'criteria': '==',
+ 'value': 1.2345,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="decimal" operator="equal" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1.2345</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_28(self):
+ """
+ Test 28 List = a,bb,ccc
+ """
+ self.worksheet.data_validation('B5', {'validate': 'list',
+ 'source': ['a', 'bb', 'ccc'],
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="list" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>"a,bb,ccc"</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_29(self):
+ """
+ Test 29 List = a,bb,ccc, No dropdown
+ """
+ self.worksheet.data_validation('B5', {'validate': 'list',
+ 'source': ['a', 'bb', 'ccc'],
+ 'dropdown': 0,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="list" allowBlank="1" showDropDown="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>"a,bb,ccc"</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_30(self):
+ """
+ Test 30 List = $D$1:$D$5
+ """
+ self.worksheet.data_validation('A1:A1', {'validate': 'list',
+ 'source': '=$D$1:$D$5',
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="list" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="A1"><formula1>$D$1:$D$5</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_31(self):
+ """
+ Test 31 Date = 39653 (2008-07-24)
+ """
+ self.worksheet.data_validation('B5', {'validate': 'date',
+ 'criteria': '==',
+ 'value': date(2008, 7, 24),
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="date" operator="equal" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>39653</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_32(self):
+ """
+ Test 32 Date = 2008-07-25T
+ """
+ self.worksheet.data_validation('B5', {'validate': 'date',
+ 'criteria': '==',
+ 'value': date(2008, 7, 25),
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="date" operator="equal" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>39654</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_33(self):
+ """
+ Test 33 Date between ranges.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'date',
+ 'criteria': 'between',
+ 'minimum': date(2008, 1, 1),
+ 'maximum': date(2008, 12, 12),
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="date" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>39448</formula1><formula2>39794</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_34(self):
+ """
+ Test 34 Time = 0.5 (12:00:00)
+ """
+ self.worksheet.data_validation('B5:B5', {'validate': 'time',
+ 'criteria': '==',
+ 'value': time(12),
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="time" operator="equal" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>0.5</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_35(self):
+ """
+ Test 35 Time = T12:00:00
+ """
+ self.worksheet.data_validation('B5', {'validate': 'time',
+ 'criteria': '==',
+ 'value': time(12, 0, 0),
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="time" operator="equal" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>0.5</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_36(self):
+ """
+ Test 36 Custom == 10.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'custom',
+ 'criteria': '==',
+ 'value': 10,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="custom" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>10</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_37(self):
+ """
+ Test 37 Check the row/col processing: single A1 style cell.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_38(self):
+ """
+ Test 38 Check the row/col processing: single A1 style range.
+ """
+ self.worksheet.data_validation('B5:B10', {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5:B10"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_39(self):
+ """
+ Test 39 Check the row/col processing: single (row, col) style cell.
+ """
+ self.worksheet.data_validation(4, 1, 4, 1, {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_40(self):
+ """
+ Test 40 Check the row/col processing: single (row, col) style range.
+ """
+ self.worksheet.data_validation(4, 1, 9, 1, {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5:B10"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_41(self):
+ """
+ Test 41 Check the row/col processing: multiple (row, col) style cells.
+ """
+ self.worksheet.data_validation(4, 1, 4, 1, {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ 'other_cells': [[4, 3, 4, 3]],
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5 D5"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_42(self):
+ """
+ Test 42 Check the row/col processing: multiple (row, col) style cells.
+ """
+ self.worksheet.data_validation(4, 1, 4, 1, {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ 'other_cells': [[6, 1, 6, 1], [8, 1, 8, 1]],
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5 B7 B9"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_43(self):
+ """
+ Test 43 Check the row/col processing: multiple (row, col) style cells.
+ """
+ self.worksheet.data_validation(4, 1, 8, 1, {'validate': 'integer',
+ 'criteria': 'between',
+ 'minimum': 1,
+ 'maximum': 10,
+ 'other_cells': [[3, 3, 3, 3]],
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation type="whole" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5:B9 D4"><formula1>1</formula1><formula2>10</formula2></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_44(self):
+ """
+ Test 44 Multiple validations.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'integer',
+ 'criteria': '>',
+ 'value': 10,
+ })
+
+ self.worksheet.data_validation('C10', {'validate': 'integer',
+ 'criteria': '<',
+ 'value': 10,
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="2"><dataValidation type="whole" operator="greaterThan" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="B5"><formula1>10</formula1></dataValidation><dataValidation type="whole" operator="lessThan" allowBlank="1" showInputMessage="1" showErrorMessage="1" sqref="C10"><formula1>10</formula1></dataValidation></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
+
+ def test_write_data_validations_21(self):
+ """
+ Test 45 Test 'any' with input messages.
+ """
+ self.worksheet.data_validation('B5', {'validate': 'any',
+ 'input_title': 'Input title January',
+ 'input_message': 'Input message February',
+ })
+
+ self.worksheet._write_data_validations()
+
+ exp = '<dataValidations count="1"><dataValidation allowBlank="1" showInputMessage="1" showErrorMessage="1" promptTitle="Input title January" prompt="Input message February" sqref="B5"/></dataValidations>'
+ got = self.fh.getvalue()
+
+ exp = _xml_to_list(exp)
+ got = _xml_to_list(got)
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_dimension.py b/xlsxwriter/test/worksheet/test_write_dimension.py
new file mode 100644
index 0000000..272da00
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_dimension.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteDimension(unittest.TestCase):
+ """
+ Test the Worksheet _write_dimension() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_dimension(self):
+ """Test the _write_dimension() method"""
+
+ self.worksheet._write_dimension()
+
+ exp = """<dimension ref="A1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_filter.py b/xlsxwriter/test/worksheet/test_write_filter.py
new file mode 100644
index 0000000..b8e3da2
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_filter.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteFilter(unittest.TestCase):
+ """
+ Test the Worksheet _write_filter() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_filter(self):
+ """Test the _write_filter() method"""
+
+ self.worksheet._write_filter('East')
+
+ exp = """<filter val="East"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_filter_column.py b/xlsxwriter/test/worksheet/test_write_filter_column.py
new file mode 100644
index 0000000..9450560
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_filter_column.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteFilterColumn(unittest.TestCase):
+ """
+ Test the Worksheet _write_filter_column() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_filter_column(self):
+ """Test the _write_filter_column() method"""
+
+ self.worksheet._write_filter_column(0, 1, ['East'])
+
+ exp = """<filterColumn colId="0"><filters><filter val="East"/></filters></filterColumn>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_filters.py b/xlsxwriter/test/worksheet/test_write_filters.py
new file mode 100644
index 0000000..5b6255e
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_filters.py
@@ -0,0 +1,52 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteFilters(unittest.TestCase):
+ """
+ Test the Worksheet _write_filters() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_filters_1(self):
+ """Test the _write_filters() method"""
+
+ self.worksheet._write_filters(['East'])
+
+ exp = """<filters><filter val="East"/></filters>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_filters_2(self):
+ """Test the _write_filters() method"""
+
+ self.worksheet._write_filters(['East', 'South'])
+
+ exp = """<filters><filter val="East"/><filter val="South"/></filters>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_filters_3(self):
+ """Test the _write_filters() method"""
+
+ self.worksheet._write_filters(['blanks'])
+
+ exp = """<filters blank="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_header_footer.py b/xlsxwriter/test/worksheet/test_write_header_footer.py
new file mode 100644
index 0000000..f78bead
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_header_footer.py
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteHeaderFooter(unittest.TestCase):
+ """
+ Test the Worksheet _write_header_footer() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_header_footer_header_only(self):
+ """Test the _write_header_footer() method header only"""
+
+ self.worksheet.set_header('Page &P of &N')
+ self.worksheet._write_header_footer()
+
+ exp = """<headerFooter><oddHeader>Page &P of &N</oddHeader></headerFooter>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_header_footer_footer_only(self):
+ """Test the _write_header_footer() method footer only"""
+
+ self.worksheet.set_footer('&F')
+ self.worksheet._write_header_footer()
+
+ exp = """<headerFooter><oddFooter>&F</oddFooter></headerFooter>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_header_footer_both(self):
+ """Test the _write_header_footer() method header and footer"""
+
+ self.worksheet.set_header('Page &P of &N')
+ self.worksheet.set_footer('&F')
+ self.worksheet._write_header_footer()
+
+ exp = """<headerFooter><oddHeader>Page &P of &N</oddHeader><oddFooter>&F</oddFooter></headerFooter>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_hyperlink.py b/xlsxwriter/test/worksheet/test_write_hyperlink.py
new file mode 100644
index 0000000..bee8195
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_hyperlink.py
@@ -0,0 +1,62 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteHyperlink(unittest.TestCase):
+ """
+ Test the Worksheet _write_hyperlink() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_hyperlink_external(self):
+ """Test the _write_hyperlink() method"""
+
+ self.worksheet._write_hyperlink_external(0, 0, 1)
+
+ exp = """<hyperlink ref="A1" r:id="rId1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_hyperlink_internal_1(self):
+ """Test the _write_hyperlink() method"""
+
+ self.worksheet._write_hyperlink_internal(0, 0, 'Sheet2!A1', 'Sheet2!A1')
+
+ exp = """<hyperlink ref="A1" location="Sheet2!A1" display="Sheet2!A1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_hyperlink_internal_2(self):
+ """Test the _write_hyperlink() method"""
+
+ self.worksheet._write_hyperlink_internal(4, 0, "'Data Sheet'!D5", "'Data Sheet'!D5")
+
+ exp = """<hyperlink ref="A5" location="'Data Sheet'!D5" display="'Data Sheet'!D5"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_hyperlink_internal_3(self):
+ """Test the _write_hyperlink() method"""
+
+ self.worksheet._write_hyperlink_internal(17, 0, 'Sheet2!A1', 'Sheet2!A1', 'Screen Tip 1')
+
+ exp = """<hyperlink ref="A18" location="Sheet2!A1" tooltip="Screen Tip 1" display="Sheet2!A1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_merge_cell.py b/xlsxwriter/test/worksheet/test_write_merge_cell.py
new file mode 100644
index 0000000..6dddb56
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_merge_cell.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteMergeCell(unittest.TestCase):
+ """
+ Test the Worksheet _write_merge_cell() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_merge_cell(self):
+ """Test the _write_merge_cell() method"""
+
+ self.worksheet._write_merge_cell([2, 1, 2, 2])
+
+ exp = """<mergeCell ref="B3:C3"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_merge_cells.py b/xlsxwriter/test/worksheet/test_write_merge_cells.py
new file mode 100644
index 0000000..6386c98
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_merge_cells.py
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+from ...format import Format
+from ...sharedstrings import SharedStringTable
+
+
+class TestWriteMergeCells(unittest.TestCase):
+ """
+ Test the Worksheet _write_merge_cells() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+ self.worksheet.str_table = SharedStringTable()
+
+ def test_write_merge_cells_1(self):
+ """Test the _write_merge_cells() method"""
+
+ cell_format = Format()
+
+ self.worksheet.merge_range(2, 1, 2, 2, 'Foo', cell_format)
+ self.worksheet._write_merge_cells()
+
+ exp = """<mergeCells count="1"><mergeCell ref="B3:C3"/></mergeCells>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_merge_cells_2(self):
+ """Test the _write_merge_cells() method"""
+
+ cell_format = Format()
+
+ self.worksheet.merge_range('B3:C3', 'Foo', cell_format)
+ self.worksheet._write_merge_cells()
+
+ exp = """<mergeCells count="1"><mergeCell ref="B3:C3"/></mergeCells>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_merge_cells_3(self):
+ """Test the _write_merge_cells() method"""
+
+ cell_format = Format()
+
+ self.worksheet.merge_range('B3:C3', 'Foo', cell_format)
+ self.worksheet.merge_range('A2:D2', 'Foo', cell_format)
+ self.worksheet._write_merge_cells()
+
+ exp = """<mergeCells count="2"><mergeCell ref="B3:C3"/><mergeCell ref="A2:D2"/></mergeCells>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_page_margins.py b/xlsxwriter/test/worksheet/test_write_page_margins.py
new file mode 100644
index 0000000..1a73135
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_page_margins.py
@@ -0,0 +1,109 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWritePageMargins(unittest.TestCase):
+ """
+ Test the Worksheet _write_page_margins() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_page_margins(self):
+ """Test the _write_page_margins() method"""
+
+ self.worksheet._write_page_margins()
+
+ exp = """<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_page_margins_deafult(self):
+ """Test the _write_page_margins() method with default margins"""
+
+ self.worksheet.set_margins()
+ self.worksheet._write_page_margins()
+
+ exp = """<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_page_margins_left(self):
+ """Test the _write_page_margins() method with left margin"""
+
+ self.worksheet.set_margins(left=0.5)
+ self.worksheet._write_page_margins()
+
+ exp = """<pageMargins left="0.5" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_page_margins_right(self):
+ """Test the _write_page_margins() method with right margin"""
+
+ self.worksheet.set_margins(right=0.5)
+ self.worksheet._write_page_margins()
+
+ exp = """<pageMargins left="0.7" right="0.5" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_page_margins_top(self):
+ """Test the _write_page_margins() method with top margin"""
+
+ self.worksheet.set_margins(top=0.5)
+ self.worksheet._write_page_margins()
+
+ exp = """<pageMargins left="0.7" right="0.7" top="0.5" bottom="0.75" header="0.3" footer="0.3"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_page_margins_bottom(self):
+ """Test the _write_page_margins() method with bottom margin"""
+
+ self.worksheet.set_margins(bottom=0.5)
+ self.worksheet._write_page_margins()
+
+ exp = """<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.5" header="0.3" footer="0.3"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_page_margins_header(self):
+ """Test the _write_page_margins() method with header margin"""
+
+ self.worksheet.set_header(margin=0.5)
+ self.worksheet._write_page_margins()
+
+ exp = """<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.5" footer="0.3"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_page_margins_footer(self):
+ """Test the _write_page_margins() method with footer margin"""
+
+ self.worksheet.set_footer(margin=0.5)
+ self.worksheet._write_page_margins()
+
+ exp = """<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.5"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_page_setup.py b/xlsxwriter/test/worksheet/test_write_page_setup.py
new file mode 100644
index 0000000..23b53fc
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_page_setup.py
@@ -0,0 +1,80 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWritePageSetup(unittest.TestCase):
+ """
+ Test the Worksheet _write_page_setup() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_page_setup_none(self):
+ """Test the _write_page_setup() method. Without any page setup"""
+
+ self.worksheet._write_page_setup()
+
+ exp = ''
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_page_setup_landscape(self):
+ """Test the _write_page_setup() method. With set_landscape()"""
+
+ self.worksheet.set_landscape()
+
+ self.worksheet._write_page_setup()
+
+ exp = """<pageSetup orientation="landscape"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_page_setup_portrait(self):
+ """Test the _write_page_setup() method. With set_portrait()"""
+
+ self.worksheet.set_portrait()
+
+ self.worksheet._write_page_setup()
+
+ exp = """<pageSetup orientation="portrait"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_page_setup_paper(self):
+ """Test the _write_page_setup() method. With set_paper()"""
+
+ self.worksheet.set_paper(9)
+
+ self.worksheet._write_page_setup()
+
+ exp = """<pageSetup paperSize="9" orientation="portrait"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_page_setup_print_across(self):
+ """Test the _write_page_setup() method. With print_across()"""
+
+ self.worksheet.print_across()
+
+ self.worksheet._write_page_setup()
+
+ exp = """<pageSetup pageOrder="overThenDown" orientation="portrait"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_print_options.py b/xlsxwriter/test/worksheet/test_write_print_options.py
new file mode 100644
index 0000000..9d7b106
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_print_options.py
@@ -0,0 +1,88 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWritePrintOptions(unittest.TestCase):
+ """
+ Test the Worksheet _write_print_options() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_print_options_default(self):
+ """Test the _write_print_options() method without options"""
+
+ self.worksheet._write_print_options()
+
+ exp = """"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_print_options_hcenter(self):
+ """Test the _write_print_options() method with horizontal center"""
+
+ self.worksheet.center_horizontally()
+ self.worksheet._write_print_options()
+
+ exp = """<printOptions horizontalCentered="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_print_options_vcenter(self):
+ """Test the _write_print_options() method with vertical center"""
+
+ self.worksheet.center_vertically()
+ self.worksheet._write_print_options()
+
+ exp = """<printOptions verticalCentered="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_print_options_center(self):
+ """Test the _write_print_options() method with horiz + vert center"""
+
+ self.worksheet.center_horizontally()
+ self.worksheet.center_vertically()
+ self.worksheet._write_print_options()
+
+ exp = """<printOptions horizontalCentered="1" verticalCentered="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_print_options_gridlines_default(self):
+ """Test the _write_print_options() method with default value"""
+
+ self.worksheet.hide_gridlines()
+ self.worksheet._write_print_options()
+
+ exp = """"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_print_options_gridlines_0(self):
+ """Test the _write_print_options() method with 0 value"""
+
+ self.worksheet.hide_gridlines(0)
+ self.worksheet._write_print_options()
+
+ exp = """<printOptions gridLines="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_row.py b/xlsxwriter/test/worksheet/test_write_row.py
new file mode 100644
index 0000000..53f5116
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_row.py
@@ -0,0 +1,105 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+from ...format import Format
+
+
+class TestWriteRow(unittest.TestCase):
+ """
+ Test the Worksheet _write_row() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_row_1(self):
+ """Test the _write_row() method"""
+
+ self.worksheet._write_row(0, None)
+
+ exp = """<row r="1">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_row_2(self):
+ """Test the _write_row() method"""
+
+ self.worksheet._write_row(2, '2:2')
+
+ exp = """<row r="3" spans="2:2">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_row_3(self):
+ """Test the _write_row() method"""
+
+ self.worksheet._write_row(1, None, [30, None, 0, 0, 0])
+
+ exp = """<row r="2" ht="30" customHeight="1">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_row_4(self):
+ """Test the _write_row() method"""
+
+ self.worksheet._write_row(3, None, [15, None, 1, 0, 0])
+
+ exp = """<row r="4" hidden="1">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_row_5(self):
+ """Test the _write_row() method"""
+
+ cell_format = Format({'xf_index': 1})
+
+ self.worksheet._write_row(6, None, [15, cell_format, 0, 0, 0])
+
+ exp = """<row r="7" s="1" customFormat="1">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_row_6(self):
+ """Test the _write_row() method"""
+
+ self.worksheet._write_row(9, None, [3, None, 0, 0, 0])
+
+ exp = """<row r="10" ht="3" customHeight="1">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_row_7(self):
+ """Test the _write_row() method"""
+
+ self.worksheet._write_row(12, None, [24, None, 1, 0, 0])
+
+ exp = """<row r="13" ht="24" hidden="1" customHeight="1">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_row_8(self):
+ """Test the _write_row() method"""
+
+ self.worksheet._write_row(12, None, [24, None, 1, 0, 0], 1)
+
+ exp = """<row r="13" ht="24" hidden="1" customHeight="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_row_breaks.py b/xlsxwriter/test/worksheet/test_write_row_breaks.py
new file mode 100644
index 0000000..96eb883
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_row_breaks.py
@@ -0,0 +1,46 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteRowBreaks(unittest.TestCase):
+ """
+ Test the Worksheet _write_row_breaks() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_row_breaks_1(self):
+ """Test the _write_row_breaks() method"""
+
+ self.worksheet.hbreaks = [1]
+
+ self.worksheet._write_row_breaks()
+
+ exp = """<rowBreaks count="1" manualBreakCount="1"><brk id="1" max="16383" man="1"/></rowBreaks>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_row_breaks_2(self):
+ """Test the _write_row_breaks() method"""
+
+ self.worksheet.hbreaks = [15, 7, 3, 0]
+
+ self.worksheet._write_row_breaks()
+
+ exp = """<rowBreaks count="3" manualBreakCount="3"><brk id="3" max="16383" man="1"/><brk id="7" max="16383" man="1"/><brk id="15" max="16383" man="1"/></rowBreaks>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_data.py b/xlsxwriter/test/worksheet/test_write_sheet_data.py
new file mode 100644
index 0000000..3ae8cf7
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_data.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetData(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_data() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_data(self):
+ """Test the _write_sheet_data() method"""
+
+ self.worksheet._write_sheet_data()
+
+ exp = """<sheetData/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_format_pr.py b/xlsxwriter/test/worksheet/test_write_sheet_format_pr.py
new file mode 100644
index 0000000..26949c1
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_format_pr.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetFormatPr(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_format_pr() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_format_pr(self):
+ """Test the _write_sheet_format_pr() method"""
+
+ self.worksheet._write_sheet_format_pr()
+
+ exp = """<sheetFormatPr defaultRowHeight="15"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_pr.py b/xlsxwriter/test/worksheet/test_write_sheet_pr.py
new file mode 100644
index 0000000..6d017fe
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_pr.py
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetPr(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_pr() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_pr_fit_to_page(self):
+ """Test the _write_sheet_pr() method"""
+
+ self.worksheet.fit_to_pages(1, 1)
+ self.worksheet._write_sheet_pr()
+
+ exp = """<sheetPr><pageSetUpPr fitToPage="1"/></sheetPr>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_pr_tab_color(self):
+ """Test the _write_sheet_pr() method"""
+
+ self.worksheet.set_tab_color('red')
+ self.worksheet._write_sheet_pr()
+
+ exp = """<sheetPr><tabColor rgb="FFFF0000"/></sheetPr>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_pr_both(self):
+ """Test the _write_sheet_pr() method"""
+
+ self.worksheet.set_tab_color('red')
+ self.worksheet.fit_to_pages(1, 1)
+ self.worksheet._write_sheet_pr()
+
+ exp = """<sheetPr><tabColor rgb="FFFF0000"/><pageSetUpPr fitToPage="1"/></sheetPr>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_protection.py b/xlsxwriter/test/worksheet/test_write_sheet_protection.py
new file mode 100644
index 0000000..98e40e7
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_protection.py
@@ -0,0 +1,290 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetProtection(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_protection() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_protection_1(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_2(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = 'password'
+ options = {}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection password="83AF" sheet="1" objects="1" scenarios="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_3(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'select_locked_cells': 0}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1" selectLockedCells="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_4(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'format_cells': 1}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1" formatCells="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_5(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'format_columns': 1}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1" formatColumns="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_6(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'format_rows': 1}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1" formatRows="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_7(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'insert_columns': 1}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1" insertColumns="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_8(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'insert_rows': 1}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1" insertRows="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_9(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'insert_hyperlinks': 1}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1" insertHyperlinks="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_10(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'delete_columns': 1}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1" deleteColumns="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_11(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'delete_rows': 1}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1" deleteRows="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_12(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'sort': 1}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1" sort="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_13(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'autofilter': 1}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1" autoFilter="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_14(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'pivot_tables': 1}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1" pivotTables="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_15(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'objects': 1}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" scenarios="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_16(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'scenarios': 1}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_17(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = ''
+ options = {'format_cells': 1, 'select_locked_cells': 0, 'select_unlocked_cells': 0}
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection sheet="1" objects="1" scenarios="1" formatCells="0" selectLockedCells="1" selectUnlockedCells="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_protection_18(self):
+ """Test the _write_sheet_protection() method."""
+
+ password = 'drowssap'
+ options = {
+ 'objects': 1,
+ 'scenarios': 1,
+ 'format_cells': 1,
+ 'format_columns': 1,
+ 'format_rows': 1,
+ 'insert_columns': 1,
+ 'insert_rows': 1,
+ 'insert_hyperlinks': 1,
+ 'delete_columns': 1,
+ 'delete_rows': 1,
+ 'select_locked_cells': 0,
+ 'sort': 1,
+ 'autofilter': 1,
+ 'pivot_tables': 1,
+ 'select_unlocked_cells': 0,
+ }
+
+ self.worksheet.protect(password, options)
+ self.worksheet._write_sheet_protection()
+
+ exp = """<sheetProtection password="996B" sheet="1" formatCells="0" formatColumns="0" formatRows="0" insertColumns="0" insertRows="0" insertHyperlinks="0" deleteColumns="0" deleteRows="0" selectLockedCells="1" sort="0" autoFilter="0" pivotTables="0" selectUnlockedCells="1"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_view.py b/xlsxwriter/test/worksheet/test_write_sheet_view.py
new file mode 100644
index 0000000..e89074d
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_view.py
@@ -0,0 +1,91 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetView(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_view() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_view_tab_not_selected(self):
+ """Test the _write_sheet_view() method. Tab not selected"""
+
+ self.worksheet._write_sheet_view()
+
+ exp = """<sheetView workbookViewId="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_view_tab_selected(self):
+ """Test the _write_sheet_view() method. Tab selected"""
+
+ self.worksheet.select()
+ self.worksheet._write_sheet_view()
+
+ exp = """<sheetView tabSelected="1" workbookViewId="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_view_hide_gridlines(self):
+ """Test the _write_sheet_view() method. Tab selected + hide_gridlines()"""
+
+ self.worksheet.select()
+ self.worksheet.hide_gridlines()
+ self.worksheet._write_sheet_view()
+
+ exp = """<sheetView tabSelected="1" workbookViewId="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_view_hide_gridlines_0(self):
+ """Test the _write_sheet_view() method. Tab selected + hide_gridlines(0)"""
+
+ self.worksheet.select()
+ self.worksheet.hide_gridlines(0)
+ self.worksheet._write_sheet_view()
+
+ exp = """<sheetView tabSelected="1" workbookViewId="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_view_hide_gridlines_1(self):
+ """Test the _write_sheet_view() method. Tab selected + hide_gridlines(1)"""
+
+ self.worksheet.select()
+ self.worksheet.hide_gridlines(1)
+ self.worksheet._write_sheet_view()
+
+ exp = """<sheetView tabSelected="1" workbookViewId="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_view_hide_gridlines_2(self):
+ """Test the _write_sheet_view() method. Tab selected + hide_gridlines(2)"""
+
+ self.worksheet.select()
+ self.worksheet.hide_gridlines(2)
+ self.worksheet._write_sheet_view()
+
+ exp = """<sheetView showGridLines="0" tabSelected="1" workbookViewId="0"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_views1.py b/xlsxwriter/test/worksheet/test_write_sheet_views1.py
new file mode 100644
index 0000000..056c234
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_views1.py
@@ -0,0 +1,93 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetViews(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_views() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_views(self):
+ """Test the _write_sheet_views() method"""
+
+ self.worksheet.select()
+ self.worksheet._write_sheet_views()
+
+ exp = """<sheetViews><sheetView tabSelected="1" workbookViewId="0"/></sheetViews>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views_zoom_100(self):
+ """Test the _write_sheet_views() method"""
+
+ self.worksheet.select()
+ self.worksheet.set_zoom(100) # Default. Should be ignored.
+ self.worksheet._write_sheet_views()
+
+ exp = """<sheetViews><sheetView tabSelected="1" workbookViewId="0"/></sheetViews>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views_zoom_200(self):
+ """Test the _write_sheet_views() method"""
+
+ self.worksheet.select()
+ self.worksheet.set_zoom(200)
+ self.worksheet._write_sheet_views()
+
+ exp = """<sheetViews><sheetView tabSelected="1" zoomScale="200" zoomScaleNormal="200" workbookViewId="0"/></sheetViews>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views_right_to_left(self):
+ """Test the _write_sheet_views() method"""
+
+ self.worksheet.select()
+ self.worksheet.right_to_left()
+ self.worksheet._write_sheet_views()
+
+ exp = """<sheetViews><sheetView rightToLeft="1" tabSelected="1" workbookViewId="0"/></sheetViews>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views_hide_zero(self):
+ """Test the _write_sheet_views() method"""
+
+ self.worksheet.select()
+ self.worksheet.hide_zero()
+ self.worksheet._write_sheet_views()
+
+ exp = """<sheetViews><sheetView showZeros="0" tabSelected="1" workbookViewId="0"/></sheetViews>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views_page_view(self):
+ """Test the _write_sheet_views() method"""
+
+ self.worksheet.select()
+ self.worksheet.set_page_view()
+ self.worksheet._write_sheet_views()
+
+ exp = """<sheetViews><sheetView tabSelected="1" view="pageLayout" workbookViewId="0"/></sheetViews>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_views2.py b/xlsxwriter/test/worksheet/test_write_sheet_views2.py
new file mode 100644
index 0000000..f86be48
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_views2.py
@@ -0,0 +1,92 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetViews(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_views() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_views1(self):
+ """Test the _write_sheet_views() method with freeze panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.freeze_panes(1, 0)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane ySplit="1" topLeftCell="A2" activePane="bottomLeft" state="frozen"/><selection pane="bottomLeft"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views2(self):
+ """Test the _write_sheet_views() method with freeze panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.freeze_panes(0, 1)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="1" topLeftCell="B1" activePane="topRight" state="frozen"/><selection pane="topRight"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views3(self):
+ """Test the _write_sheet_views() method with freeze panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.freeze_panes(1, 1)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="1" ySplit="1" topLeftCell="B2" activePane="bottomRight" state="frozen"/><selection pane="topRight" activeCell="B1" sqref="B1"/><selection pane="bottomLeft" activeCell="A2" sqref="A2"/><selection pane="bottomRight"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views4(self):
+ """Test the _write_sheet_views() method with freeze panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.freeze_panes('G4')
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="6" ySplit="3" topLeftCell="G4" activePane="bottomRight" state="frozen"/><selection pane="topRight" activeCell="G1" sqref="G1"/><selection pane="bottomLeft" activeCell="A4" sqref="A4"/><selection pane="bottomRight"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views5(self):
+ """Test the _write_sheet_views() method with freeze panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.freeze_panes(3, 6, 3, 6, 1)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="6" ySplit="3" topLeftCell="G4" activePane="bottomRight" state="frozenSplit"/><selection pane="topRight" activeCell="G1" sqref="G1"/><selection pane="bottomLeft" activeCell="A4" sqref="A4"/><selection pane="bottomRight"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_views3.py b/xlsxwriter/test/worksheet/test_write_sheet_views3.py
new file mode 100644
index 0000000..c7f2137
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_views3.py
@@ -0,0 +1,134 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetViews(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_views() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_views1(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(15, 0)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane ySplit="600" topLeftCell="A2"/><selection pane="bottomLeft" activeCell="A2" sqref="A2"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views2(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(30, 0)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane ySplit="900" topLeftCell="A3"/><selection pane="bottomLeft" activeCell="A3" sqref="A3"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views3(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(105, 0)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane ySplit="2400" topLeftCell="A8"/><selection pane="bottomLeft" activeCell="A8" sqref="A8"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views4(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(0, 8.43)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="1350" topLeftCell="B1"/><selection pane="topRight" activeCell="B1" sqref="B1"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views5(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(0, 17.57)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="2310" topLeftCell="C1"/><selection pane="topRight" activeCell="C1" sqref="C1"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views6(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(0, 45)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="5190" topLeftCell="F1"/><selection pane="topRight" activeCell="F1" sqref="F1"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views7(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(15, 8.43)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="1350" ySplit="600" topLeftCell="B2"/><selection pane="topRight" activeCell="B1" sqref="B1"/><selection pane="bottomLeft" activeCell="A2" sqref="A2"/><selection pane="bottomRight" activeCell="B2" sqref="B2"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views8(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(45, 54.14)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="6150" ySplit="1200" topLeftCell="G4"/><selection pane="topRight" activeCell="G1" sqref="G1"/><selection pane="bottomLeft" activeCell="A4" sqref="A4"/><selection pane="bottomRight" activeCell="G4" sqref="G4"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_views4.py b/xlsxwriter/test/worksheet/test_write_sheet_views4.py
new file mode 100644
index 0000000..a4c4459
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_views4.py
@@ -0,0 +1,135 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetViews(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_views() method.
+ Repeat of tests in test_write_sheet_views3.py with explicit topLeft cells.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_views1(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(15, 0, 1, 0)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane ySplit="600" topLeftCell="A2"/><selection pane="bottomLeft" activeCell="A2" sqref="A2"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views2(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(30, 0, 2, 0)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane ySplit="900" topLeftCell="A3"/><selection pane="bottomLeft" activeCell="A3" sqref="A3"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views3(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(105, 0, 7, 0)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane ySplit="2400" topLeftCell="A8"/><selection pane="bottomLeft" activeCell="A8" sqref="A8"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views4(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(0, 8.43, 0, 1)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="1350" topLeftCell="B1"/><selection pane="topRight" activeCell="B1" sqref="B1"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views5(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(0, 17.57, 0, 2)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="2310" topLeftCell="C1"/><selection pane="topRight" activeCell="C1" sqref="C1"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views6(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(0, 45, 0, 5)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="5190" topLeftCell="F1"/><selection pane="topRight" activeCell="F1" sqref="F1"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views7(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(15, 8.43, 1, 1)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="1350" ySplit="600" topLeftCell="B2"/><selection pane="topRight" activeCell="B1" sqref="B1"/><selection pane="bottomLeft" activeCell="A2" sqref="A2"/><selection pane="bottomRight" activeCell="B2" sqref="B2"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views8(self):
+ """Test the _write_sheet_views() method with split panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.split_panes(45, 54.14, 3, 6)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="6150" ySplit="1200" topLeftCell="G4"/><selection pane="topRight" activeCell="G1" sqref="G1"/><selection pane="bottomLeft" activeCell="A4" sqref="A4"/><selection pane="bottomRight" activeCell="G4" sqref="G4"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_views5.py b/xlsxwriter/test/worksheet/test_write_sheet_views5.py
new file mode 100644
index 0000000..d83e223
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_views5.py
@@ -0,0 +1,122 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetViews(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_views() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_views1(self):
+ """Test the _write_sheet_views() method with selection set"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('A1')
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"/></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views2(self):
+ """Test the _write_sheet_views() method with selection set"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('A2')
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><selection activeCell="A2" sqref="A2"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views3(self):
+ """Test the _write_sheet_views() method with selection set"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('B1')
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><selection activeCell="B1" sqref="B1"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views4(self):
+ """Test the _write_sheet_views() method with selection set"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('D3')
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><selection activeCell="D3" sqref="D3"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views5(self):
+ """Test the _write_sheet_views() method with selection set"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('D3:F4')
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><selection activeCell="D3" sqref="D3:F4"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views6(self):
+ """Test the _write_sheet_views() method with selection set"""
+
+ self.worksheet.select()
+
+ # With reversed selection direction.
+ self.worksheet.set_selection('F4:D3')
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><selection activeCell="F4" sqref="D3:F4"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views7(self):
+ """Test the _write_sheet_views() method with selection set"""
+
+ self.worksheet.select()
+
+ # Should be the same as 'A2'
+ self.worksheet.set_selection('A2:A2')
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><selection activeCell="A2" sqref="A2"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_views6.py b/xlsxwriter/test/worksheet/test_write_sheet_views6.py
new file mode 100644
index 0000000..c44bef7
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_views6.py
@@ -0,0 +1,82 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetViews(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_views() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_views1(self):
+ """Test the _write_sheet_views() method with freeze panes + selection"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('A2')
+ self.worksheet.freeze_panes(1, 0)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane ySplit="1" topLeftCell="A2" activePane="bottomLeft" state="frozen"/><selection pane="bottomLeft" activeCell="A2" sqref="A2"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views2(self):
+ """Test the _write_sheet_views() method with freeze panes + selection"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('B1')
+ self.worksheet.freeze_panes(0, 1)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="1" topLeftCell="B1" activePane="topRight" state="frozen"/><selection pane="topRight" activeCell="B1" sqref="B1"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views3(self):
+ """Test the _write_sheet_views() method with freeze panes + selection"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('G4')
+ self.worksheet.freeze_panes('G4')
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="6" ySplit="3" topLeftCell="G4" activePane="bottomRight" state="frozen"/><selection pane="topRight" activeCell="G1" sqref="G1"/><selection pane="bottomLeft" activeCell="A4" sqref="A4"/><selection pane="bottomRight" activeCell="G4" sqref="G4"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views4(self):
+ """Test the _write_sheet_views() method with freeze panes + selection"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('I5')
+ self.worksheet.freeze_panes('G4')
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="6" ySplit="3" topLeftCell="G4" activePane="bottomRight" state="frozen"/><selection pane="topRight" activeCell="G1" sqref="G1"/><selection pane="bottomLeft" activeCell="A4" sqref="A4"/><selection pane="bottomRight" activeCell="I5" sqref="I5"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_views7.py b/xlsxwriter/test/worksheet/test_write_sheet_views7.py
new file mode 100644
index 0000000..bb2bc68
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_views7.py
@@ -0,0 +1,112 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetViews(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_views() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_views1(self):
+ """Test the _write_sheet_views() method with freeze panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('A2')
+ self.worksheet.freeze_panes(1, 0, 20, 0)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane ySplit="1" topLeftCell="A21" activePane="bottomLeft" state="frozen"/><selection pane="bottomLeft" activeCell="A2" sqref="A2"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views2(self):
+ """Test the _write_sheet_views() method with freeze panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('A1')
+ self.worksheet.freeze_panes(1, 0, 20, 0)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane ySplit="1" topLeftCell="A21" activePane="bottomLeft" state="frozen"/><selection pane="bottomLeft"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views3(self):
+ """Test the _write_sheet_views() method with freeze panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('B1')
+ self.worksheet.freeze_panes(0, 1, 0, 4)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="1" topLeftCell="E1" activePane="topRight" state="frozen"/><selection pane="topRight" activeCell="B1" sqref="B1"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views4(self):
+ """Test the _write_sheet_views() method with freeze panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.freeze_panes(0, 1, 0, 4)
+
+ self.worksheet.set_selection('A1')
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="1" topLeftCell="E1" activePane="topRight" state="frozen"/><selection pane="topRight"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views5(self):
+ """Test the _write_sheet_views() method with freeze panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('G4')
+ self.worksheet.freeze_panes(3, 6, 6, 8)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="6" ySplit="3" topLeftCell="I7" activePane="bottomRight" state="frozen"/><selection pane="topRight" activeCell="G1" sqref="G1"/><selection pane="bottomLeft" activeCell="A4" sqref="A4"/><selection pane="bottomRight" activeCell="G4" sqref="G4"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views6(self):
+ """Test the _write_sheet_views() method with freeze panes"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('A1')
+ self.worksheet.freeze_panes(3, 6, 6, 8)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="6" ySplit="3" topLeftCell="I7" activePane="bottomRight" state="frozen"/><selection pane="topRight" activeCell="G1" sqref="G1"/><selection pane="bottomLeft" activeCell="A4" sqref="A4"/><selection pane="bottomRight"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_views8.py b/xlsxwriter/test/worksheet/test_write_sheet_views8.py
new file mode 100644
index 0000000..ceedb20
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_views8.py
@@ -0,0 +1,82 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetViews(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_views() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_views1(self):
+ """Test the _write_sheet_views() method with split panes + selection"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('A2')
+ self.worksheet.split_panes(15, 0)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane ySplit="600" topLeftCell="A2" activePane="bottomLeft"/><selection pane="bottomLeft" activeCell="A2" sqref="A2"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views2(self):
+ """Test the _write_sheet_views() method with split panes + selection"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('B1')
+ self.worksheet.split_panes(0, 8.43)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="1350" topLeftCell="B1" activePane="topRight"/><selection pane="topRight" activeCell="B1" sqref="B1"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views3(self):
+ """Test the _write_sheet_views() method with split panes + selection"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('G4')
+ self.worksheet.split_panes(45, 54.14)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="6150" ySplit="1200" topLeftCell="G4" activePane="bottomRight"/><selection pane="topRight" activeCell="G1" sqref="G1"/><selection pane="bottomLeft" activeCell="A4" sqref="A4"/><selection pane="bottomRight" activeCell="G4" sqref="G4"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views4(self):
+ """Test the _write_sheet_views() method with split panes + selection"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('I5')
+ self.worksheet.split_panes(45, 54.14)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="6150" ySplit="1200" topLeftCell="G4" activePane="bottomRight"/><selection pane="topRight" activeCell="G1" sqref="G1"/><selection pane="bottomLeft" activeCell="A4" sqref="A4"/><selection pane="bottomRight" activeCell="I5" sqref="I5"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_sheet_views9.py b/xlsxwriter/test/worksheet/test_write_sheet_views9.py
new file mode 100644
index 0000000..675275e
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_sheet_views9.py
@@ -0,0 +1,83 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteSheetViews(unittest.TestCase):
+ """
+ Test the Worksheet _write_sheet_views() method.
+ With explicit top/left cells.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_sheet_views1(self):
+ """Test the _write_sheet_views() method with split panes + selection"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('A2')
+ self.worksheet.split_panes(15, 0, 20, 0)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane ySplit="600" topLeftCell="A21" activePane="bottomLeft"/><selection pane="bottomLeft" activeCell="A2" sqref="A2"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views2(self):
+ """Test the _write_sheet_views() method with split panes + selection"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('A21')
+ self.worksheet.split_panes(15, 0, 20, 0)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane ySplit="600" topLeftCell="A21" activePane="bottomLeft"/><selection pane="bottomLeft" activeCell="A21" sqref="A21"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views3(self):
+ """Test the _write_sheet_views() method with split panes + selection"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('B1')
+ self.worksheet.split_panes(0, 8.43, 0, 4)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="1350" topLeftCell="E1" activePane="topRight"/><selection pane="topRight" activeCell="B1" sqref="B1"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_write_sheet_views4(self):
+ """Test the _write_sheet_views() method with split panes + selection"""
+
+ self.worksheet.select()
+
+ self.worksheet.set_selection('E1')
+ self.worksheet.split_panes(0, 8.43, 0, 4)
+
+ self.worksheet._write_sheet_views()
+
+ exp = '<sheetViews><sheetView tabSelected="1" workbookViewId="0"><pane xSplit="1350" topLeftCell="E1" activePane="topRight"/><selection pane="topRight" activeCell="E1" sqref="E1"/></sheetView></sheetViews>'
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_tab_color.py b/xlsxwriter/test/worksheet/test_write_tab_color.py
new file mode 100644
index 0000000..b99fd57
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_tab_color.py
@@ -0,0 +1,33 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteTabColor(unittest.TestCase):
+ """
+ Test the Worksheet _write_tab_color() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_tab_color(self):
+ """Test the _write_tab_color() method"""
+
+ self.worksheet.set_tab_color('red')
+ self.worksheet._write_tab_color()
+
+ exp = """<tabColor rgb="FFFF0000"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/worksheet/test_write_worksheet.py b/xlsxwriter/test/worksheet/test_write_worksheet.py
new file mode 100644
index 0000000..ecbb112
--- /dev/null
+++ b/xlsxwriter/test/worksheet/test_write_worksheet.py
@@ -0,0 +1,32 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...worksheet import Worksheet
+
+
+class TestWriteWorksheet(unittest.TestCase):
+ """
+ Test the Worksheet _write_worksheet() method.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.worksheet = Worksheet()
+ self.worksheet._set_filehandle(self.fh)
+
+ def test_write_worksheet(self):
+ """Test the _write_worksheet() method"""
+
+ self.worksheet._write_worksheet()
+
+ exp = """<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/test/xmlwriter/__init__.py b/xlsxwriter/test/xmlwriter/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xlsxwriter/test/xmlwriter/test_xmlwriter.py b/xlsxwriter/test/xmlwriter/test_xmlwriter.py
new file mode 100644
index 0000000..fcbb589
--- /dev/null
+++ b/xlsxwriter/test/xmlwriter/test_xmlwriter.py
@@ -0,0 +1,192 @@
+###############################################################################
+#
+# Tests for XlsxWriter.
+#
+# Copyright (c), 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+import unittest
+from ...compatibility import StringIO
+from ...xmlwriter import XMLwriter
+
+
+class TestXMLwriter(unittest.TestCase):
+ """
+ Test the XML Writer class.
+
+ """
+
+ def setUp(self):
+ self.fh = StringIO()
+ self.writer = XMLwriter()
+ self.writer._set_filehandle(self.fh)
+
+ def test_xml_declaration(self):
+ """Test _xml_declaration()"""
+
+ self.writer._xml_declaration()
+
+ exp = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_start_tag(self):
+ """Test _xml_start_tag() with no attributes"""
+
+ self.writer._xml_start_tag('foo')
+
+ exp = """<foo>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_start_tag_with_attributes(self):
+ """Test _xml_start_tag() with attributes"""
+
+ self.writer._xml_start_tag('foo', [('span', '8'), ('baz', '7')])
+
+ exp = """<foo span="8" baz="7">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_start_tag_with_attributes_to_escape(self):
+ """Test _xml_start_tag() with attributes requiring escaping"""
+
+ self.writer._xml_start_tag('foo', [('span', '&<>"')])
+
+ exp = """<foo span="&<>"">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_start_tag_unencoded(self):
+ """Test _xml_start_tag_unencoded() with attributes"""
+
+ self.writer._xml_start_tag_unencoded('foo', [('span', '&<>"')])
+
+ exp = """<foo span="&<>"">"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_end_tag(self):
+ """Test _xml_end_tag()"""
+
+ self.writer._xml_end_tag('foo')
+
+ exp = """</foo>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_empty_tag(self):
+ """Test _xml_empty_tag()"""
+
+ self.writer._xml_empty_tag('foo')
+
+ exp = """<foo/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_empty_tag_with_attributes(self):
+ """Test _xml_empty_tag() with attributes"""
+
+ self.writer._xml_empty_tag('foo', [('span', '8')])
+
+ exp = """<foo span="8"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_empty_tag_unencoded(self):
+ """Test _xml_empty_tag_unencoded() with attributes"""
+
+ self.writer._xml_empty_tag_unencoded('foo', [('span', '&')])
+
+ exp = """<foo span="&"/>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_data_element(self):
+ """Test _xml_data_element()"""
+
+ self.writer._xml_data_element('foo', 'bar')
+
+ exp = """<foo>bar</foo>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_data_element_with_attributes(self):
+ """Test _xml_data_element() with attributes"""
+
+ self.writer._xml_data_element('foo', 'bar', [('span', '8')])
+
+ exp = """<foo span="8">bar</foo>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_data_element_with_escapes(self):
+ """Test _xml_data_element() with data requiring escaping"""
+
+ self.writer._xml_data_element('foo', '&<>"', [('span', '8')])
+
+ exp = """<foo span="8">&<>"</foo>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_string_element(self):
+ """Test _xml_string_element()"""
+
+ self.writer._xml_string_element(99, [('span', '8')])
+
+ exp = """<c span="8" t=\"s\"><v>99</v></c>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_si_element(self):
+ """Test _xml_si_element()"""
+
+ self.writer._xml_si_element('foo', [('span', '8')])
+
+ exp = """<si><t span="8">foo</t></si>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_rich_si_element(self):
+ """Test _xml_rich_si_element()"""
+
+ self.writer._xml_rich_si_element('foo')
+
+ exp = """<si>foo</si>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_number_element(self):
+ """Test _xml_number_element()"""
+
+ self.writer._xml_number_element(99, [('span', '8')])
+
+ exp = """<c span="8"><v>99</v></c>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
+
+ def test_xml_formula_element(self):
+ """Test _xml_formula_element()"""
+
+ self.writer._xml_formula_element('1+2', 3, [('span', '8')])
+
+ exp = """<c span="8"><f>1+2</f><v>3</v></c>"""
+ got = self.fh.getvalue()
+
+ self.assertEqual(got, exp)
diff --git a/xlsxwriter/theme.py b/xlsxwriter/theme.py
new file mode 100644
index 0000000..0d7e3bf
--- /dev/null
+++ b/xlsxwriter/theme.py
@@ -0,0 +1,74 @@
+###############################################################################
+#
+# Theme - A class for writing the Excel XLSX Worksheet file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Standard packages.
+import codecs
+import sys
+
+# Standard packages in Python 2/3 compatibility mode.
+from .compatibility import StringIO
+
+
+class Theme(object):
+ """
+ A class for writing the Excel XLSX Theme file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+ super(Theme, self).__init__()
+ self.fh = None
+ self.internal_fh = False
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+ self._write_theme_file()
+ if self.internal_fh:
+ self.fh.close()
+
+ def _set_xml_writer(self, filename):
+ # Set the XML writer filehandle for the object.
+ if isinstance(filename, StringIO):
+ self.internal_fh = False
+ self.fh = filename
+ else:
+ self.internal_fh = True
+ self.fh = codecs.open(filename, 'w', 'utf-8')
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_theme_file(self):
+ # Write a default theme.xml file.
+
+ # The theme is encoded to allow Python 2.5/Jython support.
+ default_theme = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme"><a:themeElements><a:clrScheme name="Office"><a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1><a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="1F497D"/></a:dk2><a:lt2><a:srgbClr val="EEECE1"/></a:lt2><a:accent1><a:srgbClr val="4F81BD"/></a:accent1><a:accent2><a:srgbClr val=" [...]
+
+ if sys.version_info < (3, 0, 0):
+ default_theme = default_theme.decode('unicode-escape')
+
+ self.fh.write(default_theme)
diff --git a/xlsxwriter/utility.py b/xlsxwriter/utility.py
new file mode 100644
index 0000000..13a8e5b
--- /dev/null
+++ b/xlsxwriter/utility.py
@@ -0,0 +1,673 @@
+###############################################################################
+#
+# Worksheet - A class for writing Excel Worksheets.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+import re
+import datetime
+from warnings import warn
+
+COL_NAMES = {}
+range_parts = re.compile(r'(\$?)([A-Z]{1,3})(\$?)(\d+)')
+
+
+def xl_rowcol_to_cell(row, col, row_abs=False, col_abs=False):
+ """
+ Convert a zero indexed row and column cell reference to a A1 style string.
+
+ Args:
+ row: The cell row. Int.
+ col: The cell column. Int.
+ row_abs: Optional flag to make the row absolute. Bool.
+ col_abs: Optional flag to make the column absolute. Bool.
+
+ Returns:
+ A1 style string.
+
+ """
+ row += 1 # Change to 1-index.
+ row_abs = '$' if row_abs else ''
+
+ col_str = xl_col_to_name(col, col_abs)
+
+ return col_str + row_abs + str(row)
+
+
+def xl_rowcol_to_cell_fast(row, col):
+ """
+ Optimized version of the xl_rowcol_to_cell function. Only used internally.
+
+ Args:
+ row: The cell row. Int.
+ col: The cell column. Int.
+
+ Returns:
+ A1 style string.
+
+ """
+ if col in COL_NAMES:
+ col_str = COL_NAMES[col]
+ else:
+ col_str = xl_col_to_name(col)
+ COL_NAMES[col] = col_str
+
+ return col_str + str(row + 1)
+
+
+def xl_col_to_name(col_num, col_abs=False):
+ """
+ Convert a zero indexed column cell reference to a string.
+
+ Args:
+ col: The cell column. Int.
+ col_abs: Optional flag to make the column absolute. Bool.
+
+ Returns:
+ Column style string.
+
+ """
+ col_num += 1 # Change to 1-index.
+ col_str = ''
+ col_abs = '$' if col_abs else ''
+
+ while col_num:
+ # Set remainder from 1 .. 26
+ remainder = col_num % 26
+
+ if remainder == 0:
+ remainder = 26
+
+ # Convert the remainder to a character.
+ col_letter = chr(ord('A') + remainder - 1)
+
+ # Accumulate the column letters, right to left.
+ col_str = col_letter + col_str
+
+ # Get the next order of magnitude.
+ col_num = int((col_num - 1) / 26)
+
+ return col_abs + col_str
+
+
+def xl_cell_to_rowcol(cell_str):
+ """
+ Convert a cell reference in A1 notation to a zero indexed row and column.
+
+ Args:
+ cell_str: A1 style string.
+
+ Returns:
+ row, col: Zero indexed cell row and column indices.
+
+ """
+ if not cell_str:
+ return 0, 0
+
+ match = range_parts.match(cell_str)
+ col_str = match.group(2)
+ row_str = match.group(4)
+
+ # Convert base26 column string to number.
+ expn = 0
+ col = 0
+ for char in reversed(col_str):
+ col += (ord(char) - ord('A') + 1) * (26 ** expn)
+ expn += 1
+
+ # Convert 1-index to zero-index
+ row = int(row_str) - 1
+ col -= 1
+
+ return row, col
+
+
+def xl_cell_to_rowcol_abs(cell_str):
+ """
+ Convert an absolute cell reference in A1 notation to a zero indexed
+ row and column, with True/False values for absolute rows or columns.
+
+ Args:
+ cell_str: A1 style string.
+
+ Returns:
+ row, col, row_abs, col_abs: Zero indexed cell row and column indices.
+
+ """
+ if not cell_str:
+ return 0, 0, False, False
+
+ match = range_parts.match(cell_str)
+
+ col_abs = match.group(1)
+ col_str = match.group(2)
+ row_abs = match.group(3)
+ row_str = match.group(4)
+
+ if col_abs:
+ col_abs = True
+ else:
+ col_abs = False
+
+ if row_abs:
+ row_abs = True
+ else:
+ row_abs = False
+
+ # Convert base26 column string to number.
+ expn = 0
+ col = 0
+ for char in reversed(col_str):
+ col += (ord(char) - ord('A') + 1) * (26 ** expn)
+ expn += 1
+
+ # Convert 1-index to zero-index
+ row = int(row_str) - 1
+ col -= 1
+
+ return row, col, row_abs, col_abs
+
+
+def xl_range(first_row, first_col, last_row, last_col):
+ """
+ Convert zero indexed row and col cell references to a A1:B1 range string.
+
+ Args:
+ first_row: The first cell row. Int.
+ first_col: The first cell column. Int.
+ last_row: The last cell row. Int.
+ last_col: The last cell column. Int.
+
+ Returns:
+ A1:B1 style range string.
+
+ """
+ range1 = xl_rowcol_to_cell(first_row, first_col)
+ range2 = xl_rowcol_to_cell(last_row, last_col)
+
+ return range1 + ':' + range2
+
+
+def xl_range_abs(first_row, first_col, last_row, last_col):
+ """
+ Convert zero indexed row and col cell references to a $A$1:$B$1 absolute
+ range string.
+
+ Args:
+ first_row: The first cell row. Int.
+ first_col: The first cell column. Int.
+ last_row: The last cell row. Int.
+ last_col: The last cell column. Int.
+
+ Returns:
+ $A$1:$B$1 style range string.
+
+ """
+ range1 = xl_rowcol_to_cell(first_row, first_col, True, True)
+ range2 = xl_rowcol_to_cell(last_row, last_col, True, True)
+
+ return range1 + ':' + range2
+
+
+def xl_range_formula(sheetname, first_row, first_col, last_row, last_col):
+ """
+ Convert worksheet name and zero indexed row and col cell references to
+ a Sheet1!A1:B1 range formula string.
+
+ Args:
+ sheetname: The worksheet name. String.
+ first_row: The first cell row. Int.
+ first_col: The first cell column. Int.
+ last_row: The last cell row. Int.
+ last_col: The last cell column. Int.
+
+ Returns:
+ A1:B1 style range string.
+
+ """
+ cell_range = xl_range_abs(first_row, first_col, last_row, last_col)
+ sheetname = quote_sheetname(sheetname)
+
+ return sheetname + '!' + cell_range
+
+
+def quote_sheetname(sheetname):
+ """
+ Convert a worksheet name to a quoted name if it contains spaces or
+ special characters.
+
+ Args:
+ sheetname: The worksheet name. String.
+
+ Returns:
+ A quoted worksheet string.
+
+ """
+
+ # TODO. Possibly extend this to quote sheetnames that look like ranges.
+ if not sheetname.isalnum() and not sheetname.startswith("'"):
+ # Double quote any single quotes.
+ sheetname = sheetname.replace("'", "''")
+
+ # Singe quote the sheet name.
+ sheetname = "'%s'" % sheetname
+
+ return sheetname
+
+
+def xl_color(color):
+ # Used in conjunction with the XlsxWriter *color() methods to convert
+ # a color name into an RGB formatted string. These colors are for
+ # backward compatibility with older versions of Excel.
+ named_colors = {
+ 'black': '#000000',
+ 'blue': '#0000FF',
+ 'brown': '#800000',
+ 'cyan': '#00FFFF',
+ 'gray': '#808080',
+ 'green': '#008000',
+ 'lime': '#00FF00',
+ 'magenta': '#FF00FF',
+ 'navy': '#000080',
+ 'orange': '#FF6600',
+ 'pink': '#FF00FF',
+ 'purple': '#800080',
+ 'red': '#FF0000',
+ 'silver': '#C0C0C0',
+ 'white': '#FFFFFF',
+ 'yellow': '#FFFF00',
+ }
+
+ if color in named_colors:
+ color = named_colors[color]
+
+ if not re.match('#[0-9a-fA-F]{6}', color):
+ warn("Color '%s' isn't a valid Excel color" % color)
+
+ # Convert the RGB color to the Excel ARGB format.
+ return "FF" + color.lstrip('#').upper()
+
+
+def get_rgb_color(color):
+ # Convert the user specified color to an RGB color.
+ rgb_color = xl_color(color)
+
+ # Remove leading FF from RGB color for charts.
+ rgb_color = re.sub(r'^FF', '', rgb_color)
+
+ return rgb_color
+
+
+def get_sparkline_style(style_id):
+ styles = [
+ {'series': {'theme': "4", 'tint': "-0.499984740745262"},
+ 'negative': {'theme': "5"},
+ 'markers': {'theme': "4", 'tint': "-0.499984740745262"},
+ 'first': {'theme': "4", 'tint': "0.39997558519241921"},
+ 'last': {'theme': "4", 'tint': "0.39997558519241921"},
+ 'high': {'theme': "4"},
+ 'low': {'theme': "4"},
+ }, # 0
+ {'series': {'theme': "4", 'tint': "-0.499984740745262"},
+ 'negative': {'theme': "5"},
+ 'markers': {'theme': "4", 'tint': "-0.499984740745262"},
+ 'first': {'theme': "4", 'tint': "0.39997558519241921"},
+ 'last': {'theme': "4", 'tint': "0.39997558519241921"},
+ 'high': {'theme': "4"},
+ 'low': {'theme': "4"},
+ }, # 1
+ {'series': {'theme': "5", 'tint': "-0.499984740745262"},
+ 'negative': {'theme': "6"},
+ 'markers': {'theme': "5", 'tint': "-0.499984740745262"},
+ 'first': {'theme': "5", 'tint': "0.39997558519241921"},
+ 'last': {'theme': "5", 'tint': "0.39997558519241921"},
+ 'high': {'theme': "5"},
+ 'low': {'theme': "5"},
+ }, # 2
+ {'series': {'theme': "6", 'tint': "-0.499984740745262"},
+ 'negative': {'theme': "7"},
+ 'markers': {'theme': "6", 'tint': "-0.499984740745262"},
+ 'first': {'theme': "6", 'tint': "0.39997558519241921"},
+ 'last': {'theme': "6", 'tint': "0.39997558519241921"},
+ 'high': {'theme': "6"},
+ 'low': {'theme': "6"},
+ }, # 3
+ {'series': {'theme': "7", 'tint': "-0.499984740745262"},
+ 'negative': {'theme': "8"},
+ 'markers': {'theme': "7", 'tint': "-0.499984740745262"},
+ 'first': {'theme': "7", 'tint': "0.39997558519241921"},
+ 'last': {'theme': "7", 'tint': "0.39997558519241921"},
+ 'high': {'theme': "7"},
+ 'low': {'theme': "7"},
+ }, # 4
+ {'series': {'theme': "8", 'tint': "-0.499984740745262"},
+ 'negative': {'theme': "9"},
+ 'markers': {'theme': "8", 'tint': "-0.499984740745262"},
+ 'first': {'theme': "8", 'tint': "0.39997558519241921"},
+ 'last': {'theme': "8", 'tint': "0.39997558519241921"},
+ 'high': {'theme': "8"},
+ 'low': {'theme': "8"},
+ }, # 5
+ {'series': {'theme': "9", 'tint': "-0.499984740745262"},
+ 'negative': {'theme': "4"},
+ 'markers': {'theme': "9", 'tint': "-0.499984740745262"},
+ 'first': {'theme': "9", 'tint': "0.39997558519241921"},
+ 'last': {'theme': "9", 'tint': "0.39997558519241921"},
+ 'high': {'theme': "9"},
+ 'low': {'theme': "9"},
+ }, # 6
+ {'series': {'theme': "4", 'tint': "-0.249977111117893"},
+ 'negative': {'theme': "5"},
+ 'markers': {'theme': "5", 'tint': "-0.249977111117893"},
+ 'first': {'theme': "5", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "5", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "5", 'tint': "-0.249977111117893"},
+ 'low': {'theme': "5", 'tint': "-0.249977111117893"},
+ }, # 7
+ {'series': {'theme': "5", 'tint': "-0.249977111117893"},
+ 'negative': {'theme': "6"},
+ 'markers': {'theme': "6", 'tint': "-0.249977111117893"},
+ 'first': {'theme': "6", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "6", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "6", 'tint': "-0.249977111117893"},
+ 'low': {'theme': "6", 'tint': "-0.249977111117893"},
+ }, # 8
+ {'series': {'theme': "6", 'tint': "-0.249977111117893"},
+ 'negative': {'theme': "7"},
+ 'markers': {'theme': "7", 'tint': "-0.249977111117893"},
+ 'first': {'theme': "7", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "7", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "7", 'tint': "-0.249977111117893"},
+ 'low': {'theme': "7", 'tint': "-0.249977111117893"},
+ }, # 9
+ {'series': {'theme': "7", 'tint': "-0.249977111117893"},
+ 'negative': {'theme': "8"},
+ 'markers': {'theme': "8", 'tint': "-0.249977111117893"},
+ 'first': {'theme': "8", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "8", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "8", 'tint': "-0.249977111117893"},
+ 'low': {'theme': "8", 'tint': "-0.249977111117893"},
+ }, # 10
+ {'series': {'theme': "8", 'tint': "-0.249977111117893"},
+ 'negative': {'theme': "9"},
+ 'markers': {'theme': "9", 'tint': "-0.249977111117893"},
+ 'first': {'theme': "9", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "9", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "9", 'tint': "-0.249977111117893"},
+ 'low': {'theme': "9", 'tint': "-0.249977111117893"},
+ }, # 11
+ {'series': {'theme': "9", 'tint': "-0.249977111117893"},
+ 'negative': {'theme': "4"},
+ 'markers': {'theme': "4", 'tint': "-0.249977111117893"},
+ 'first': {'theme': "4", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "4", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "4", 'tint': "-0.249977111117893"},
+ 'low': {'theme': "4", 'tint': "-0.249977111117893"},
+ }, # 12
+ {'series': {'theme': "4"},
+ 'negative': {'theme': "5"},
+ 'markers': {'theme': "4", 'tint': "-0.249977111117893"},
+ 'first': {'theme': "4", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "4", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "4", 'tint': "-0.249977111117893"},
+ 'low': {'theme': "4", 'tint': "-0.249977111117893"},
+ }, # 13
+ {'series': {'theme': "5"},
+ 'negative': {'theme': "6"},
+ 'markers': {'theme': "5", 'tint': "-0.249977111117893"},
+ 'first': {'theme': "5", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "5", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "5", 'tint': "-0.249977111117893"},
+ 'low': {'theme': "5", 'tint': "-0.249977111117893"},
+ }, # 14
+ {'series': {'theme': "6"},
+ 'negative': {'theme': "7"},
+ 'markers': {'theme': "6", 'tint': "-0.249977111117893"},
+ 'first': {'theme': "6", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "6", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "6", 'tint': "-0.249977111117893"},
+ 'low': {'theme': "6", 'tint': "-0.249977111117893"},
+ }, # 15
+ {'series': {'theme': "7"},
+ 'negative': {'theme': "8"},
+ 'markers': {'theme': "7", 'tint': "-0.249977111117893"},
+ 'first': {'theme': "7", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "7", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "7", 'tint': "-0.249977111117893"},
+ 'low': {'theme': "7", 'tint': "-0.249977111117893"},
+ }, # 16
+ {'series': {'theme': "8"},
+ 'negative': {'theme': "9"},
+ 'markers': {'theme': "8", 'tint': "-0.249977111117893"},
+ 'first': {'theme': "8", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "8", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "8", 'tint': "-0.249977111117893"},
+ 'low': {'theme': "8", 'tint': "-0.249977111117893"},
+ }, # 17
+ {'series': {'theme': "9"},
+ 'negative': {'theme': "4"},
+ 'markers': {'theme': "9", 'tint': "-0.249977111117893"},
+ 'first': {'theme': "9", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "9", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "9", 'tint': "-0.249977111117893"},
+ 'low': {'theme': "9", 'tint': "-0.249977111117893"},
+ }, # 18
+ {'series': {'theme': "4", 'tint': "0.39997558519241921"},
+ 'negative': {'theme': "0", 'tint': "-0.499984740745262"},
+ 'markers': {'theme': "4", 'tint': "0.79998168889431442"},
+ 'first': {'theme': "4", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "4", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "4", 'tint': "-0.499984740745262"},
+ 'low': {'theme': "4", 'tint': "-0.499984740745262"},
+ }, # 19
+ {'series': {'theme': "5", 'tint': "0.39997558519241921"},
+ 'negative': {'theme': "0", 'tint': "-0.499984740745262"},
+ 'markers': {'theme': "5", 'tint': "0.79998168889431442"},
+ 'first': {'theme': "5", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "5", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "5", 'tint': "-0.499984740745262"},
+ 'low': {'theme': "5", 'tint': "-0.499984740745262"},
+ }, # 20
+ {'series': {'theme': "6", 'tint': "0.39997558519241921"},
+ 'negative': {'theme': "0", 'tint': "-0.499984740745262"},
+ 'markers': {'theme': "6", 'tint': "0.79998168889431442"},
+ 'first': {'theme': "6", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "6", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "6", 'tint': "-0.499984740745262"},
+ 'low': {'theme': "6", 'tint': "-0.499984740745262"},
+ }, # 21
+ {'series': {'theme': "7", 'tint': "0.39997558519241921"},
+ 'negative': {'theme': "0", 'tint': "-0.499984740745262"},
+ 'markers': {'theme': "7", 'tint': "0.79998168889431442"},
+ 'first': {'theme': "7", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "7", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "7", 'tint': "-0.499984740745262"},
+ 'low': {'theme': "7", 'tint': "-0.499984740745262"},
+ }, # 22
+ {'series': {'theme': "8", 'tint': "0.39997558519241921"},
+ 'negative': {'theme': "0", 'tint': "-0.499984740745262"},
+ 'markers': {'theme': "8", 'tint': "0.79998168889431442"},
+ 'first': {'theme': "8", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "8", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "8", 'tint': "-0.499984740745262"},
+ 'low': {'theme': "8", 'tint': "-0.499984740745262"},
+ }, # 23
+ {'series': {'theme': "9", 'tint': "0.39997558519241921"},
+ 'negative': {'theme': "0", 'tint': "-0.499984740745262"},
+ 'markers': {'theme': "9", 'tint': "0.79998168889431442"},
+ 'first': {'theme': "9", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "9", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "9", 'tint': "-0.499984740745262"},
+ 'low': {'theme': "9", 'tint': "-0.499984740745262"},
+ }, # 24
+ {'series': {'theme': "1", 'tint': "0.499984740745262"},
+ 'negative': {'theme': "1", 'tint': "0.249977111117893"},
+ 'markers': {'theme': "1", 'tint': "0.249977111117893"},
+ 'first': {'theme': "1", 'tint': "0.249977111117893"},
+ 'last': {'theme': "1", 'tint': "0.249977111117893"},
+ 'high': {'theme': "1", 'tint': "0.249977111117893"},
+ 'low': {'theme': "1", 'tint': "0.249977111117893"},
+ }, # 25
+ {'series': {'theme': "1", 'tint': "0.34998626667073579"},
+ 'negative': {'theme': "0", 'tint': "-0.249977111117893"},
+ 'markers': {'theme': "0", 'tint': "-0.249977111117893"},
+ 'first': {'theme': "0", 'tint': "-0.249977111117893"},
+ 'last': {'theme': "0", 'tint': "-0.249977111117893"},
+ 'high': {'theme': "0", 'tint': "-0.249977111117893"},
+ 'low': {'theme': "0", 'tint': "-0.249977111117893"},
+ }, # 26
+ {'series': {'rgb': "FF323232"},
+ 'negative': {'rgb': "FFD00000"},
+ 'markers': {'rgb': "FFD00000"},
+ 'first': {'rgb': "FFD00000"},
+ 'last': {'rgb': "FFD00000"},
+ 'high': {'rgb': "FFD00000"},
+ 'low': {'rgb': "FFD00000"},
+ }, # 27
+ {'series': {'rgb': "FF000000"},
+ 'negative': {'rgb': "FF0070C0"},
+ 'markers': {'rgb': "FF0070C0"},
+ 'first': {'rgb': "FF0070C0"},
+ 'last': {'rgb': "FF0070C0"},
+ 'high': {'rgb': "FF0070C0"},
+ 'low': {'rgb': "FF0070C0"},
+ }, # 28
+ {'series': {'rgb': "FF376092"},
+ 'negative': {'rgb': "FFD00000"},
+ 'markers': {'rgb': "FFD00000"},
+ 'first': {'rgb': "FFD00000"},
+ 'last': {'rgb': "FFD00000"},
+ 'high': {'rgb': "FFD00000"},
+ 'low': {'rgb': "FFD00000"},
+ }, # 29
+ {'series': {'rgb': "FF0070C0"},
+ 'negative': {'rgb': "FF000000"},
+ 'markers': {'rgb': "FF000000"},
+ 'first': {'rgb': "FF000000"},
+ 'last': {'rgb': "FF000000"},
+ 'high': {'rgb': "FF000000"},
+ 'low': {'rgb': "FF000000"},
+ }, # 30
+ {'series': {'rgb': "FF5F5F5F"},
+ 'negative': {'rgb': "FFFFB620"},
+ 'markers': {'rgb': "FFD70077"},
+ 'first': {'rgb': "FF5687C2"},
+ 'last': {'rgb': "FF359CEB"},
+ 'high': {'rgb': "FF56BE79"},
+ 'low': {'rgb': "FFFF5055"},
+ }, # 31
+ {'series': {'rgb': "FF5687C2"},
+ 'negative': {'rgb': "FFFFB620"},
+ 'markers': {'rgb': "FFD70077"},
+ 'first': {'rgb': "FF777777"},
+ 'last': {'rgb': "FF359CEB"},
+ 'high': {'rgb': "FF56BE79"},
+ 'low': {'rgb': "FFFF5055"},
+ }, # 32
+ {'series': {'rgb': "FFC6EFCE"},
+ 'negative': {'rgb': "FFFFC7CE"},
+ 'markers': {'rgb': "FF8CADD6"},
+ 'first': {'rgb': "FFFFDC47"},
+ 'last': {'rgb': "FFFFEB9C"},
+ 'high': {'rgb': "FF60D276"},
+ 'low': {'rgb': "FFFF5367"},
+ }, # 33
+ {'series': {'rgb': "FF00B050"},
+ 'negative': {'rgb': "FFFF0000"},
+ 'markers': {'rgb': "FF0070C0"},
+ 'first': {'rgb': "FFFFC000"},
+ 'last': {'rgb': "FFFFC000"},
+ 'high': {'rgb': "FF00B050"},
+ 'low': {'rgb': "FFFF0000"},
+ }, # 34
+ {'series': {'theme': "3"},
+ 'negative': {'theme': "9"},
+ 'markers': {'theme': "8"},
+ 'first': {'theme': "4"},
+ 'last': {'theme': "5"},
+ 'high': {'theme': "6"},
+ 'low': {'theme': "7"},
+ }, # 35
+ {'series': {'theme': "1"},
+ 'negative': {'theme': "9"},
+ 'markers': {'theme': "8"},
+ 'first': {'theme': "4"},
+ 'last': {'theme': "5"},
+ 'high': {'theme': "6"},
+ 'low': {'theme': "7"},
+ }, # 36
+ ]
+
+ return styles[style_id]
+
+
+def supported_datetime(dt):
+ # Determine is an argument is a supported datetime object.
+ return(isinstance(dt, (datetime.datetime,
+ datetime.date,
+ datetime.time,
+ datetime.timedelta)))
+
+
+def remove_datetime_timezone(dt_obj, remove_timezone):
+ # Excel doesn't support timezones in datetimes/times so we remove the
+ # tzinfo from the object if the user has specified that option in the
+ # constructor.
+ if remove_timezone:
+ dt_obj = dt_obj.replace(tzinfo=None)
+ else:
+ if dt_obj.tzinfo:
+ raise TypeError(
+ "Excel doesn't support timezones in datetimes. "
+ "Set the tzinfo in the datetime/time object to None or "
+ "use the 'remove_timezone' Workbook() option")
+
+ return dt_obj
+
+
+def datetime_to_excel_datetime(dt_obj, date_1904, remove_timezone):
+ # Convert a datetime object to an Excel serial date and time. The integer
+ # part of the number stores the number of days since the epoch and the
+ # fractional part stores the percentage of the day.
+
+ if date_1904:
+ # Excel for Mac date epoch.
+ epoch = datetime.datetime(1904, 1, 1)
+ else:
+ # Default Excel epoch.
+ epoch = datetime.datetime(1899, 12, 31)
+
+ # We handle datetime .datetime, .date and .time objects but convert
+ # them to datetime.datetime objects and process them in the same way.
+ if isinstance(dt_obj, datetime.datetime):
+ dt_obj = remove_datetime_timezone(dt_obj, remove_timezone)
+ delta = dt_obj - epoch
+ elif isinstance(dt_obj, datetime.date):
+ dt_obj = datetime.datetime.fromordinal(dt_obj.toordinal())
+ delta = dt_obj - epoch
+ elif isinstance(dt_obj, datetime.time):
+ dt_obj = datetime.datetime.combine(epoch, dt_obj)
+ dt_obj = remove_datetime_timezone(dt_obj, remove_timezone)
+ delta = dt_obj - epoch
+ elif isinstance(dt_obj, datetime.timedelta):
+ delta = dt_obj
+ else:
+ raise TypeError("Unknown or unsupported datetime type")
+
+ # Convert a Python datetime.datetime value to an Excel date number.
+ excel_time = (delta.days
+ + (float(delta.seconds)
+ + float(delta.microseconds) / 1E6)
+ / (60 * 60 * 24))
+
+ # Special case for datetime where time only has been specified and
+ # the default date of 1900-01-01 is used.
+ if (not isinstance(dt_obj, datetime.timedelta)
+ and dt_obj.isocalendar() == (1900, 1, 1)):
+ excel_time -= 1
+
+ # Account for Excel erroneously treating 1900 as a leap year.
+ if not date_1904 and excel_time > 59:
+ excel_time += 1
+
+ return excel_time
diff --git a/xlsxwriter/vml.py b/xlsxwriter/vml.py
new file mode 100644
index 0000000..22e859d
--- /dev/null
+++ b/xlsxwriter/vml.py
@@ -0,0 +1,706 @@
+###############################################################################
+#
+# Vml - A class for writing the Excel XLSX Vml file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Package imports.
+from . import xmlwriter
+
+
+class Vml(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX Vml file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(Vml, self).__init__()
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+ def _assemble_xml_file(self, data_id, vml_shape_id, comments_data=None,
+ buttons_data=None, header_images_data=None):
+ # Assemble and write the XML file.
+ z_index = 1
+
+ self._write_xml_namespace()
+
+ # Write the o:shapelayout element.
+ self._write_shapelayout(data_id)
+
+ if buttons_data:
+ # Write the v:shapetype element.
+ self._write_button_shapetype()
+
+ for button in buttons_data:
+ # Write the v:shape element.
+ vml_shape_id += 1
+ self._write_button_shape(vml_shape_id, z_index, button)
+ z_index += 1
+
+ if comments_data:
+ # Write the v:shapetype element.
+ self._write_comment_shapetype()
+
+ for comment in comments_data:
+ # Write the v:shape element.
+ vml_shape_id += 1
+ self._write_comment_shape(vml_shape_id, z_index, comment)
+ z_index += 1
+
+ if header_images_data:
+
+ # Write the v:shapetype element.
+ self._write_image_shapetype()
+
+ index = 1
+ for image in header_images_data:
+ # Write the v:shape element.
+ vml_shape_id += 1
+ self._write_image_shape(vml_shape_id, index, image)
+ index += 1
+
+ self._xml_end_tag('xml')
+
+ # Close the XML writer filehandle.
+ self._xml_close()
+
+ def _pixels_to_points(self, vertices):
+ # Convert comment vertices from pixels to points.
+
+ left, top, width, height = vertices[8:12]
+
+ # Scale to pixels.
+ left *= 0.75
+ top *= 0.75
+ width *= 0.75
+ height *= 0.75
+
+ return left, top, width, height
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+ def _write_xml_namespace(self):
+ # Write the <xml> element. This is the root element of VML.
+ schema = 'urn:schemas-microsoft-com:'
+ xmlns = schema + 'vml'
+ xmlns_o = schema + 'office:office'
+ xmlns_x = schema + 'office:excel'
+
+ attributes = [
+ ('xmlns:v', xmlns),
+ ('xmlns:o', xmlns_o),
+ ('xmlns:x', xmlns_x),
+ ]
+
+ self._xml_start_tag('xml', attributes)
+
+ def _write_shapelayout(self, data_id):
+ # Write the <o:shapelayout> element.
+ attributes = [('v:ext', 'edit')]
+
+ self._xml_start_tag('o:shapelayout', attributes)
+
+ # Write the o:idmap element.
+ self._write_idmap(data_id)
+
+ self._xml_end_tag('o:shapelayout')
+
+ def _write_idmap(self, data_id):
+ # Write the <o:idmap> element.
+ attributes = [
+ ('v:ext', 'edit'),
+ ('data', data_id),
+ ]
+
+ self._xml_empty_tag('o:idmap', attributes)
+
+ def _write_comment_shapetype(self):
+ # Write the <v:shapetype> element.
+ shape_id = '_x0000_t202'
+ coordsize = '21600,21600'
+ spt = 202
+ path = 'm,l,21600r21600,l21600,xe'
+
+ attributes = [
+ ('id', shape_id),
+ ('coordsize', coordsize),
+ ('o:spt', spt),
+ ('path', path),
+ ]
+
+ self._xml_start_tag('v:shapetype', attributes)
+
+ # Write the v:stroke element.
+ self._write_stroke()
+
+ # Write the v:path element.
+ self._write_comment_path('t', 'rect')
+
+ self._xml_end_tag('v:shapetype')
+
+ def _write_button_shapetype(self):
+ # Write the <v:shapetype> element.
+ shape_id = '_x0000_t201'
+ coordsize = '21600,21600'
+ spt = 201
+ path = 'm,l,21600r21600,l21600,xe'
+
+ attributes = [
+ ('id', shape_id),
+ ('coordsize', coordsize),
+ ('o:spt', spt),
+ ('path', path),
+ ]
+
+ self._xml_start_tag('v:shapetype', attributes)
+
+ # Write the v:stroke element.
+ self._write_stroke()
+
+ # Write the v:path element.
+ self._write_button_path()
+
+ # Write the o:lock element.
+ self._write_shapetype_lock()
+
+ self._xml_end_tag('v:shapetype')
+
+ def _write_image_shapetype(self):
+ # Write the <v:shapetype> element.
+ shape_id = '_x0000_t75'
+ coordsize = '21600,21600'
+ spt = 75
+ o_preferrelative = 't'
+ path = 'm at 4@5l at 4@11 at 9@11 at 9@5xe'
+ filled = 'f'
+ stroked = 'f'
+
+ attributes = [
+ ('id', shape_id),
+ ('coordsize', coordsize),
+ ('o:spt', spt),
+ ('o:preferrelative', o_preferrelative),
+ ('path', path),
+ ('filled', filled),
+ ('stroked', stroked),
+ ]
+
+ self._xml_start_tag('v:shapetype', attributes)
+
+ # Write the v:stroke element.
+ self._write_stroke()
+
+ # Write the v:formulas element.
+ self._write_formulas()
+
+ # Write the v:path element.
+ self._write_image_path()
+
+ # Write the o:lock element.
+ self._write_aspect_ratio_lock()
+
+ self._xml_end_tag('v:shapetype')
+
+ def _write_stroke(self):
+ # Write the <v:stroke> element.
+ joinstyle = 'miter'
+
+ attributes = [('joinstyle', joinstyle)]
+
+ self._xml_empty_tag('v:stroke', attributes)
+
+ def _write_comment_path(self, gradientshapeok, connecttype):
+ # Write the <v:path> element.
+ attributes = []
+
+ if gradientshapeok:
+ attributes.append(('gradientshapeok', 't'))
+
+ attributes.append(('o:connecttype', connecttype))
+
+ self._xml_empty_tag('v:path', attributes)
+
+ def _write_button_path(self):
+ # Write the <v:path> element.
+ shadowok = 'f'
+ extrusionok = 'f'
+ strokeok = 'f'
+ fillok = 'f'
+ connecttype = 'rect'
+
+ attributes = [
+ ('shadowok', shadowok),
+ ('o:extrusionok', extrusionok),
+ ('strokeok', strokeok),
+ ('fillok', fillok),
+ ('o:connecttype', connecttype),
+ ]
+
+ self._xml_empty_tag('v:path', attributes)
+
+ def _write_image_path(self):
+ # Write the <v:path> element.
+ extrusionok = 'f'
+ gradientshapeok = 't'
+ connecttype = 'rect'
+
+ attributes = [
+ ('o:extrusionok', extrusionok),
+ ('gradientshapeok', gradientshapeok),
+ ('o:connecttype', connecttype),
+ ]
+
+ self._xml_empty_tag('v:path', attributes)
+
+ def _write_shapetype_lock(self):
+ # Write the <o:lock> element.
+ ext = 'edit'
+ shapetype = 't'
+
+ attributes = [
+ ('v:ext', ext),
+ ('shapetype', shapetype),
+ ]
+
+ self._xml_empty_tag('o:lock', attributes)
+
+ def _write_rotation_lock(self):
+ # Write the <o:lock> element.
+ ext = 'edit'
+ rotation = 't'
+
+ attributes = [
+ ('v:ext', ext),
+ ('rotation', rotation),
+ ]
+
+ self._xml_empty_tag('o:lock', attributes)
+
+ def _write_aspect_ratio_lock(self):
+ # Write the <o:lock> element.
+ ext = 'edit'
+ aspectratio = 't'
+
+ attributes = [
+ ('v:ext', ext),
+ ('aspectratio', aspectratio),
+ ]
+
+ self._xml_empty_tag('o:lock', attributes)
+
+ def _write_comment_shape(self, shape_id, z_index, comment):
+ # Write the <v:shape> element.
+ shape_type = '#_x0000_t202'
+ insetmode = 'auto'
+ visibility = 'hidden'
+
+ # Set the shape index.
+ shape_id = '_x0000_s' + str(shape_id)
+
+ # Get the comment parameters
+ row = comment[0]
+ col = comment[1]
+ visible = comment[4]
+ fillcolor = comment[5]
+ vertices = comment[6]
+
+ (left, top, width, height) = self._pixels_to_points(vertices)
+
+ # Set the visibility.
+ if visible:
+ visibility = 'visible'
+
+ style = (
+ 'position:absolute;'
+ 'margin-left:%.15gpt;'
+ 'margin-top:%.15gpt;'
+ 'width:%.15gpt;'
+ 'height:%.15gpt;'
+ 'z-index:%d;'
+ 'visibility:%s' % (left, top, width, height, z_index, visibility))
+
+ attributes = [
+ ('id', shape_id),
+ ('type', shape_type),
+ ('style', style),
+ ('fillcolor', fillcolor),
+ ('o:insetmode', insetmode),
+ ]
+
+ self._xml_start_tag('v:shape', attributes)
+
+ # Write the v:fill element.
+ self._write_comment_fill()
+
+ # Write the v:shadow element.
+ self._write_shadow()
+
+ # Write the v:path element.
+ self._write_comment_path(None, 'none')
+
+ # Write the v:textbox element.
+ self._write_comment_textbox()
+
+ # Write the x:ClientData element.
+ self._write_comment_client_data(row, col, visible, vertices)
+
+ self._xml_end_tag('v:shape')
+
+ def _write_button_shape(self, shape_id, z_index, button):
+ # Write the <v:shape> element.
+ shape_type = '#_x0000_t201'
+
+ # Set the shape index.
+ shape_id = '_x0000_s' + str(shape_id)
+
+ # Get the button parameters.
+ # row = button["_row"]
+ # col = button["_col"]
+ vertices = button["vertices"]
+
+ (left, top, width, height) = self._pixels_to_points(vertices)
+
+ style = (
+ 'position:absolute;'
+ 'margin-left:%.15gpt;'
+ 'margin-top:%.15gpt;'
+ 'width:%.15gpt;'
+ 'height:%.15gpt;'
+ 'z-index:%d;'
+ 'mso-wrap-style:tight' % (left, top, width, height, z_index))
+
+ attributes = [
+ ('id', shape_id),
+ ('type', shape_type),
+ ('style', style),
+ ('o:button', 't'),
+ ('fillcolor', 'buttonFace [67]'),
+ ('strokecolor', 'windowText [64]'),
+ ('o:insetmode', 'auto'),
+ ]
+
+ self._xml_start_tag('v:shape', attributes)
+
+ # Write the v:fill element.
+ self._write_button_fill()
+
+ # Write the o:lock element.
+ self._write_rotation_lock()
+
+ # Write the v:textbox element.
+ self._write_button_textbox(button["font"])
+
+ # Write the x:ClientData element.
+ self._write_button_client_data(button)
+
+ self._xml_end_tag('v:shape')
+
+ def _write_image_shape(self, shape_id, z_index, image_data):
+ # Write the <v:shape> element.
+ shape_type = '#_x0000_t75'
+
+ # Set the shape index.
+ shape_id = '_x0000_s' + str(shape_id)
+
+ # Get the image parameters
+ width = image_data[0]
+ height = image_data[1]
+ name = image_data[2]
+ position = image_data[3]
+ x_dpi = image_data[4]
+ y_dpi = image_data[5]
+
+ # Scale the height/width by the resolution, relative to 72dpi.
+ width = width * 72.0 / x_dpi
+ height = height * 72.0 / y_dpi
+
+ # Excel uses a rounding based around 72 and 96 dpi.
+ width = 72.0 / 96 * int(width * 96.0 / 72 + 0.25)
+ height = 72.0 / 96 * int(height * 96.0 / 72 + 0.25)
+
+ style = (
+ 'position:absolute;'
+ 'margin-left:0;'
+ 'margin-top:0;'
+ 'width:%.15gpt;'
+ 'height:%.15gpt;'
+ 'z-index:%d' % (width, height, z_index))
+
+ attributes = [
+ ('id', position),
+ ('o:spid', shape_id),
+ ('type', shape_type),
+ ('style', style),
+ ]
+
+ self._xml_start_tag('v:shape', attributes)
+
+ # Write the v:imagedata element.
+ self._write_imagedata(z_index, name)
+
+ # Write the o:lock element.
+ self._write_rotation_lock()
+
+ self._xml_end_tag('v:shape')
+
+ def _write_comment_fill(self):
+ # Write the <v:fill> element.
+ color_2 = '#ffffe1'
+
+ attributes = [('color2', color_2)]
+
+ self._xml_empty_tag('v:fill', attributes)
+
+ def _write_button_fill(self):
+ # Write the <v:fill> element.
+ color_2 = 'buttonFace [67]'
+ detectmouseclick = 't'
+
+ attributes = [
+ ('color2', color_2),
+ ('o:detectmouseclick', detectmouseclick),
+ ]
+
+ self._xml_empty_tag('v:fill', attributes)
+
+ def _write_shadow(self):
+ # Write the <v:shadow> element.
+ on = 't'
+ color = 'black'
+ obscured = 't'
+
+ attributes = [
+ ('on', on),
+ ('color', color),
+ ('obscured', obscured),
+ ]
+
+ self._xml_empty_tag('v:shadow', attributes)
+
+ def _write_comment_textbox(self):
+ # Write the <v:textbox> element.
+ style = 'mso-direction-alt:auto'
+
+ attributes = [('style', style)]
+
+ self._xml_start_tag('v:textbox', attributes)
+
+ # Write the div element.
+ self._write_div('left')
+
+ self._xml_end_tag('v:textbox')
+
+ def _write_button_textbox(self, font):
+ # Write the <v:textbox> element.
+ style = 'mso-direction-alt:auto'
+
+ attributes = [('style', style), ('o:singleclick', 'f')]
+
+ self._xml_start_tag('v:textbox', attributes)
+
+ # Write the div element.
+ self._write_div('center', font)
+
+ self._xml_end_tag('v:textbox')
+
+ def _write_div(self, align, font=None):
+ # Write the <div> element.
+
+ style = 'text-align:' + align
+
+ attributes = [('style', style)]
+
+ self._xml_start_tag('div', attributes)
+
+ if font:
+ # Write the font element.
+ self._write_font(font)
+
+ self._xml_end_tag('div')
+
+ def _write_font(self, font):
+ # Write the <font> element.
+ caption = font["caption"]
+ face = 'Calibri'
+ size = 220
+ color = '#000000'
+
+ attributes = [
+ ('face', face),
+ ('size', size),
+ ('color', color),
+ ]
+
+ self._xml_data_element('font', caption, attributes)
+
+ def _write_comment_client_data(self, row, col, visible, vertices):
+ # Write the <x:ClientData> element.
+ object_type = 'Note'
+
+ attributes = [('ObjectType', object_type)]
+
+ self._xml_start_tag('x:ClientData', attributes)
+
+ # Write the x:MoveWithCells element.
+ self._write_move_with_cells()
+
+ # Write the x:SizeWithCells element.
+ self._write_size_with_cells()
+
+ # Write the x:Anchor element.
+ self._write_anchor(vertices)
+
+ # Write the x:AutoFill element.
+ self._write_auto_fill()
+
+ # Write the x:Row element.
+ self._write_row(row)
+
+ # Write the x:Column element.
+ self._write_column(col)
+
+ # Write the x:Visible element.
+ if visible:
+ self._write_visible()
+
+ self._xml_end_tag('x:ClientData')
+
+ def _write_button_client_data(self, button):
+ # Write the <x:ClientData> element.
+ macro = button["macro"]
+ vertices = button["vertices"]
+
+ object_type = 'Button'
+
+ attributes = [('ObjectType', object_type)]
+
+ self._xml_start_tag('x:ClientData', attributes)
+
+ # Write the x:Anchor element.
+ self._write_anchor(vertices)
+
+ # Write the x:PrintObject element.
+ self._write_print_object()
+
+ # Write the x:AutoFill element.
+ self._write_auto_fill()
+
+ # Write the x:FmlaMacro element.
+ self._write_fmla_macro(macro)
+
+ # Write the x:TextHAlign element.
+ self._write_text_halign()
+
+ # Write the x:TextVAlign element.
+ self._write_text_valign()
+
+ self._xml_end_tag('x:ClientData')
+
+ def _write_move_with_cells(self):
+ # Write the <x:MoveWithCells> element.
+ self._xml_empty_tag('x:MoveWithCells')
+
+ def _write_size_with_cells(self):
+ # Write the <x:SizeWithCells> element.
+ self._xml_empty_tag('x:SizeWithCells')
+
+ def _write_visible(self):
+ # Write the <x:Visible> element.
+ self._xml_empty_tag('x:Visible')
+
+ def _write_anchor(self, vertices):
+ # Write the <x:Anchor> element.
+ (col_start, row_start, x1, y1, col_end, row_end, x2, y2) = vertices[:8]
+
+ strings = [col_start, x1, row_start, y1, col_end, x2, row_end, y2]
+ strings = [str(i) for i in strings]
+
+ data = ", ".join(strings)
+
+ self._xml_data_element('x:Anchor', data)
+
+ def _write_auto_fill(self):
+ # Write the <x:AutoFill> element.
+ data = 'False'
+
+ self._xml_data_element('x:AutoFill', data)
+
+ def _write_row(self, data):
+ # Write the <x:Row> element.
+ self._xml_data_element('x:Row', data)
+
+ def _write_column(self, data):
+ # Write the <x:Column> element.
+ self._xml_data_element('x:Column', data)
+
+ def _write_print_object(self):
+ # Write the <x:PrintObject> element.
+ self._xml_data_element('x:PrintObject', 'False')
+
+ def _write_text_halign(self):
+ # Write the <x:TextHAlign> element.
+ self._xml_data_element('x:TextHAlign', 'Center')
+
+ def _write_text_valign(self):
+ # Write the <x:TextVAlign> element.
+ self._xml_data_element('x:TextVAlign', 'Center')
+
+ def _write_fmla_macro(self, data):
+ # Write the <x:FmlaMacro> element.
+ self._xml_data_element('x:FmlaMacro', data)
+
+ def _write_imagedata(self, image_index, o_title):
+ # Write the <v:imagedata> element.
+ attributes = [
+ ('o:relid', 'rId' + str(image_index)),
+ ('o:title', o_title),
+ ]
+
+ self._xml_empty_tag('v:imagedata', attributes)
+
+ def _write_formulas(self):
+ # Write the <v:formulas> element.
+ self._xml_start_tag('v:formulas')
+
+ # Write the v:f elements.
+ self._write_formula('if lineDrawn pixelLineWidth 0')
+ self._write_formula('sum @0 1 0')
+ self._write_formula('sum 0 0 @1')
+ self._write_formula('prod @2 1 2')
+ self._write_formula('prod @3 21600 pixelWidth')
+ self._write_formula('prod @3 21600 pixelHeight')
+ self._write_formula('sum @0 0 1')
+ self._write_formula('prod @6 1 2')
+ self._write_formula('prod @7 21600 pixelWidth')
+ self._write_formula('sum @8 21600 0')
+ self._write_formula('prod @7 21600 pixelHeight')
+ self._write_formula('sum @10 21600 0')
+
+ self._xml_end_tag('v:formulas')
+
+ def _write_formula(self, eqn):
+ # Write the <v:f> element.
+ attributes = [('eqn', eqn)]
+
+ self._xml_empty_tag('v:f', attributes)
diff --git a/xlsxwriter/workbook.py b/xlsxwriter/workbook.py
new file mode 100644
index 0000000..c709e79
--- /dev/null
+++ b/xlsxwriter/workbook.py
@@ -0,0 +1,1637 @@
+###############################################################################
+#
+# Workbook - A class for writing the Excel XLSX Workbook file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Standard packages.
+import sys
+import re
+import os
+import operator
+from warnings import warn
+from datetime import datetime
+from zipfile import ZipFile, ZIP_DEFLATED
+from struct import unpack
+
+from .compatibility import int_types, num_types, str_types, force_unicode
+
+# Package imports.
+from . import xmlwriter
+from .worksheet import Worksheet
+from .chartsheet import Chartsheet
+from .sharedstrings import SharedStringTable
+from .format import Format
+from .packager import Packager
+from .utility import xl_cell_to_rowcol
+from .chart_area import ChartArea
+from .chart_bar import ChartBar
+from .chart_column import ChartColumn
+from .chart_doughnut import ChartDoughnut
+from .chart_line import ChartLine
+from .chart_pie import ChartPie
+from .chart_radar import ChartRadar
+from .chart_scatter import ChartScatter
+from .chart_stock import ChartStock
+
+
+class Workbook(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX Workbook file.
+
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self, filename=None, options={}):
+ """
+ Constructor.
+
+ """
+
+ super(Workbook, self).__init__()
+
+ self.filename = filename
+
+ self.tmpdir = options.get('tmpdir', None)
+ self.date_1904 = options.get('date_1904', False)
+ self.strings_to_numbers = options.get('strings_to_numbers', False)
+ self.strings_to_formulas = options.get('strings_to_formulas', True)
+ self.strings_to_urls = options.get('strings_to_urls', True)
+ self.nan_inf_to_errors = options.get('nan_inf_to_errors', False)
+ self.default_date_format = options.get('default_date_format', None)
+ self.optimization = options.get('constant_memory', False)
+ self.in_memory = options.get('in_memory', False)
+ self.excel2003_style = options.get('excel2003_style', False)
+ self.remove_timezone = options.get('remove_timezone', False)
+ self.default_format_properties = \
+ options.get('default_format_properties', {})
+
+ self.worksheet_meta = WorksheetMeta()
+ self.selected = 0
+ self.fileclosed = 0
+ self.filehandle = None
+ self.internal_fh = 0
+ self.sheet_name = 'Sheet'
+ self.chart_name = 'Chart'
+ self.sheetname_count = 0
+ self.chartname_count = 0
+ self.worksheets_objs = []
+ self.charts = []
+ self.drawings = []
+ self.sheetnames = {}
+ self.formats = []
+ self.xf_formats = []
+ self.xf_format_indices = {}
+ self.dxf_formats = []
+ self.dxf_format_indices = {}
+ self.palette = []
+ self.font_count = 0
+ self.num_format_count = 0
+ self.defined_names = []
+ self.named_ranges = []
+ self.custom_colors = []
+ self.doc_properties = {}
+ self.custom_properties = []
+ self.createtime = datetime.utcnow()
+ self.num_vml_files = 0
+ self.num_comment_files = 0
+ self.x_window = 240
+ self.y_window = 15
+ self.window_width = 16095
+ self.window_height = 9660
+ self.tab_ratio = 500
+ self.str_table = SharedStringTable()
+ self.vba_project = None
+ self.vba_is_stream = False
+ self.vba_codename = None
+ self.image_types = {}
+ self.images = []
+ self.border_count = 0
+ self.fill_count = 0
+ self.drawing_count = 0
+ self.calc_mode = "auto"
+ self.calc_on_load = True
+ self.allow_zip64 = False
+ self.calc_id = 124519
+
+ # We can't do 'constant_memory' mode while doing 'in_memory' mode.
+ if self.in_memory:
+ self.optimization = False
+
+ # Add the default cell format.
+ if self.excel2003_style:
+ self.add_format({'xf_index': 0, 'font_family': 0})
+ else:
+ self.add_format({'xf_index': 0})
+
+ # Add a default URL format.
+ self.default_url_format = self.add_format({'color': 'blue',
+ 'underline': 1})
+
+ # Add the default date format.
+ if self.default_date_format is not None:
+ self.default_date_format = \
+ self.add_format({'num_format': self.default_date_format})
+
+ def __del__(self):
+ """Close file in destructor if it hasn't been closed explicitly."""
+ try:
+ if not self.fileclosed:
+ self.close()
+ except:
+ raise Exception("Exception caught in workbook destructor. "
+ "Explicit close() may be required for workbook.")
+
+ def __enter__(self):
+ """Return self object to use with "with" statement."""
+ return self
+
+ def __exit__(self, type, value, traceback):
+ """Close workbook when exiting "with" statement."""
+ self.close()
+
+ def add_worksheet(self, name=None):
+ """
+ Add a new worksheet to the Excel workbook.
+
+ Args:
+ name: The worksheet name. Defaults to 'Sheet1', etc.
+
+ Returns:
+ Reference to a worksheet object.
+
+ """
+ return self._add_sheet(name, is_chartsheet=False)
+
+ def add_chartsheet(self, name=None):
+ """
+ Add a new chartsheet to the Excel workbook.
+
+ Args:
+ name: The chartsheet name. Defaults to 'Sheet1', etc.
+
+ Returns:
+ Reference to a chartsheet object.
+
+ """
+ return self._add_sheet(name, is_chartsheet=True)
+
+ def add_format(self, properties={}):
+ """
+ Add a new Format to the Excel Workbook.
+
+ Args:
+ properties: The format properties.
+
+ Returns:
+ Reference to a Format object.
+
+ """
+ format_properties = self.default_format_properties.copy()
+
+ if self.excel2003_style:
+ format_properties = {'font_name': 'Arial', 'font_size': 10,
+ 'theme': 1 * -1}
+
+ format_properties.update(properties)
+
+ xf_format = Format(format_properties,
+ self.xf_format_indices,
+ self.dxf_format_indices)
+
+ # Store the format reference.
+ self.formats.append(xf_format)
+
+ return xf_format
+
+ def add_chart(self, options):
+ """
+ Create a chart object.
+
+ Args:
+ options: The chart type and subtype options.
+
+ Returns:
+ Reference to a Chart object.
+
+ """
+
+ # Type must be specified so we can create the required chart instance.
+ chart_type = options.get('type')
+ if chart_type is None:
+ warn("Chart type must be defined in add_chart()")
+ return
+
+ if chart_type == 'area':
+ chart = ChartArea(options)
+ elif chart_type == 'bar':
+ chart = ChartBar(options)
+ elif chart_type == 'column':
+ chart = ChartColumn(options)
+ elif chart_type == 'doughnut':
+ chart = ChartDoughnut(options)
+ elif chart_type == 'line':
+ chart = ChartLine(options)
+ elif chart_type == 'pie':
+ chart = ChartPie(options)
+ elif chart_type == 'radar':
+ chart = ChartRadar(options)
+ elif chart_type == 'scatter':
+ chart = ChartScatter(options)
+ elif chart_type == 'stock':
+ chart = ChartStock(options)
+ else:
+ warn("Unknown chart type '%s' in add_chart()" % chart_type)
+ return
+
+ # Set the embedded chart name if present.
+ if 'name' in options:
+ chart.chart_name = options['name']
+
+ chart.embedded = True
+ chart.date_1904 = self.date_1904
+ chart.remove_timezone = self.remove_timezone
+
+ self.charts.append(chart)
+
+ return chart
+
+ def add_vba_project(self, vba_project, is_stream=False):
+ """
+ Add a vbaProject binary to the Excel workbook.
+
+ Args:
+ vba_project: The vbaProject binary file name.
+ is_stream: vba_project is an in memory byte stream.
+
+ Returns:
+ Nothing.
+
+ """
+ if not is_stream and not os.path.exists(vba_project):
+ warn("VBA project binary file '%s' not found."
+ % force_unicode(vba_project))
+ return -1
+
+ self.vba_project = vba_project
+ self.vba_is_stream = is_stream
+
+ def close(self):
+ """
+ Call finalization code and close file.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ if not self.fileclosed:
+ self.fileclosed = 1
+ self._store_workbook()
+
+ def set_size(self, width, height):
+ """
+ Set the size of a workbook window.
+
+ Args:
+ width: Width of the window in pixels.
+ height: Height of the window in pixels.
+
+ Returns:
+ Nothing.
+
+ """
+ # Convert the width/height to twips at 96 dpi.
+ if width:
+ self.window_width = int(width * 1440 / 96)
+ else:
+ self.window_width = 16095
+
+ if height:
+ self.window_height = int(height * 1440 / 96)
+ else:
+ self.window_height = 9660
+
+ def set_properties(self, properties):
+ """
+ Set the document properties such as Title, Author etc.
+
+ Args:
+ properties: Dictionary of document properties.
+
+ Returns:
+ Nothing.
+
+ """
+ self.doc_properties = properties
+
+ def set_custom_property(self, name, value, property_type=None):
+ """
+ Set a custom document property.
+
+ Args:
+ name: The name of the custom property.
+ value: The value of the custom property.
+ property_type: The type of the custom property. Optional.
+
+ Returns:
+ Nothing.
+
+ """
+ if name is None or value is None:
+ warn("The name and value parameters must be non-None in "
+ "set_custom_property()")
+ return -1
+
+ if property_type is None:
+ # Determine the property type from the Python type.
+ if isinstance(value, bool):
+ property_type = 'bool'
+ elif isinstance(value, datetime):
+ property_type = 'date'
+ elif isinstance(value, int_types):
+ property_type = 'number_int'
+ elif isinstance(value, num_types):
+ property_type = 'number'
+ else:
+ property_type = 'text'
+
+ if property_type == 'date':
+ value = value.strftime("%Y-%m-%dT%H:%M:%SZ")
+
+ if property_type == 'text' and len(value) > 255:
+ warn("Length of 'value' parameter exceeds Excel's limit of 255 "
+ "characters in set_custom_property(): '%s'" %
+ force_unicode(value))
+
+ if len(name) > 255:
+ warn("Length of 'name' parameter exceeds Excel's limit of 255 "
+ "characters in set_custom_property(): '%s'" %
+ force_unicode(name))
+
+ self.custom_properties.append((name, value, property_type))
+
+ def set_calc_mode(self, mode, calc_id=None):
+ """
+ Set the Excel calculation mode for the workbook.
+
+ Args:
+ mode: String containing one of:
+ * manual
+ * auto_except_tables
+ * auto
+
+ Returns:
+ Nothing.
+
+ """
+ self.calc_mode = mode
+
+ if mode == 'manual':
+ self.calc_on_load = False
+ elif mode == 'auto_except_tables':
+ self.calc_mode = 'autoNoTable'
+
+ # Leave undocumented for now. Rarely required.
+ if calc_id:
+ self.calc_id = calc_id
+
+ def define_name(self, name, formula):
+ # Create a defined name in Excel. We handle global/workbook level
+ # names and local/worksheet names.
+ """
+ Create a defined name in the workbook.
+
+ Args:
+ name: The defined name.
+ formula: The cell or range that the defined name refers to.
+
+ Returns:
+ Nothing.
+
+ """
+ sheet_index = None
+ sheetname = ''
+
+ # Remove the = sign from the formula if it exists.
+ if formula.startswith('='):
+ formula = formula.lstrip('=')
+
+ # Local defined names are formatted like "Sheet1!name".
+ sheet_parts = re.compile(r'^(.*)!(.*)$')
+ match = sheet_parts.match(name)
+
+ if match:
+ sheetname = match.group(1)
+ name = match.group(2)
+ sheet_index = self._get_sheet_index(sheetname)
+
+ # Warn if the sheet index wasn't found.
+ if sheet_index is None:
+ warn("Unknown sheet name '%s' in defined_name()"
+ % force_unicode(sheetname))
+ return -1
+ else:
+ # Use -1 to indicate global names.
+ sheet_index = -1
+
+ # Warn if the defined name contains invalid chars as defined by Excel.
+ if (not re.match(r'^[\w\\][\w\\.]*$', name, re.UNICODE)
+ or re.match(r'^\d', name)):
+ warn("Invalid Excel characters in defined_name(): '%s'"
+ % force_unicode(name))
+ return -1
+
+ # Warn if the defined name looks like a cell name.
+ if re.match(r'^[a-zA-Z][a-zA-Z]?[a-dA-D]?[0-9]+$', name):
+ warn("Name looks like a cell name in defined_name(): '%s'"
+ % force_unicode(name))
+ return -1
+
+ # Warn if the name looks like a R1C1 cell reference.
+ if (re.match(r'^[rcRC]$', name)
+ or re.match(r'^[rcRC]\d+[rcRC]\d+$', name)):
+ warn("Invalid name '%s' like a RC cell ref in defined_name()"
+ % force_unicode(name))
+ return -1
+
+ self.defined_names.append([name, sheet_index, formula, False])
+
+ def worksheets(self):
+ """
+ Return a list of the worksheet objects in the workbook.
+
+ Args:
+ None.
+
+ Returns:
+ A list of worksheet objects.
+
+ """
+ return self.worksheets_objs
+
+ def get_worksheet_by_name(self, name):
+ """
+ Return a worksheet object in the workbook using the sheetname.
+
+ Args:
+ name: The name of the worksheet.
+
+ Returns:
+ A worksheet object or None.
+
+ """
+ return self.sheetnames.get(name)
+
+ def use_zip64(self):
+ """
+ Allow ZIP64 extensions when writing xlsx file zip container.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.allow_zip64 = True
+
+ def set_vba_name(self, name=None):
+ """
+ Set the VBA name for the workbook. By default the workbook is referred
+ to as ThisWorkbook in VBA.
+
+ Args:
+ name: The VBA name for the workbook.
+
+ Returns:
+ Nothing.
+
+ """
+ if name is not None:
+ self.vba_codename = name
+ else:
+ self.vba_codename = 'ThisWorkbook'
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+
+ # Prepare format object for passing to Style.pm.
+ self._prepare_format_properties()
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ # Write the workbook element.
+ self._write_workbook()
+
+ # Write the fileVersion element.
+ self._write_file_version()
+
+ # Write the workbookPr element.
+ self._write_workbook_pr()
+
+ # Write the bookViews element.
+ self._write_book_views()
+
+ # Write the sheets element.
+ self._write_sheets()
+
+ # Write the workbook defined names.
+ self._write_defined_names()
+
+ # Write the calcPr element.
+ self._write_calc_pr()
+
+ # Close the workbook tag.
+ self._xml_end_tag('workbook')
+
+ # Close the file.
+ self._xml_close()
+
+ def _store_workbook(self):
+ # Assemble worksheets into a workbook.
+ packager = Packager()
+
+ # Add a default worksheet if non have been added.
+ if not self.worksheets():
+ self.add_worksheet()
+
+ # Ensure that at least one worksheet has been selected.
+ if self.worksheet_meta.activesheet == 0:
+ self.worksheets_objs[0].selected = 1
+ self.worksheets_objs[0].hidden = 0
+
+ # Set the active sheet.
+ for sheet in self.worksheets():
+ if sheet.index == self.worksheet_meta.activesheet:
+ sheet.active = 1
+
+ # Convert the SST strings data structure.
+ self._prepare_sst_string_data()
+
+ # Prepare the worksheet VML elements such as comments and buttons.
+ self._prepare_vml()
+
+ # Set the defined names for the worksheets such as Print Titles.
+ self._prepare_defined_names()
+
+ # Prepare the drawings, charts and images.
+ self._prepare_drawings()
+
+ # Add cached data to charts.
+ self._add_chart_data()
+
+ # Prepare the worksheet tables.
+ self._prepare_tables()
+
+ # Package the workbook.
+ packager._add_workbook(self)
+ packager._set_tmpdir(self.tmpdir)
+ packager._set_in_memory(self.in_memory)
+ xml_files = packager._create_package()
+
+ # Free up the Packager object.
+ packager = None
+
+ xlsx_file = ZipFile(self.filename, "w", compression=ZIP_DEFLATED,
+ allowZip64=self.allow_zip64)
+
+ # Add XML sub-files to the Zip file with their Excel filename.
+ for os_filename, xml_filename, is_binary in xml_files:
+ if self.in_memory:
+ # The files are in-memory StringIOs.
+ if is_binary:
+ xlsx_file.writestr(xml_filename, os_filename.getvalue())
+ else:
+ xlsx_file.writestr(xml_filename,
+ os_filename.getvalue().encode('utf-8'))
+ else:
+ # The files are tempfiles.
+ xlsx_file.write(os_filename, xml_filename)
+ os.remove(os_filename)
+
+ xlsx_file.close()
+
+ def _add_sheet(self, name, is_chartsheet):
+ # Utility for shared code in add_worksheet() and add_chartsheet().
+
+ sheet_index = len(self.worksheets_objs)
+ name = self._check_sheetname(name, is_chartsheet)
+
+ # Initialization data to pass to the worksheet.
+ init_data = {
+ 'name': name,
+ 'index': sheet_index,
+ 'str_table': self.str_table,
+ 'worksheet_meta': self.worksheet_meta,
+ 'optimization': self.optimization,
+ 'tmpdir': self.tmpdir,
+ 'date_1904': self.date_1904,
+ 'strings_to_numbers': self.strings_to_numbers,
+ 'strings_to_formulas': self.strings_to_formulas,
+ 'strings_to_urls': self.strings_to_urls,
+ 'nan_inf_to_errors': self.nan_inf_to_errors,
+ 'default_date_format': self.default_date_format,
+ 'default_url_format': self.default_url_format,
+ 'excel2003_style': self.excel2003_style,
+ 'remove_timezone': self.remove_timezone,
+ }
+
+ if is_chartsheet:
+ worksheet = Chartsheet()
+ else:
+ worksheet = Worksheet()
+
+ worksheet._initialize(init_data)
+
+ self.worksheets_objs.append(worksheet)
+ self.sheetnames[name] = worksheet
+
+ return worksheet
+
+ def _check_sheetname(self, sheetname, is_chartsheet=False):
+ # Check for valid worksheet names. We check the length, if it contains
+ # any invalid chars and if the sheetname is unique in the workbook.
+ invalid_char = re.compile(r'[\[\]:*?/\\]')
+
+ # Increment the Sheet/Chart number used for default sheet names below.
+ if is_chartsheet:
+ self.chartname_count += 1
+ else:
+ self.sheetname_count += 1
+
+ # Supply default Sheet/Chart sheetname if none has been defined.
+ if sheetname is None:
+ if is_chartsheet:
+ sheetname = self.chart_name + str(self.chartname_count)
+ else:
+ sheetname = self.sheet_name + str(self.sheetname_count)
+
+ # Check that sheet sheetname is <= 31. Excel limit.
+ if len(sheetname) > 31:
+ raise Exception("Excel worksheet name '%s' must be <= 31 chars." %
+ sheetname)
+
+ # Check that sheetname doesn't contain any invalid characters
+ if invalid_char.search(sheetname):
+ raise Exception(
+ "Invalid Excel character '[]:*?/\\' in sheetname '%s'" %
+ sheetname)
+
+ # Check that the worksheet name doesn't already exist since this is a
+ # fatal Excel error. The check must be case insensitive like Excel.
+ for worksheet in self.worksheets():
+ if sheetname.lower() == worksheet.name.lower():
+ raise Exception(
+ "Sheetname '%s', with case ignored, is already in use." %
+ sheetname)
+
+ return sheetname
+
+ def _prepare_format_properties(self):
+ # Prepare all Format properties prior to passing them to styles.py.
+
+ # Separate format objects into XF and DXF formats.
+ self._prepare_formats()
+
+ # Set the font index for the format objects.
+ self._prepare_fonts()
+
+ # Set the number format index for the format objects.
+ self._prepare_num_formats()
+
+ # Set the border index for the format objects.
+ self._prepare_borders()
+
+ # Set the fill index for the format objects.
+ self._prepare_fills()
+
+ def _prepare_formats(self):
+ # Iterate through the XF Format objects and separate them into
+ # XF and DXF formats. The XF and DF formats then need to be sorted
+ # back into index order rather than creation order.
+ xf_formats = []
+ dxf_formats = []
+
+ # Sort into XF and DXF formats.
+ for xf_format in self.formats:
+ if xf_format.xf_index is not None:
+ xf_formats.append(xf_format)
+
+ if xf_format.dxf_index is not None:
+ dxf_formats.append(xf_format)
+
+ # Pre-extend the format lists.
+ self.xf_formats = [None] * len(xf_formats)
+ self.dxf_formats = [None] * len(dxf_formats)
+
+ # Rearrange formats into index order.
+ for xf_format in xf_formats:
+ index = xf_format.xf_index
+ self.xf_formats[index] = xf_format
+
+ for dxf_format in dxf_formats:
+ index = dxf_format.dxf_index
+ self.dxf_formats[index] = dxf_format
+
+ def _set_default_xf_indices(self):
+ # Set the default index for each format. Only used for testing.
+
+ formats = list(self.formats)
+
+ # Delete the default url format.
+ del formats[1]
+
+ # Skip the default date format if set.
+ if self.default_date_format is not None:
+ del formats[1]
+
+ # Set the remaining formats.
+ for xf_format in formats:
+ xf_format._get_xf_index()
+
+ def _prepare_fonts(self):
+ # Iterate through the XF Format objects and give them an index to
+ # non-default font elements.
+ fonts = {}
+ index = 0
+
+ for xf_format in self.xf_formats:
+ key = xf_format._get_font_key()
+ if key in fonts:
+ # Font has already been used.
+ xf_format.font_index = fonts[key]
+ xf_format.has_font = 0
+ else:
+ # This is a new font.
+ fonts[key] = index
+ xf_format.font_index = index
+ xf_format.has_font = 1
+ index += 1
+
+ self.font_count = index
+
+ # For DXF formats we only need to check if the properties have changed.
+ for xf_format in self.dxf_formats:
+ # The only font properties that can change for a DXF format are:
+ # color, bold, italic, underline and strikethrough.
+ if (xf_format.font_color or xf_format.bold or xf_format.italic
+ or xf_format.underline or xf_format.font_strikeout):
+ xf_format.has_dxf_font = 1
+
+ def _prepare_num_formats(self):
+ # User defined records in Excel start from index 0xA4.
+ num_formats = {}
+ index = 164
+ num_format_count = 0
+
+ for xf_format in (self.xf_formats + self.dxf_formats):
+ num_format = xf_format.num_format
+
+ # Check if num_format is an index to a built-in number format.
+ if not isinstance(num_format, str_types):
+ xf_format.num_format_index = int(num_format)
+ continue
+
+ if num_format in num_formats:
+ # Number xf_format has already been used.
+ xf_format.num_format_index = num_formats[num_format]
+ else:
+ # Add a new number xf_format.
+ num_formats[num_format] = index
+ xf_format.num_format_index = index
+ index += 1
+
+ # Only increase font count for XF formats (not DXF formats).
+ if xf_format.xf_index:
+ num_format_count += 1
+
+ self.num_format_count = num_format_count
+
+ def _prepare_borders(self):
+ # Iterate through the XF Format objects and give them an index to
+ # non-default border elements.
+ borders = {}
+ index = 0
+
+ for xf_format in self.xf_formats:
+ key = xf_format._get_border_key()
+
+ if key in borders:
+ # Border has already been used.
+ xf_format.border_index = borders[key]
+ xf_format.has_border = 0
+ else:
+ # This is a new border.
+ borders[key] = index
+ xf_format.border_index = index
+ xf_format.has_border = 1
+ index += 1
+
+ self.border_count = index
+
+ # For DXF formats we only need to check if the properties have changed.
+ has_border = re.compile(r'[^0:]')
+
+ for xf_format in self.dxf_formats:
+ key = xf_format._get_border_key()
+
+ if has_border.search(key):
+ xf_format.has_dxf_border = 1
+
+ def _prepare_fills(self):
+ # Iterate through the XF Format objects and give them an index to
+ # non-default fill elements.
+ # The user defined fill properties start from 2 since there are 2
+ # default fills: patternType="none" and patternType="gray125".
+ fills = {}
+ index = 2 # Start from 2. See above.
+
+ # Add the default fills.
+ fills['0:0:0'] = 0
+ fills['17:0:0'] = 1
+
+ # Store the DXF colors separately since them may be reversed below.
+ for xf_format in self.dxf_formats:
+ if xf_format.pattern or xf_format.bg_color or xf_format.fg_color:
+ xf_format.has_dxf_fill = 1
+ xf_format.dxf_bg_color = xf_format.bg_color
+ xf_format.dxf_fg_color = xf_format.fg_color
+
+ for xf_format in self.xf_formats:
+ # The following logical statements jointly take care of special
+ # cases in relation to cell colors and patterns:
+ # 1. For a solid fill (_pattern == 1) Excel reverses the role of
+ # foreground and background colors, and
+ # 2. If the user specifies a foreground or background color
+ # without a pattern they probably wanted a solid fill, so we fill
+ # in the defaults.
+ if (xf_format.pattern == 1 and xf_format.bg_color != 0
+ and xf_format.fg_color != 0):
+ tmp = xf_format.fg_color
+ xf_format.fg_color = xf_format.bg_color
+ xf_format.bg_color = tmp
+
+ if (xf_format.pattern <= 1 and xf_format.bg_color != 0
+ and xf_format.fg_color == 0):
+ xf_format.fg_color = xf_format.bg_color
+ xf_format.bg_color = 0
+ xf_format.pattern = 1
+
+ if (xf_format.pattern <= 1 and xf_format.bg_color == 0
+ and xf_format.fg_color != 0):
+ xf_format.bg_color = 0
+ xf_format.pattern = 1
+
+ key = xf_format._get_fill_key()
+
+ if key in fills:
+ # Fill has already been used.
+ xf_format.fill_index = fills[key]
+ xf_format.has_fill = 0
+ else:
+ # This is a new fill.
+ fills[key] = index
+ xf_format.fill_index = index
+ xf_format.has_fill = 1
+ index += 1
+
+ self.fill_count = index
+
+ def _prepare_defined_names(self):
+ # Iterate through the worksheets and store any defined names in
+ # addition to any user defined names. Stores the defined names
+ # for the Workbook.xml and the named ranges for App.xml.
+ defined_names = self.defined_names
+
+ for sheet in self.worksheets():
+ # Check for Print Area settings.
+ if sheet.autofilter_area:
+ hidden = 1
+ sheet_range = sheet.autofilter_area
+ # Store the defined names.
+ defined_names.append(['_xlnm._FilterDatabase',
+ sheet.index, sheet_range, hidden])
+
+ # Check for Print Area settings.
+ if sheet.print_area_range:
+ hidden = 0
+ sheet_range = sheet.print_area_range
+ # Store the defined names.
+ defined_names.append(['_xlnm.Print_Area',
+ sheet.index, sheet_range, hidden])
+
+ # Check for repeat rows/cols referred to as Print Titles.
+ if sheet.repeat_col_range or sheet.repeat_row_range:
+ hidden = 0
+ sheet_range = ''
+ if sheet.repeat_col_range and sheet.repeat_row_range:
+ sheet_range = (sheet.repeat_col_range + ',' +
+ sheet.repeat_row_range)
+ else:
+ sheet_range = (sheet.repeat_col_range +
+ sheet.repeat_row_range)
+ # Store the defined names.
+ defined_names.append(['_xlnm.Print_Titles',
+ sheet.index, sheet_range, hidden])
+
+ defined_names = self._sort_defined_names(defined_names)
+ self.defined_names = defined_names
+ self.named_ranges = self._extract_named_ranges(defined_names)
+
+ def _sort_defined_names(self, names):
+ # Sort the list of list of internal and user defined names in
+ # the same order as used by Excel.
+
+ # Add a normalize name string to each list for sorting.
+ for name_list in names:
+ (defined_name, _, sheet_name, _) = name_list
+
+ # Normalize the defined name by removing any leading '_xmln.'
+ # from internal names and lowercasing the string.
+ defined_name = defined_name.replace('_xlnm.', '').lower()
+
+ # Normalize the sheetname by removing the leading quote and
+ # lowercasing the string.
+ sheet_name = sheet_name.lstrip("'").lower()
+
+ name_list.append(defined_name + "::" + sheet_name)
+
+ # Sort based on the normalized key.
+ names.sort(key=operator.itemgetter(4))
+
+ # Remove the extra key used for sorting.
+ for name_list in names:
+ name_list.pop()
+
+ return names
+
+ def _prepare_drawings(self):
+ # Iterate through the worksheets and set up chart and image drawings.
+ chart_ref_id = 0
+ image_ref_id = 0
+ drawing_id = 0
+ x_dpi = 96
+ y_dpi = 96
+
+ for sheet in self.worksheets():
+ chart_count = len(sheet.charts)
+ image_count = len(sheet.images)
+ shape_count = len(sheet.shapes)
+
+ header_image_count = len(sheet.header_images)
+ footer_image_count = len(sheet.footer_images)
+ has_drawing = False
+
+ if not (chart_count or image_count or shape_count
+ or header_image_count or footer_image_count):
+ continue
+
+ # Don't increase the drawing_id header/footer images.
+ if chart_count or image_count or shape_count:
+ drawing_id += 1
+ has_drawing = True
+
+ # Prepare the worksheet charts.
+ for index in range(chart_count):
+ chart_ref_id += 1
+ sheet._prepare_chart(index, chart_ref_id, drawing_id)
+
+ # Prepare the worksheet images.
+ for index in range(image_count):
+ filename = sheet.images[index][2]
+ image_data = sheet.images[index][10]
+ (image_type, width, height, name, x_dpi, y_dpi) = \
+ self._get_image_properties(filename, image_data)
+ image_ref_id += 1
+
+ sheet._prepare_image(index, image_ref_id, drawing_id, width,
+ height, name, image_type, x_dpi, y_dpi)
+
+ # Prepare the worksheet shapes.
+ for index in range(shape_count):
+ sheet._prepare_shape(index, drawing_id)
+
+ # Prepare the header images.
+ for index in range(header_image_count):
+
+ filename = sheet.header_images[index][0]
+ image_data = sheet.header_images[index][1]
+ position = sheet.header_images[index][2]
+
+ (image_type, width, height, name, x_dpi, y_dpi) = \
+ self._get_image_properties(filename, image_data)
+
+ image_ref_id += 1
+
+ sheet._prepare_header_image(image_ref_id, width, height,
+ name, image_type, position,
+ x_dpi, y_dpi)
+
+ # Prepare the footer images.
+ for index in range(footer_image_count):
+
+ filename = sheet.footer_images[index][0]
+ image_data = sheet.footer_images[index][1]
+ position = sheet.footer_images[index][2]
+
+ (image_type, width, height, name, x_dpi, y_dpi) = \
+ self._get_image_properties(filename, image_data)
+
+ image_ref_id += 1
+
+ sheet._prepare_header_image(image_ref_id, width, height,
+ name, image_type, position,
+ x_dpi, y_dpi)
+
+ if has_drawing:
+ drawing = sheet.drawing
+ self.drawings.append(drawing)
+
+ # Remove charts that were created but not inserted into worksheets.
+ for chart in self.charts[:]:
+ if chart.id == -1:
+ self.charts.remove(chart)
+
+ # Sort the workbook charts references into the order that the were
+ # written to the worksheets above.
+ self.charts = sorted(self.charts, key=lambda chart: chart.id)
+
+ self.drawing_count = drawing_id
+
+ def _get_image_properties(self, filename, image_data):
+ # Extract dimension information from the image file.
+ height = 0
+ width = 0
+ x_dpi = 96
+ y_dpi = 96
+
+ if not image_data:
+ # Open the image file and read in the data.
+ fh = open(filename, "rb")
+ data = fh.read()
+ else:
+ # Read the image data from the user supplied byte stream.
+ data = image_data.getvalue()
+
+ # Get the image filename without the path.
+ image_name = os.path.basename(filename)
+
+ # Look for some common image file markers.
+ marker1 = (unpack('3s', data[1:4]))[0]
+ marker2 = (unpack('>H', data[:2]))[0]
+ marker3 = (unpack('2s', data[:2]))[0]
+
+ if sys.version_info < (2, 6, 0):
+ # Python 2.5/Jython.
+ png_marker = 'PNG'
+ bmp_marker = 'BM'
+ else:
+ # Eval the binary literals for Python 2.5/Jython compatibility.
+ png_marker = eval("b'PNG'")
+ bmp_marker = eval("b'BM'")
+
+ if marker1 == png_marker:
+ self.image_types['png'] = 1
+ (image_type, width, height, x_dpi, y_dpi) = self._process_png(data)
+
+ elif marker2 == 0xFFD8:
+ self.image_types['jpeg'] = 1
+ (image_type, width, height, x_dpi, y_dpi) = self._process_jpg(data)
+
+ elif marker3 == bmp_marker:
+ self.image_types['bmp'] = 1
+ (image_type, width, height) = self._process_bmp(data)
+
+ else:
+ raise Exception("%s: Unknown or unsupported image file format."
+ % filename)
+
+ # Check that we found the required data.
+ if not height or not width:
+ raise Exception("%s: no size data found in image file." % filename)
+
+ # Store image data to copy it into file container.
+ self.images.append([filename, image_type, image_data])
+
+ if not image_data:
+ fh.close()
+
+ # Set a default dpi for images with 0 dpi.
+ if x_dpi == 0:
+ x_dpi = 96
+ if y_dpi == 0:
+ y_dpi = 96
+
+ return image_type, width, height, image_name, x_dpi, y_dpi
+
+ def _process_png(self, data):
+ # Extract width and height information from a PNG file.
+ offset = 8
+ data_length = len(data)
+ end_marker = False
+ width = 0
+ height = 0
+ x_dpi = 96
+ y_dpi = 96
+
+ # Look for numbers rather than strings for Python 2.6/3 compatibility.
+ marker_ihdr = 0x49484452 # IHDR
+ marker_phys = 0x70485973 # pHYs
+ marker_iend = 0X49454E44 # IEND
+
+ # Search through the image data to read the height and width in the
+ # IHDR element. Also read the DPI in the pHYs element.
+ while not end_marker and offset < data_length:
+
+ length = (unpack('>I', data[offset + 0:offset + 4]))[0]
+ marker = (unpack('>I', data[offset + 4:offset + 8]))[0]
+
+ # Read the image dimensions.
+ if marker == marker_ihdr:
+ width = (unpack('>I', data[offset + 8:offset + 12]))[0]
+ height = (unpack('>I', data[offset + 12:offset + 16]))[0]
+
+ # Read the image DPI.
+ if marker == marker_phys:
+ x_density = (unpack('>I', data[offset + 8:offset + 12]))[0]
+ y_density = (unpack('>I', data[offset + 12:offset + 16]))[0]
+ units = (unpack('b', data[offset + 16:offset + 17]))[0]
+
+ if units == 1:
+ x_dpi = x_density * 0.0254
+ y_dpi = y_density * 0.0254
+
+ if marker == marker_iend:
+ end_marker = True
+ continue
+
+ offset = offset + length + 12
+
+ return 'png', width, height, x_dpi, y_dpi
+
+ def _process_jpg(self, data):
+ # Extract width and height information from a JPEG file.
+ offset = 2
+ data_length = len(data)
+ end_marker = False
+ width = 0
+ height = 0
+ x_dpi = 96
+ y_dpi = 96
+
+ # Search through the image data to read the height and width in the
+ # 0xFFC0/C2 element. Also read the DPI in the 0xFFE0 element.
+ while not end_marker and offset < data_length:
+
+ marker = (unpack('>H', data[offset + 0:offset + 2]))[0]
+ length = (unpack('>H', data[offset + 2:offset + 4]))[0]
+
+ # Read the image dimensions.
+ if marker == 0xFFC0 or marker == 0xFFC2:
+ height = (unpack('>H', data[offset + 5:offset + 7]))[0]
+ width = (unpack('>H', data[offset + 7:offset + 9]))[0]
+
+ # Read the image DPI.
+ if marker == 0xFFE0:
+ units = (unpack('b', data[offset + 11:offset + 12]))[0]
+ x_density = (unpack('>H', data[offset + 12:offset + 14]))[0]
+ y_density = (unpack('>H', data[offset + 14:offset + 16]))[0]
+
+ if units == 1:
+ x_dpi = x_density
+ y_dpi = y_density
+
+ if units == 2:
+ x_dpi = x_density * 2.54
+ y_dpi = y_density * 2.54
+
+ # Workaround for incorrect dpi.
+ if x_dpi == 1:
+ x_dpi = 96
+ if y_dpi == 1:
+ y_dpi = 96
+
+ if marker == 0xFFDA:
+ end_marker = True
+ continue
+
+ offset = offset + length + 2
+
+ return 'jpeg', width, height, x_dpi, y_dpi
+
+ def _process_bmp(self, data):
+ # Extract width and height information from a BMP file.
+ width = (unpack('<L', data[18:22]))[0]
+ height = (unpack('<L', data[22:26]))[0]
+ return 'bmp', width, height
+
+ def _extract_named_ranges(self, defined_names):
+ # Extract the named ranges from the sorted list of defined names.
+ # These are used in the App.xml file.
+ named_ranges = []
+
+ for defined_name in defined_names:
+
+ name = defined_name[0]
+ index = defined_name[1]
+ sheet_range = defined_name[2]
+
+ # Skip autoFilter ranges.
+ if name == '_xlnm._FilterDatabase':
+ continue
+
+ # We are only interested in defined names with ranges.
+ if '!' in sheet_range:
+ sheet_name, _ = sheet_range.split('!', 1)
+
+ # Match Print_Area and Print_Titles xlnm types.
+ if name.startswith('_xlnm.'):
+ xlnm_type = name.replace('_xlnm.', '')
+ name = sheet_name + '!' + xlnm_type
+ elif index != -1:
+ name = sheet_name + '!' + name
+
+ named_ranges.append(name)
+
+ return named_ranges
+
+ def _get_sheet_index(self, sheetname):
+ # Convert a sheet name to its index. Return None otherwise.
+ sheetname = sheetname.strip("'")
+
+ if sheetname in self.sheetnames:
+ return self.sheetnames[sheetname].index
+ else:
+ return None
+
+ def _prepare_vml(self):
+ # Iterate through the worksheets and set up the VML objects.
+ comment_id = 0
+ vml_drawing_id = 0
+ vml_data_id = 1
+ vml_header_id = 0
+ vml_shape_id = 1024
+ vml_files = 0
+ comment_files = 0
+ has_button = False
+
+ for sheet in self.worksheets():
+ if not sheet.has_vml and not sheet.has_header_vml:
+ continue
+
+ vml_files += 1
+
+ if sheet.has_vml:
+ if sheet.has_comments:
+ comment_files += 1
+ comment_id += 1
+
+ vml_drawing_id += 1
+
+ count = sheet._prepare_vml_objects(vml_data_id,
+ vml_shape_id,
+ vml_drawing_id,
+ comment_id)
+
+ # Each VML should start with a shape id incremented by 1024.
+ vml_data_id += 1 * int((1024 + count) / 1024)
+ vml_shape_id += 1024 * int((1024 + count) / 1024)
+
+ if sheet.has_header_vml:
+ vml_header_id += 1
+ vml_drawing_id += 1
+ sheet._prepare_header_vml_objects(vml_header_id,
+ vml_drawing_id)
+
+ self.num_vml_files = vml_files
+ self.num_comment_files = comment_files
+
+ if len(sheet.buttons_list):
+ has_button = True
+
+ # Set the sheet vba_codename if it has a button and the
+ # workbook has a vbaProject binary.
+ if self.vba_project and sheet.vba_codename is None:
+ sheet.set_vba_name()
+
+ # Add a font format for cell comments.
+ if comment_files > 0:
+ xf = self.add_format({'font_name': 'Tahoma', 'font_size': 8,
+ 'color_indexed': 81, 'font_only': True})
+ xf._get_xf_index()
+
+ # Set the workbook vba_codename if one of the sheets has a button and
+ # the workbook has a vbaProject binary.
+ if has_button and self.vba_project and self.vba_codename is None:
+ self.set_vba_name()
+
+ def _prepare_tables(self):
+ # Set the table ids for the worksheet tables.
+ table_id = 0
+ seen = {}
+
+ for sheet in self.worksheets():
+ table_count = len(sheet.tables)
+
+ if not table_count:
+ continue
+
+ sheet._prepare_tables(table_id + 1, seen)
+ table_id += table_count
+
+ def _add_chart_data(self):
+ # Add "cached" data to charts to provide the numCache and strCache
+ # data for series and title/axis ranges.
+ worksheets = {}
+ seen_ranges = {}
+ charts = []
+
+ # Map worksheet names to worksheet objects.
+ for worksheet in self.worksheets():
+ worksheets[worksheet.name] = worksheet
+
+ # Build a list of the worksheet charts including any combined charts.
+ for chart in self.charts:
+ charts.append(chart)
+ if chart.combined:
+ charts.append(chart.combined)
+
+ for chart in charts:
+
+ for c_range in chart.formula_ids.keys():
+ r_id = chart.formula_ids[c_range]
+
+ # Skip if the series has user defined data.
+ if chart.formula_data[r_id] is not None:
+ if (c_range not in seen_ranges
+ or seen_ranges[c_range] is None):
+ data = chart.formula_data[r_id]
+ seen_ranges[c_range] = data
+ continue
+
+ # Check to see if the data is already cached locally.
+ if c_range in seen_ranges:
+ chart.formula_data[r_id] = seen_ranges[c_range]
+ continue
+
+ # Convert the range formula to a sheet name and cell range.
+ (sheetname, cells) = self._get_chart_range(c_range)
+
+ # Skip if we couldn't parse the formula.
+ if sheetname is None:
+ continue
+
+ # Handle non-contiguous ranges like:
+ # (Sheet1!$A$1:$A$2,Sheet1!$A$4:$A$5).
+ # We don't try to parse them. We just return an empty list.
+ if sheetname.startswith('('):
+ chart.formula_data[r_id] = []
+ seen_ranges[c_range] = []
+ continue
+
+ # Warn if the name is unknown since it indicates a user error
+ # in a chart series formula.
+ if sheetname not in worksheets:
+ warn("Unknown worksheet reference '%s' in range "
+ "'%s' passed to add_series()"
+ % (force_unicode(sheetname), force_unicode(c_range)))
+ chart.formula_data[r_id] = []
+ seen_ranges[c_range] = []
+ continue
+
+ # Find the worksheet object based on the sheet name.
+ worksheet = worksheets[sheetname]
+
+ # Get the data from the worksheet table.
+ data = worksheet._get_range_data(*cells)
+
+ # TODO. Handle SST string ids if required.
+
+ # Add the data to the chart.
+ chart.formula_data[r_id] = data
+
+ # Store range data locally to avoid lookup if seen again.
+ seen_ranges[c_range] = data
+
+ def _get_chart_range(self, c_range):
+ # Convert a range formula such as Sheet1!$B$1:$B$5 into a sheet name
+ # and cell range such as ( 'Sheet1', 0, 1, 4, 1 ).
+
+ # Split the range formula into sheetname and cells at the last '!'.
+ pos = c_range.rfind('!')
+ if pos > 0:
+ sheetname = c_range[:pos]
+ cells = c_range[pos + 1:]
+ else:
+ return None, None
+
+ # Split the cell range into 2 cells or else use single cell for both.
+ if cells.find(':') > 0:
+ (cell_1, cell_2) = cells.split(':', 1)
+ else:
+ (cell_1, cell_2) = (cells, cells)
+
+ # Remove leading/trailing quotes and convert escaped quotes to single.
+ sheetname = sheetname.strip("'")
+ sheetname = sheetname.replace("''", "'")
+
+ try:
+ # Get the row, col values from the Excel ranges. We do this in a
+ # try block for ranges that can't be parsed such as defined names.
+ (row_start, col_start) = xl_cell_to_rowcol(cell_1)
+ (row_end, col_end) = xl_cell_to_rowcol(cell_2)
+ except:
+ return None, None
+
+ # We only handle 1D ranges.
+ if row_start != row_end and col_start != col_end:
+ return None, None
+
+ return sheetname, [row_start, col_start, row_end, col_end]
+
+ def _prepare_sst_string_data(self):
+ # Convert the SST string data from a dict to a list.
+ self.str_table._sort_string_data()
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_workbook(self):
+ # Write <workbook> element.
+
+ schema = 'http://schemas.openxmlformats.org'
+ xmlns = schema + '/spreadsheetml/2006/main'
+ xmlns_r = schema + '/officeDocument/2006/relationships'
+
+ attributes = [
+ ('xmlns', xmlns),
+ ('xmlns:r', xmlns_r),
+ ]
+
+ self._xml_start_tag('workbook', attributes)
+
+ def _write_file_version(self):
+ # Write the <fileVersion> element.
+
+ app_name = 'xl'
+ last_edited = 4
+ lowest_edited = 4
+ rup_build = 4505
+
+ attributes = [
+ ('appName', app_name),
+ ('lastEdited', last_edited),
+ ('lowestEdited', lowest_edited),
+ ('rupBuild', rup_build),
+ ]
+
+ if self.vba_project:
+ attributes.append(
+ ('codeName', '{37E998C4-C9E5-D4B9-71C8-EB1FF731991C}'))
+
+ self._xml_empty_tag('fileVersion', attributes)
+
+ def _write_workbook_pr(self):
+ # Write <workbookPr> element.
+ default_theme_version = 124226
+ attributes = []
+
+ if self.vba_codename:
+ attributes.append(('codeName', self.vba_codename))
+ if self.date_1904:
+ attributes.append(('date1904', 1))
+
+ attributes.append(('defaultThemeVersion', default_theme_version))
+
+ self._xml_empty_tag('workbookPr', attributes)
+
+ def _write_book_views(self):
+ # Write <bookViews> element.
+ self._xml_start_tag('bookViews')
+ self._write_workbook_view()
+ self._xml_end_tag('bookViews')
+
+ def _write_workbook_view(self):
+ # Write <workbookView> element.
+ attributes = [
+ ('xWindow', self.x_window),
+ ('yWindow', self.y_window),
+ ('windowWidth', self.window_width),
+ ('windowHeight', self.window_height),
+ ]
+
+ # Store the tabRatio attribute when it isn't the default.
+ if self.tab_ratio != 500:
+ attributes.append(('tabRatio', self.tab_ratio))
+
+ # Store the firstSheet attribute when it isn't the default.
+ if self.worksheet_meta.firstsheet > 0:
+ firstsheet = self.worksheet_meta.firstsheet + 1
+ attributes.append(('firstSheet', firstsheet))
+
+ # Store the activeTab attribute when it isn't the first sheet.
+ if self.worksheet_meta.activesheet > 0:
+ attributes.append(('activeTab', self.worksheet_meta.activesheet))
+
+ self._xml_empty_tag('workbookView', attributes)
+
+ def _write_sheets(self):
+ # Write <sheets> element.
+ self._xml_start_tag('sheets')
+
+ id_num = 1
+ for worksheet in self.worksheets():
+ self._write_sheet(worksheet.name, id_num, worksheet.hidden)
+ id_num += 1
+
+ self._xml_end_tag('sheets')
+
+ def _write_sheet(self, name, sheet_id, hidden):
+ # Write <sheet> element.
+ attributes = [
+ ('name', name),
+ ('sheetId', sheet_id),
+ ]
+
+ if hidden:
+ attributes.append(('state', 'hidden'))
+
+ attributes.append(('r:id', 'rId' + str(sheet_id)))
+
+ self._xml_empty_tag('sheet', attributes)
+
+ def _write_calc_pr(self):
+ # Write the <calcPr> element.
+ attributes = [('calcId', self.calc_id)]
+
+ if self.calc_mode == 'manual':
+ attributes.append(('calcMode', self.calc_mode))
+ attributes.append(('calcOnSave', "0"))
+ elif self.calc_mode == 'autoNoTable':
+ attributes.append(('calcMode', self.calc_mode))
+
+ if self.calc_on_load:
+ attributes.append(('fullCalcOnLoad', '1'))
+
+ self._xml_empty_tag('calcPr', attributes)
+
+ def _write_defined_names(self):
+ # Write the <definedNames> element.
+ if not self.defined_names:
+ return
+
+ self._xml_start_tag('definedNames')
+
+ for defined_name in self.defined_names:
+ self._write_defined_name(defined_name)
+
+ self._xml_end_tag('definedNames')
+
+ def _write_defined_name(self, defined_name):
+ # Write the <definedName> element.
+ name = defined_name[0]
+ sheet_id = defined_name[1]
+ sheet_range = defined_name[2]
+ hidden = defined_name[3]
+
+ attributes = [('name', name)]
+
+ if sheet_id != -1:
+ attributes.append(('localSheetId', sheet_id))
+ if hidden:
+ attributes.append(('hidden', 1))
+
+ self._xml_data_element('definedName', sheet_range, attributes)
+
+
+# A metadata class to share data between worksheets.
+class WorksheetMeta(object):
+ """
+ A class to track worksheets data such as the active sheet and the
+ first sheet.
+
+ """
+
+ def __init__(self):
+ self.activesheet = 0
+ self.firstsheet = 0
diff --git a/xlsxwriter/worksheet.py b/xlsxwriter/worksheet.py
new file mode 100644
index 0000000..dafdd28
--- /dev/null
+++ b/xlsxwriter/worksheet.py
@@ -0,0 +1,6514 @@
+###############################################################################
+#
+# Worksheet - A class for writing the Excel XLSX Worksheet file.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Standard packages.
+import re
+import tempfile
+import codecs
+import os
+
+from warnings import warn
+
+# Standard packages in Python 2/3 compatibility mode.
+from .compatibility import StringIO
+from .compatibility import defaultdict
+from .compatibility import namedtuple
+from .compatibility import force_unicode
+from .compatibility import num_types, str_types
+
+# Package imports.
+from . import xmlwriter
+from .format import Format
+from .drawing import Drawing
+from .shape import Shape
+from .xmlwriter import XMLwriter
+from .utility import xl_rowcol_to_cell
+from .utility import xl_rowcol_to_cell_fast
+from .utility import xl_cell_to_rowcol
+from .utility import xl_col_to_name
+from .utility import xl_range
+from .utility import xl_color
+from .utility import get_sparkline_style
+from .utility import supported_datetime
+from .utility import datetime_to_excel_datetime
+from .utility import quote_sheetname
+
+
+###############################################################################
+#
+# Decorator functions.
+#
+###############################################################################
+def convert_cell_args(method):
+ """
+ Decorator function to convert A1 notation in cell method calls
+ to the default row/col notation.
+
+ """
+ def cell_wrapper(self, *args, **kwargs):
+
+ try:
+ # First arg is an int, default to row/col notation.
+ if len(args):
+ int(args[0])
+ except ValueError:
+ # First arg isn't an int, convert to A1 notation.
+ new_args = list(xl_cell_to_rowcol(args[0]))
+ new_args.extend(args[1:])
+ args = new_args
+
+ return method(self, *args, **kwargs)
+
+ return cell_wrapper
+
+
+def convert_range_args(method):
+ """
+ Decorator function to convert A1 notation in range method calls
+ to the default row/col notation.
+
+ """
+ def cell_wrapper(self, *args, **kwargs):
+
+ try:
+ # First arg is an int, default to row/col notation.
+ if len(args):
+ int(args[0])
+ except ValueError:
+ # First arg isn't an int, convert to A1 notation.
+ if ':' in args[0]:
+ cell_1, cell_2 = args[0].split(':')
+ row_1, col_1 = xl_cell_to_rowcol(cell_1)
+ row_2, col_2 = xl_cell_to_rowcol(cell_2)
+ else:
+ row_1, col_1 = xl_cell_to_rowcol(args[0])
+ row_2, col_2 = row_1, col_1
+
+ new_args = [row_1, col_1, row_2, col_2]
+ new_args.extend(args[1:])
+ args = new_args
+
+ return method(self, *args, **kwargs)
+
+ return cell_wrapper
+
+
+def convert_column_args(method):
+ """
+ Decorator function to convert A1 notation in columns method calls
+ to the default row/col notation.
+
+ """
+ def column_wrapper(self, *args, **kwargs):
+
+ try:
+ # First arg is an int, default to row/col notation.
+ if len(args):
+ int(args[0])
+ except ValueError:
+ # First arg isn't an int, convert to A1 notation.
+ cell_1, cell_2 = [col + '1' for col in args[0].split(':')]
+ _, col_1 = xl_cell_to_rowcol(cell_1)
+ _, col_2 = xl_cell_to_rowcol(cell_2)
+ new_args = [col_1, col_2]
+ new_args.extend(args[1:])
+ args = new_args
+
+ return method(self, *args, **kwargs)
+
+ return column_wrapper
+
+
+###############################################################################
+#
+# Named tuples used for cell types.
+#
+###############################################################################
+cell_string_tuple = namedtuple('String', 'string, format')
+cell_number_tuple = namedtuple('Number', 'number, format')
+cell_blank_tuple = namedtuple('Blank', 'format')
+cell_boolean_tuple = namedtuple('Boolean', 'boolean, format')
+cell_formula_tuple = namedtuple('Formula', 'formula, format, value')
+cell_arformula_tuple = namedtuple('ArrayFormula',
+ 'formula, format, value, range')
+
+
+###############################################################################
+#
+# Worksheet Class definition.
+#
+###############################################################################
+class Worksheet(xmlwriter.XMLwriter):
+ """
+ A class for writing the Excel XLSX Worksheet file.
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super(Worksheet, self).__init__()
+
+ self.name = None
+ self.index = None
+ self.str_table = None
+ self.palette = None
+ self.optimization = 0
+ self.tmpdir = None
+ self.is_chartsheet = False
+
+ self.ext_sheets = []
+ self.fileclosed = 0
+ self.excel_version = 2007
+ self.excel2003_style = False
+
+ self.xls_rowmax = 1048576
+ self.xls_colmax = 16384
+ self.xls_strmax = 32767
+ self.dim_rowmin = None
+ self.dim_rowmax = None
+ self.dim_colmin = None
+ self.dim_colmax = None
+
+ self.colinfo = {}
+ self.selections = []
+ self.hidden = 0
+ self.active = 0
+ self.tab_color = 0
+
+ self.panes = []
+ self.active_pane = 3
+ self.selected = 0
+
+ self.page_setup_changed = False
+ self.paper_size = 0
+ self.orientation = 1
+
+ self.print_options_changed = False
+ self.hcenter = 0
+ self.vcenter = 0
+ self.print_gridlines = 0
+ self.screen_gridlines = 1
+ self.print_headers = 0
+
+ self.header_footer_changed = False
+ self.header = ''
+ self.footer = ''
+ self.header_footer_aligns = True
+ self.header_footer_scales = True
+ self.header_images = []
+ self.footer_images = []
+ self.header_images_list = []
+
+ self.margin_left = 0.7
+ self.margin_right = 0.7
+ self.margin_top = 0.75
+ self.margin_bottom = 0.75
+ self.margin_header = 0.3
+ self.margin_footer = 0.3
+
+ self.repeat_row_range = ''
+ self.repeat_col_range = ''
+ self.print_area_range = ''
+
+ self.page_order = 0
+ self.black_white = 0
+ self.draft_quality = 0
+ self.print_comments = 0
+ self.page_start = 0
+
+ self.fit_page = 0
+ self.fit_width = 0
+ self.fit_height = 0
+
+ self.hbreaks = []
+ self.vbreaks = []
+
+ self.protect_options = {}
+ self.set_cols = {}
+ self.set_rows = defaultdict(dict)
+
+ self.zoom = 100
+ self.zoom_scale_normal = 1
+ self.print_scale = 100
+ self.is_right_to_left = 0
+ self.show_zeros = 1
+ self.leading_zeros = 0
+
+ self.outline_row_level = 0
+ self.outline_col_level = 0
+ self.outline_style = 0
+ self.outline_below = 1
+ self.outline_right = 1
+ self.outline_on = 1
+ self.outline_changed = False
+
+ self.original_row_height = 15
+ self.default_row_height = 15
+ self.default_row_pixels = 20
+ self.default_col_pixels = 64
+ self.default_row_zeroed = 0
+
+ self.names = {}
+ self.write_match = []
+ self.table = defaultdict(dict)
+ self.merge = []
+ self.row_spans = {}
+
+ self.has_vml = False
+ self.has_header_vml = False
+ self.has_comments = False
+ self.comments = defaultdict(dict)
+ self.comments_list = []
+ self.comments_author = ''
+ self.comments_visible = 0
+ self.vml_shape_id = 1024
+ self.buttons_list = []
+ self.vml_header_id = 0
+
+ self.autofilter_area = ''
+ self.autofilter_ref = None
+ self.filter_range = []
+ self.filter_on = 0
+ self.filter_range = []
+ self.filter_cols = {}
+ self.filter_type = {}
+
+ self.col_sizes = {}
+ self.row_sizes = {}
+ self.col_formats = {}
+ self.col_size_changed = False
+ self.row_size_changed = False
+
+ self.last_shape_id = 1
+ self.rel_count = 0
+ self.hlink_count = 0
+ self.hlink_refs = []
+ self.external_hyper_links = []
+ self.external_drawing_links = []
+ self.external_comment_links = []
+ self.external_vml_links = []
+ self.external_table_links = []
+ self.drawing_links = []
+ self.vml_drawing_links = []
+ self.charts = []
+ self.images = []
+ self.tables = []
+ self.sparklines = []
+ self.shapes = []
+ self.shape_hash = {}
+ self.drawing = 0
+
+ self.rstring = ''
+ self.previous_row = 0
+
+ self.validations = []
+ self.cond_formats = {}
+ self.dxf_priority = 1
+ self.is_chartsheet = 0
+ self.page_view = 0
+
+ self.vba_codename = None
+
+ self.date_1904 = False
+ self.hyperlinks = defaultdict(dict)
+
+ self.strings_to_numbers = False
+ self.strings_to_urls = True
+ self.nan_inf_to_errors = False
+ self.strings_to_formulas = True
+
+ self.default_date_format = None
+ self.default_url_format = None
+ self.remove_timezone = False
+
+ self.row_data_filename = None
+ self.row_data_fh = None
+ self.worksheet_meta = None
+ self.vml_data_id = None
+ self.vml_shape_id = None
+
+ self.row_data_filename = None
+ self.row_data_fh = None
+ self.row_data_fh_closed = False
+
+ self.vertical_dpi = 0
+ self.horizontal_dpi = 0
+
+ @convert_cell_args
+ def write(self, row, col, *args):
+ """
+ Write data to a worksheet cell by calling the appropriate write_*()
+ method based on the type of data being passed.
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ *args: Args to pass to sub functions.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+ other: Return value of called method.
+
+ """
+ # Check the number of args passed.
+ if not len(args):
+ raise TypeError("write() takes at least 4 arguments (3 given)")
+
+ # The first arg should be the token for all write calls.
+ token = args[0]
+
+ # Write None as a blank cell.
+ if token is None:
+ return self.write_blank(row, col, *args)
+
+ # Write boolean types.
+ if isinstance(token, bool):
+ return self.write_boolean(row, col, *args)
+
+ # Write datetime objects.
+ if supported_datetime(token):
+ return self.write_datetime(row, col, *args)
+
+ # Write number types.
+ if isinstance(token, num_types):
+ return self.write_number(row, col, *args)
+
+ # Write string types.
+ if isinstance(token, str_types):
+ # Map the data to the appropriate write_*() method.
+ if token == '':
+ return self.write_blank(row, col, *args)
+
+ elif self.strings_to_formulas and token.startswith('='):
+ return self.write_formula(row, col, *args)
+
+ elif self.strings_to_urls and re.match('(ftp|http)s?://', token):
+ return self.write_url(row, col, *args)
+
+ elif self.strings_to_urls and re.match('mailto:', token):
+ return self.write_url(row, col, *args)
+
+ elif self.strings_to_urls and re.match('(in|ex)ternal:', token):
+ return self.write_url(row, col, *args)
+
+ elif self.strings_to_numbers:
+ try:
+ f = float(token)
+ if (self.nan_inf_to_errors or
+ (not self._isnan(f) and not self._isinf(f))):
+ return self.write_number(row, col, f, *args[1:])
+ except ValueError:
+ # Not a number, write as a string.
+ pass
+
+ return self.write_string(row, col, *args)
+
+ else:
+ # We have a plain string.
+ return self.write_string(row, col, *args)
+
+ # We haven't matched a supported type. Try float.
+ try:
+ f = float(token)
+ return self.write_number(row, col, f, *args[1:])
+ except ValueError:
+ pass
+ except TypeError:
+ raise TypeError("Unsupported type %s in write()" % type(token))
+
+ # Finally try string.
+ try:
+ str(token)
+ return self.write_string(row, col, *args)
+ except ValueError:
+ raise TypeError("Unsupported type %s in write()" % type(token))
+
+ @convert_cell_args
+ def write_string(self, row, col, string, cell_format=None):
+ """
+ Write a string to a worksheet cell.
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ string: Cell data. Str.
+ format: An optional cell Format object.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+ -2: String truncated to 32k characters.
+
+ """
+ str_error = 0
+
+ # Check that row and col are valid and store max and min values.
+ if self._check_dimensions(row, col):
+ return -1
+
+ # Check that the string is < 32767 chars.
+ if len(string) > self.xls_strmax:
+ string = string[:self.xls_strmax]
+ str_error = -2
+
+ # Write a shared string or an in-line string in optimization mode.
+ if self.optimization == 0:
+ string_index = self.str_table._get_shared_string_index(string)
+ else:
+ string_index = string
+
+ # Write previous row if in in-line string optimization mode.
+ if self.optimization and row > self.previous_row:
+ self._write_single_row(row)
+
+ # Store the cell data in the worksheet data table.
+ self.table[row][col] = cell_string_tuple(string_index, cell_format)
+
+ return str_error
+
+ @convert_cell_args
+ def write_number(self, row, col, number, cell_format=None):
+ """
+ Write a number to a worksheet cell.
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ number: Cell data. Int or float.
+ cell_format: An optional cell Format object.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+
+ """
+ if self._isnan(number) or self._isinf(number):
+ if self.nan_inf_to_errors:
+ if self._isnan(number):
+ return self.write_formula(row, col, '#NUM!', cell_format,
+ '#NUM!')
+ elif self._isinf(number):
+ return self.write_formula(row, col, '1/0', cell_format,
+ '#DIV/0!')
+ else:
+ raise TypeError(
+ "NAN/INF not supported in write_number() "
+ "without 'nan_inf_to_errors' Workbook() option")
+
+ # Check that row and col are valid and store max and min values.
+ if self._check_dimensions(row, col):
+ return -1
+
+ # Write previous row if in in-line string optimization mode.
+ if self.optimization and row > self.previous_row:
+ self._write_single_row(row)
+
+ # Store the cell data in the worksheet data table.
+ self.table[row][col] = cell_number_tuple(number, cell_format)
+
+ return 0
+
+ @convert_cell_args
+ def write_blank(self, row, col, blank, cell_format=None):
+ """
+ Write a blank cell with formatting to a worksheet cell. The blank
+ token is ignored and the format only is written to the cell.
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ blank: Any value. It is ignored.
+ cell_format: An optional cell Format object.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+
+ """
+ # Don't write a blank cell unless it has a format.
+ if cell_format is None:
+ return 0
+
+ # Check that row and col are valid and store max and min values.
+ if self._check_dimensions(row, col):
+ return -1
+
+ # Write previous row if in in-line string optimization mode.
+ if self.optimization and row > self.previous_row:
+ self._write_single_row(row)
+
+ # Store the cell data in the worksheet data table.
+ self.table[row][col] = cell_blank_tuple(cell_format)
+
+ return 0
+
+ @convert_cell_args
+ def write_formula(self, row, col, formula, cell_format=None, value=0):
+ """
+ Write a formula to a worksheet cell.
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ formula: Cell formula.
+ cell_format: An optional cell Format object.
+ value: An optional value for the formula. Default is 0.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+
+ """
+ # Check that row and col are valid and store max and min values.
+ if self._check_dimensions(row, col):
+ return -1
+
+ # Hand off array formulas.
+ if formula.startswith('{') and formula.endswith('}'):
+ return self.write_array_formula(row, col, row, col, formula,
+ cell_format, value)
+
+ # Remove the formula '=' sign if it exists.
+ if formula.startswith('='):
+ formula = formula.lstrip('=')
+
+ # Write previous row if in in-line string optimization mode.
+ if self.optimization and row > self.previous_row:
+ self._write_single_row(row)
+
+ # Store the cell data in the worksheet data table.
+ self.table[row][col] = cell_formula_tuple(formula, cell_format, value)
+
+ return 0
+
+ @convert_range_args
+ def write_array_formula(self, first_row, first_col, last_row, last_col,
+ formula, cell_format=None, value=0):
+ """
+ Write a formula to a worksheet cell.
+
+ Args:
+ first_row: The first row of the cell range. (zero indexed).
+ first_col: The first column of the cell range.
+ last_row: The last row of the cell range. (zero indexed).
+ last_col: The last column of the cell range.
+ formula: Cell formula.
+ cell_format: An optional cell Format object.
+ value: An optional value for the formula. Default is 0.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+
+ """
+
+ # Swap last row/col with first row/col as necessary.
+ if first_row > last_row:
+ first_row, last_row = last_row, first_row
+ if first_col > last_col:
+ first_col, last_col = last_col, first_col
+
+ # Check that row and col are valid and store max and min values
+ if self._check_dimensions(last_row, last_col):
+ return -1
+
+ # Define array range
+ if first_row == last_row and first_col == last_col:
+ cell_range = xl_rowcol_to_cell(first_row, first_col)
+ else:
+ cell_range = (xl_rowcol_to_cell(first_row, first_col) + ':'
+ + xl_rowcol_to_cell(last_row, last_col))
+
+ # Remove array formula braces and the leading =.
+ if formula[0] == '{':
+ formula = formula[1:]
+ if formula[0] == '=':
+ formula = formula[1:]
+ if formula[-1] == '}':
+ formula = formula[:-1]
+
+ # Write previous row if in in-line string optimization mode.
+ if self.optimization and first_row > self.previous_row:
+ self._write_single_row(first_row)
+
+ # Store the cell data in the worksheet data table.
+ self.table[first_row][first_col] = cell_arformula_tuple(formula,
+ cell_format,
+ value,
+ cell_range)
+
+ # Pad out the rest of the area with formatted zeroes.
+ if not self.optimization:
+ for row in range(first_row, last_row + 1):
+ for col in range(first_col, last_col + 1):
+ if row != first_row or col != first_col:
+ self.write_number(row, col, 0, cell_format)
+
+ return 0
+
+ @convert_cell_args
+ def write_datetime(self, row, col, date, cell_format=None):
+ """
+ Write a date or time to a worksheet cell.
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ date: Date and/or time as a datetime object.
+ cell_format: A cell Format object.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+
+ """
+ # Check that row and col are valid and store max and min values.
+ if self._check_dimensions(row, col):
+ return -1
+
+ # Write previous row if in in-line string optimization mode.
+ if self.optimization and row > self.previous_row:
+ self._write_single_row(row)
+
+ # Convert datetime to an Excel date.
+ number = self._convert_date_time(date)
+
+ # Add the default date format.
+ if cell_format is None:
+ cell_format = self.default_date_format
+
+ # Store the cell data in the worksheet data table.
+ self.table[row][col] = cell_number_tuple(number, cell_format)
+
+ return 0
+
+ @convert_cell_args
+ def write_boolean(self, row, col, boolean, cell_format=None):
+ """
+ Write a boolean value to a worksheet cell.
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ boolean: Cell data. bool type.
+ cell_format: An optional cell Format object.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+
+ """
+ # Check that row and col are valid and store max and min values.
+ if self._check_dimensions(row, col):
+ return -1
+
+ # Write previous row if in in-line string optimization mode.
+ if self.optimization and row > self.previous_row:
+ self._write_single_row(row)
+
+ if boolean:
+ value = 1
+ else:
+ value = 0
+
+ # Store the cell data in the worksheet data table.
+ self.table[row][col] = cell_boolean_tuple(value, cell_format)
+
+ return 0
+
+ # Write a hyperlink. This is comprised of two elements: the displayed
+ # string and the non-displayed link. The displayed string is the same as
+ # the link unless an alternative string is specified. The display string
+ # is written using the write_string() method. Therefore the max characters
+ # string limit applies.
+ #
+ # The hyperlink can be to a http, ftp, mail, internal sheet, or external
+ # directory urls.
+ @convert_cell_args
+ def write_url(self, row, col, url, cell_format=None,
+ string=None, tip=None):
+ """
+ Write a hyperlink to a worksheet cell.
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ url: Hyperlink url.
+ format: An optional cell Format object.
+ string: An optional display string for the hyperlink.
+ tip: An optional tooltip.
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+ -2: String longer than 32767 characters.
+ -3: URL longer than Excel limit of 255 characters
+ -4: Exceeds Excel limit of 65,530 urls per worksheet
+ """
+ # Set the displayed string to the URL unless defined by the user.
+ if string is None:
+ string = url
+
+ # Default to external link type such as 'http://' or 'external:'.
+ link_type = 1
+
+ # Remove the URI scheme from internal links.
+ if re.match("internal:", url):
+ url = url.replace('internal:', '')
+ string = string.replace('internal:', '')
+ link_type = 2
+
+ # Remove the URI scheme from external links and change the directory
+ # separator from Unix to Dos.
+ external = False
+ if re.match("external:", url):
+ url = url.replace('external:', '')
+ url = url.replace('/', '\\')
+ string = string.replace('external:', '')
+ string = string.replace('/', '\\')
+ external = True
+
+ # Strip the mailto header.
+ string = string.replace('mailto:', '')
+
+ # Check that row and col are valid and store max and min values
+ if self._check_dimensions(row, col):
+ return -1
+
+ # Check that the string is < 32767 chars
+ str_error = 0
+ if len(string) > self.xls_strmax:
+ warn("Ignoring URL since it exceeds Excel's string limit of "
+ "32767 characters")
+ return -2
+
+ # Copy string for use in hyperlink elements.
+ url_str = string
+
+ # External links to URLs and to other Excel workbooks have slightly
+ # different characteristics that we have to account for.
+ if link_type == 1:
+
+ # Split url into the link and optional anchor/location.
+ if '#' in url:
+ url, url_str = url.split('#', 1)
+ else:
+ url_str = None
+
+ url = self._escape_url(url)
+
+ if url_str is not None and not external:
+ url_str = self._escape_url(url_str)
+
+ # Add the file:/// URI to the url for Windows style "C:/" link and
+ # Network shares.
+ if re.match('\w:', url) or re.match(r'\\', url):
+ url = 'file:///' + url
+
+ # Convert a .\dir\file.xlsx link to dir\file.xlsx.
+ url = re.sub(r'^\.\\', '', url)
+
+ # Excel limits the escaped URL and location/anchor to 255 characters.
+ tmp_url_str = url_str or ''
+ if len(url) > 255 or len(tmp_url_str) > 255:
+ warn("Ignoring URL '%s' with link or location/anchor > 255 "
+ "characters since it exceeds Excel's limit for URLS" %
+ force_unicode(url))
+ return -3
+
+ # Check the limit of URLS per worksheet.
+ self.hlink_count += 1
+
+ if self.hlink_count > 65530:
+ warn("Ignoring URL '%s' since it exceeds Excel's limit of "
+ "65,530 URLS per worksheet." % force_unicode(url))
+ return -5
+
+ # Write previous row if in in-line string optimization mode.
+ if self.optimization == 1 and row > self.previous_row:
+ self._write_single_row(row)
+
+ # Add the default URL format.
+ if cell_format is None:
+ cell_format = self.default_url_format
+
+ # Write the hyperlink string.
+ self.write_string(row, col, string, cell_format)
+
+ # Store the hyperlink data in a separate structure.
+ self.hyperlinks[row][col] = {
+ 'link_type': link_type,
+ 'url': url,
+ 'str': url_str,
+ 'tip': tip}
+
+ return str_error
+
+ @convert_cell_args
+ def write_rich_string(self, row, col, *args):
+ """
+ Write a "rich" string with multiple formats to a worksheet cell.
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ string_parts: String and format pairs.
+ cell_format: Optional Format object.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+ -2: String truncated to 32k characters.
+ -3: 2 consecutive formats used.
+
+ """
+ tokens = list(args)
+ cell_format = None
+ str_length = 0
+ string_index = 0
+
+ # Check that row and col are valid and store max and min values
+ if self._check_dimensions(row, col):
+ return -1
+
+ # If the last arg is a format we use it as the cell format.
+ if isinstance(tokens[-1], Format):
+ cell_format = tokens.pop()
+
+ # Create a temp XMLWriter object and use it to write the rich string
+ # XML to a string.
+ fh = StringIO()
+ self.rstring = XMLwriter()
+ self.rstring._set_filehandle(fh)
+
+ # Create a temp format with the default font for unformatted fragments.
+ default = Format()
+
+ # Convert list of format, string tokens to pairs of (format, string)
+ # except for the first string fragment which doesn't require a default
+ # formatting run. Use the default for strings without a leading format.
+ fragments = []
+ previous = 'format'
+ pos = 0
+
+ for token in tokens:
+ if not isinstance(token, Format):
+ # Token is a string.
+ if previous != 'format':
+ # If previous token wasn't a format add one before string.
+ fragments.append(default)
+ fragments.append(token)
+ else:
+ # If previous token was a format just add the string.
+ fragments.append(token)
+
+ # Keep track of actual string str_length.
+ str_length += len(token)
+ previous = 'string'
+ else:
+ # Can't allow 2 formats in a row.
+ if previous == 'format' and pos > 0:
+ return -3
+
+ # Token is a format object. Add it to the fragment list.
+ fragments.append(token)
+ previous = 'format'
+
+ pos += 1
+
+ # If the first token is a string start the <r> element.
+ if not isinstance(fragments[0], Format):
+ self.rstring._xml_start_tag('r')
+
+ # Write the XML elements for the $format $string fragments.
+ for token in fragments:
+ if isinstance(token, Format):
+ # Write the font run.
+ self.rstring._xml_start_tag('r')
+ self._write_font(token)
+ else:
+ # Write the string fragment part, with whitespace handling.
+ attributes = []
+
+ if re.search('^\s', token) or re.search('\s$', token):
+ attributes.append(('xml:space', 'preserve'))
+
+ self.rstring._xml_data_element('t', token, attributes)
+ self.rstring._xml_end_tag('r')
+
+ # Read the in-memory string.
+ string = self.rstring.fh.getvalue()
+
+ # Check that the string is < 32767 chars.
+ if str_length > self.xls_strmax:
+ return -2
+
+ # Write a shared string or an in-line string in optimization mode.
+ if self.optimization == 0:
+ string_index = self.str_table._get_shared_string_index(string)
+ else:
+ string_index = string
+
+ # Write previous row if in in-line string optimization mode.
+ if self.optimization and row > self.previous_row:
+ self._write_single_row(row)
+
+ # Store the cell data in the worksheet data table.
+ self.table[row][col] = cell_string_tuple(string_index, cell_format)
+
+ return 0
+
+ @convert_cell_args
+ def write_row(self, row, col, data, cell_format=None):
+ """
+ Write a row of data starting from (row, col).
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ data: A list of tokens to be written with write().
+ format: An optional cell Format object.
+ Returns:
+ 0: Success.
+ other: Return value of write() method.
+
+ """
+ for token in data:
+ error = self.write(row, col, token, cell_format)
+ if error:
+ return error
+ col += 1
+
+ return 0
+
+ @convert_cell_args
+ def write_column(self, row, col, data, cell_format=None):
+ """
+ Write a column of data starting from (row, col).
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ data: A list of tokens to be written with write().
+ format: An optional cell Format object.
+ Returns:
+ 0: Success.
+ other: Return value of write() method.
+
+ """
+ for token in data:
+ error = self.write(row, col, token, cell_format)
+ if error:
+ return error
+ row += 1
+
+ return 0
+
+ @convert_cell_args
+ def insert_image(self, row, col, filename, options={}):
+ """
+ Insert an image with its top-left corner in a worksheet cell.
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ filename: Path and filename for image in PNG, JPG or BMP format.
+ options: Position, scale, url and data stream of the image.
+ Returns:
+ 0: Success.
+ """
+ x_offset = options.get('x_offset', 0)
+ y_offset = options.get('y_offset', 0)
+ x_scale = options.get('x_scale', 1)
+ y_scale = options.get('y_scale', 1)
+ url = options.get('url', None)
+ tip = options.get('tip', None)
+ anchor = options.get('positioning', None)
+ image_data = options.get('image_data', None)
+
+ if not image_data and not os.path.exists(filename):
+ warn("Image file '%s' not found." % force_unicode(filename))
+ return -1
+
+ self.images.append([row, col, filename, x_offset, y_offset,
+ x_scale, y_scale, url, tip, anchor, image_data])
+
+ @convert_cell_args
+ def insert_textbox(self, row, col, text, options=None):
+ """
+ Insert an textbox with its top-left corner in a worksheet cell.
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ text: The text for the textbox.
+ options: Textbox options.
+ Returns:
+ 0: Success.
+ """
+ if options is None:
+ options = {}
+
+ x_offset = options.get('x_offset', 0)
+ y_offset = options.get('y_offset', 0)
+ x_scale = options.get('x_scale', 1)
+ y_scale = options.get('y_scale', 1)
+
+ self.shapes.append([row, col, x_offset, y_offset,
+ x_scale, y_scale, text, options])
+
+ @convert_cell_args
+ def insert_chart(self, row, col, chart, options={}):
+ """
+ Insert an chart with its top-left corner in a worksheet cell.
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ chart: Chart object.
+ options: Position and scale of the chart.
+
+ Returns:
+ 0: Success.
+ """
+
+ # Ensure a chart isn't inserted more than once.
+ if (chart.already_inserted or chart.combined
+ and chart.combined.already_inserted):
+
+ warn('Chart cannot be inserted in a worksheet more than once.')
+ return
+ else:
+ chart.already_inserted = True
+
+ if chart.combined:
+ chart.combined.already_inserted = True
+
+ x_offset = options.get('x_offset', 0)
+ y_offset = options.get('y_offset', 0)
+ x_scale = options.get('x_scale', 1)
+ y_scale = options.get('y_scale', 1)
+
+ # Allow Chart to override the scale and offset.
+ if chart.x_scale != 1:
+ x_scale = chart.x_scale
+
+ if chart.y_scale != 1:
+ y_scale = chart.y_scale
+
+ if chart.x_offset:
+ x_offset = chart.x_offset
+
+ if chart.y_offset:
+ y_offset = chart.y_offset
+
+ self.charts.append([row, col, chart,
+ x_offset, y_offset,
+ x_scale, y_scale])
+
+ @convert_cell_args
+ def write_comment(self, row, col, comment, options={}):
+ """
+ Write a comment to a worksheet cell.
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ comment: Cell comment. Str.
+ options: Comment formatting options.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+ -2: String longer than 32k characters.
+
+ """
+ # Check that row and col are valid and store max and min values
+ if self._check_dimensions(row, col):
+ return -1
+
+ # Check that the comment string is < 32767 chars.
+ if len(comment) > self.xls_strmax:
+ return -2
+
+ self.has_vml = 1
+ self.has_comments = 1
+
+ # Process the properties of the cell comment.
+ self.comments[row][col] = \
+ self._comment_params(row, col, comment, options)
+
+ def show_comments(self):
+ """
+ Make any comments in the worksheet visible.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.comments_visible = 1
+
+ def set_comments_author(self, author):
+ """
+ Set the default author of the cell comments.
+
+ Args:
+ author: Comment author name. String.
+
+ Returns:
+ Nothing.
+
+ """
+ self.comments_author = author
+
+ def get_name(self):
+ """
+ Retrieve the worksheet name.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ # There is no set_name() method. Name must be set in add_worksheet().
+ return self.name
+
+ def activate(self):
+ """
+ Set this worksheet as the active worksheet, i.e. the worksheet that is
+ displayed when the workbook is opened. Also set it as selected.
+
+ Note: An active worksheet cannot be hidden.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.hidden = 0
+ self.selected = 1
+ self.worksheet_meta.activesheet = self.index
+
+ def select(self):
+ """
+ Set current worksheet as a selected worksheet, i.e. the worksheet
+ has its tab highlighted.
+
+ Note: A selected worksheet cannot be hidden.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.selected = 1
+ self.hidden = 0
+
+ def hide(self):
+ """
+ Hide the current worksheet.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.hidden = 1
+
+ # A hidden worksheet shouldn't be active or selected.
+ self.selected = 0
+
+ # TODO. Should add a check to see if the sheet is the global
+ # activesheet or firstsheet and reset them.
+
+ def set_first_sheet(self):
+ """
+ Set current worksheet as the first visible sheet. This is necessary
+ when there are a large number of worksheets and the activated
+ worksheet is not visible on the screen.
+
+ Note: A selected worksheet cannot be hidden.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.hidden = 0 # Active worksheet can't be hidden.
+ self.worksheet_meta.firstsheet = self.index
+
+ @convert_column_args
+ def set_column(self, firstcol, lastcol, width=None, cell_format=None,
+ options={}):
+ """
+ Set the width, and other properties of a single column or a
+ range of columns.
+
+ Args:
+ firstcol: First column (zero-indexed).
+ lastcol: Last column (zero-indexed). Can be same as firstcol.
+ width: Column width. (optional).
+ cell_format: Column cell_format. (optional).
+ options: Dict of options such as hidden and level.
+
+ Returns:
+ 0: Success.
+ -1: Column number is out of worksheet bounds.
+
+ """
+ # Ensure 2nd col is larger than first.
+ if firstcol > lastcol:
+ (firstcol, lastcol) = (lastcol, firstcol)
+
+ # Don't modify the row dimensions when checking the columns.
+ ignore_row = True
+
+ # Set optional column values.
+ hidden = options.get('hidden', False)
+ collapsed = options.get('collapsed', False)
+ level = options.get('level', 0)
+ # Store the column dimension only in some conditions.
+ if cell_format or (width and hidden):
+ ignore_col = False
+ else:
+ ignore_col = True
+
+ # Check that each column is valid and store the max and min values.
+ if self._check_dimensions(0, lastcol, ignore_row, ignore_col):
+ return -1
+ if self._check_dimensions(0, firstcol, ignore_row, ignore_col):
+ return -1
+
+ # Set the limits for the outline levels (0 <= x <= 7).
+ if level < 0:
+ level = 0
+ if level > 7:
+ level = 7
+
+ if level > self.outline_col_level:
+ self.outline_col_level = level
+
+ # Store the column data. Padded for sorting.
+ self.colinfo["%05d" % firstcol] = [firstcol, lastcol, width,
+ cell_format, hidden, level,
+ collapsed]
+
+ # Store the column change to allow optimizations.
+ self.col_size_changed = True
+
+ # Store the col sizes for use when calculating image vertices taking
+ # hidden columns into account. Also store the column formats.
+
+ # Set width to zero if col is hidden
+ if hidden:
+ width = 0
+
+ for col in range(firstcol, lastcol + 1):
+ self.col_sizes[col] = width
+ if cell_format:
+ self.col_formats[col] = cell_format
+
+ return 0
+
+ def set_row(self, row, height=None, cell_format=None, options={}):
+ """
+ Set the width, and other properties of a row.
+
+ Args:
+ row: Row number (zero-indexed).
+ height: Row width. (optional).
+ cell_format: Row cell_format. (optional).
+ options: Dict of options such as hidden, level and collapsed.
+
+ Returns:
+ 0: Success.
+ -1: Row number is out of worksheet bounds.
+
+ """
+ # Use minimum col in _check_dimensions().
+ if self.dim_colmin is not None:
+ min_col = self.dim_colmin
+ else:
+ min_col = 0
+
+ # Check that row is valid.
+ if self._check_dimensions(row, min_col):
+ return -1
+
+ if height is None:
+ height = self.default_row_height
+
+ # Set optional row values.
+ hidden = options.get('hidden', False)
+ collapsed = options.get('collapsed', False)
+ level = options.get('level', 0)
+
+ # If the height is 0 the row is hidden and the height is the default.
+ if height == 0:
+ hidden = 1
+ height = self.default_row_height
+
+ # Set the limits for the outline levels (0 <= x <= 7).
+ if level < 0:
+ level = 0
+ if level > 7:
+ level = 7
+
+ if level > self.outline_row_level:
+ self.outline_row_level = level
+
+ # Store the row properties.
+ self.set_rows[row] = [height, cell_format, hidden, level, collapsed]
+
+ # Store the row change to allow optimizations.
+ self.row_size_changed = True
+
+ if hidden:
+ height = 0
+
+ # Store the row sizes for use when calculating image vertices.
+ self.row_sizes[row] = height
+
+ def set_default_row(self, height=None, hide_unused_rows=False):
+ """
+ Set the default row properties.
+
+ Args:
+ height: Default height. Optional, defaults to 15.
+ hide_unused_rows: Hide unused rows. Optional, defaults to False.
+
+ Returns:
+ Nothing.
+
+ """
+ if height is None:
+ height = self.default_row_height
+
+ if height != self.original_row_height:
+ # Store the row change to allow optimizations.
+ self.row_size_changed = True
+ self.default_row_height = height
+
+ if hide_unused_rows:
+ self.default_row_zeroed = 1
+
+ @convert_range_args
+ def merge_range(self, first_row, first_col, last_row, last_col,
+ data, cell_format=None):
+ """
+ Merge a range of cells.
+
+ Args:
+ first_row: The first row of the cell range. (zero indexed).
+ first_col: The first column of the cell range.
+ last_row: The last row of the cell range. (zero indexed).
+ last_col: The last column of the cell range.
+ data: Cell data.
+ cell_format: Cell Format object.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+ other: Return value of write().
+
+ """
+ # Merge a range of cells. The first cell should contain the data and
+ # the others should be blank. All cells should have the same format.
+
+ # Excel doesn't allow a single cell to be merged
+ if first_row == last_row and first_col == last_col:
+ warn("Can't merge single cell")
+ return
+
+ # Swap last row/col with first row/col as necessary
+ if first_row > last_row:
+ (first_row, last_row) = (last_row, first_row)
+ if first_col > last_col:
+ (first_col, last_col) = (last_col, first_col)
+
+ # Check that column number is valid and store the max value
+ if self._check_dimensions(last_row, last_col) == -1:
+ return
+
+ # Store the merge range.
+ self.merge.append([first_row, first_col, last_row, last_col])
+
+ # Write the first cell
+ self.write(first_row, first_col, data, cell_format)
+
+ # Pad out the rest of the area with formatted blank cells.
+ for row in range(first_row, last_row + 1):
+ for col in range(first_col, last_col + 1):
+ if row == first_row and col == first_col:
+ continue
+ self.write_blank(row, col, '', cell_format)
+
+ @convert_range_args
+ def autofilter(self, first_row, first_col, last_row, last_col):
+ """
+ Set the autofilter area in the worksheet.
+
+ Args:
+ first_row: The first row of the cell range. (zero indexed).
+ first_col: The first column of the cell range.
+ last_row: The last row of the cell range. (zero indexed).
+ last_col: The last column of the cell range.
+
+ Returns:
+ Nothing.
+
+ """
+ # Reverse max and min values if necessary.
+ if last_row < first_row:
+ (first_row, last_row) = (last_row, first_row)
+ if last_col < first_col:
+ (first_col, last_col) = (last_col, first_col)
+
+ # Build up the print area range "Sheet1!$A$1:$C$13".
+ area = self._convert_name_area(first_row, first_col,
+ last_row, last_col)
+ ref = xl_range(first_row, first_col, last_row, last_col)
+
+ self.autofilter_area = area
+ self.autofilter_ref = ref
+ self.filter_range = [first_col, last_col]
+
+ def filter_column(self, col, criteria):
+ """
+ Set the column filter criteria.
+
+ Args:
+ col: Filter column (zero-indexed).
+ criteria: Filter criteria.
+
+ Returns:
+ Nothing.
+
+ """
+ if not self.autofilter_area:
+ warn("Must call autofilter() before filter_column()")
+ return
+
+ # Check for a column reference in A1 notation and substitute.
+ try:
+ int(col)
+ except ValueError:
+ # Convert col ref to a cell ref and then to a col number.
+ col_letter = col
+ (_, col) = xl_cell_to_rowcol(col + '1')
+
+ if col >= self.xls_colmax:
+ warn("Invalid column '%s'" % col_letter)
+ return
+
+ (col_first, col_last) = self.filter_range
+
+ # Reject column if it is outside filter range.
+ if col < col_first or col > col_last:
+ warn("Column '%d' outside autofilter() column range (%d, %d)"
+ % (col, col_first, col_last))
+ return
+
+ tokens = self._extract_filter_tokens(criteria)
+
+ if not (len(tokens) == 3 or len(tokens) == 7):
+ warn("Incorrect number of tokens in criteria '%s'" % criteria)
+
+ tokens = self._parse_filter_expression(criteria, tokens)
+
+ # Excel handles single or double custom filters as default filters.
+ # We need to check for them and handle them accordingly.
+ if len(tokens) == 2 and tokens[0] == 2:
+ # Single equality.
+ self.filter_column_list(col, [tokens[1]])
+ elif (len(tokens) == 5 and tokens[0] == 2 and tokens[2] == 1
+ and tokens[3] == 2):
+ # Double equality with "or" operator.
+ self.filter_column_list(col, [tokens[1], tokens[4]])
+ else:
+ # Non default custom filter.
+ self.filter_cols[col] = tokens
+ self.filter_type[col] = 0
+
+ self.filter_on = 1
+
+ def filter_column_list(self, col, filters):
+ """
+ Set the column filter criteria in Excel 2007 list style.
+
+ Args:
+ col: Filter column (zero-indexed).
+ filters: List of filter criteria to match.
+
+ Returns:
+ Nothing.
+
+ """
+ if not self.autofilter_area:
+ warn("Must call autofilter() before filter_column()")
+ return
+
+ # Check for a column reference in A1 notation and substitute.
+ try:
+ int(col)
+ except ValueError:
+ # Convert col ref to a cell ref and then to a col number.
+ col_letter = col
+ (_, col) = xl_cell_to_rowcol(col + '1')
+
+ if col >= self.xls_colmax:
+ warn("Invalid column '%s'" % col_letter)
+ return
+
+ (col_first, col_last) = self.filter_range
+
+ # Reject column if it is outside filter range.
+ if col < col_first or col > col_last:
+ warn("Column '%d' outside autofilter() column range "
+ "(%d,%d)" % (col, col_first, col_last))
+ return
+
+ self.filter_cols[col] = filters
+ self.filter_type[col] = 1
+ self.filter_on = 1
+
+ @convert_range_args
+ def data_validation(self, first_row, first_col, last_row, last_col,
+ options):
+ """
+ Add a data validation to a worksheet.
+
+ Args:
+ first_row: The first row of the cell range. (zero indexed).
+ first_col: The first column of the cell range.
+ last_row: The last row of the cell range. (zero indexed).
+ last_col: The last column of the cell range.
+ options: Data validation options.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+ -2: Incorrect parameter or option.
+ """
+ # Check that row and col are valid without storing the values.
+ if self._check_dimensions(first_row, first_col, True, True):
+ return -1
+ if self._check_dimensions(last_row, last_col, True, True):
+ return -1
+
+ # List of valid input parameters.
+ valid_parameters = {
+ 'validate': True,
+ 'criteria': True,
+ 'value': True,
+ 'source': True,
+ 'minimum': True,
+ 'maximum': True,
+ 'ignore_blank': True,
+ 'dropdown': True,
+ 'show_input': True,
+ 'input_title': True,
+ 'input_message': True,
+ 'show_error': True,
+ 'error_title': True,
+ 'error_message': True,
+ 'error_type': True,
+ 'other_cells': True,
+ }
+
+ # Check for valid input parameters.
+ for param_key in options.keys():
+ if param_key not in valid_parameters:
+ warn("Unknown parameter '%s' in data_validation()" % param_key)
+ return -2
+
+ # Map alternative parameter names 'source' or 'minimum' to 'value'.
+ if 'source' in options:
+ options['value'] = options['source']
+ if 'minimum' in options:
+ options['value'] = options['minimum']
+
+ # 'validate' is a required parameter.
+ if 'validate' not in options:
+ warn("Parameter 'validate' is required in data_validation()")
+ return -2
+
+ # List of valid validation types.
+ valid_types = {
+ 'any': 'none',
+ 'any value': 'none',
+ 'whole number': 'whole',
+ 'whole': 'whole',
+ 'integer': 'whole',
+ 'decimal': 'decimal',
+ 'list': 'list',
+ 'date': 'date',
+ 'time': 'time',
+ 'text length': 'textLength',
+ 'length': 'textLength',
+ 'custom': 'custom',
+ }
+
+ # Check for valid validation types.
+ if not options['validate'] in valid_types:
+ warn("Unknown validation type '%s' for parameter "
+ "'validate' in data_validation()" % options['validate'])
+ return -2
+ else:
+ options['validate'] = valid_types[options['validate']]
+
+ # No action is required for validation type 'any' if there are no
+ # input messages to display.
+ if (options['validate'] == 'none'
+ and options.get('input_title') is None
+ and options.get('input_message') is None):
+ return -2
+
+ # The any, list and custom validations don't have a criteria so we use
+ # a default of 'between'.
+ if (options['validate'] == 'none'
+ or options['validate'] == 'list'
+ or options['validate'] == 'custom'):
+ options['criteria'] = 'between'
+ options['maximum'] = None
+
+ # 'criteria' is a required parameter.
+ if 'criteria' not in options:
+ warn("Parameter 'criteria' is required in data_validation()")
+ return -2
+
+ # List of valid criteria types.
+ criteria_types = {
+ 'between': 'between',
+ 'not between': 'notBetween',
+ 'equal to': 'equal',
+ '=': 'equal',
+ '==': 'equal',
+ 'not equal to': 'notEqual',
+ '!=': 'notEqual',
+ '<>': 'notEqual',
+ 'greater than': 'greaterThan',
+ '>': 'greaterThan',
+ 'less than': 'lessThan',
+ '<': 'lessThan',
+ 'greater than or equal to': 'greaterThanOrEqual',
+ '>=': 'greaterThanOrEqual',
+ 'less than or equal to': 'lessThanOrEqual',
+ '<=': 'lessThanOrEqual',
+ }
+
+ # Check for valid criteria types.
+ if not options['criteria'] in criteria_types:
+ warn("Unknown criteria type '%s' for parameter "
+ "'criteria' in data_validation()" % options['criteria'])
+ return -2
+ else:
+ options['criteria'] = criteria_types[options['criteria']]
+
+ # 'Between' and 'Not between' criteria require 2 values.
+ if (options['criteria'] == 'between' or
+ options['criteria'] == 'notBetween'):
+ if 'maximum' not in options:
+ warn("Parameter 'maximum' is required in data_validation() "
+ "when using 'between' or 'not between' criteria")
+ return -2
+ else:
+ options['maximum'] = None
+
+ # List of valid error dialog types.
+ error_types = {
+ 'stop': 0,
+ 'warning': 1,
+ 'information': 2,
+ }
+
+ # Check for valid error dialog types.
+ if 'error_type' not in options:
+ options['error_type'] = 0
+ elif not options['error_type'] in error_types:
+ warn("Unknown criteria type '%s' for parameter 'error_type' "
+ "in data_validation()" % options['error_type'])
+ return -2
+ else:
+ options['error_type'] = error_types[options['error_type']]
+
+ # Convert date/times value if required.
+ if options['validate'] == 'date' or options['validate'] == 'time':
+
+ if options['value']:
+ if not supported_datetime(options['value']):
+ warn("Data validation 'value/minimum' must be a "
+ "datetime object.")
+ return -2
+ else:
+ date_time = self._convert_date_time(options['value'])
+ # Format date number to the same precision as Excel.
+ options['value'] = "%.16g" % date_time
+
+ if options['maximum']:
+ if not supported_datetime(options['maximum']):
+ warn("Conditional format 'maximum' must be a "
+ "datetime object.")
+ return -2
+ else:
+ date_time = self._convert_date_time(options['maximum'])
+ options['maximum'] = "%.16g" % date_time
+
+ # Check that the input title doesn't exceed the maximum length.
+ if options.get('input_title') and len(options['input_title']) > 32:
+ warn("Length of input title '%s' exceeds Excel's limit of 32"
+ % force_unicode(options['input_title']))
+ return -2
+
+ # Check that the error title doesn't exceed the maximum length.
+ if options.get('error_title') and len(options['error_title']) > 32:
+ warn("Length of error title '%s' exceeds Excel's limit of 32"
+ % force_unicode(options['error_title']))
+ return -2
+
+ # Check that the input message doesn't exceed the maximum length.
+ if (options.get('input_message')
+ and len(options['input_message']) > 255):
+ warn("Length of input message '%s' exceeds Excel's limit of 255"
+ % force_unicode(options['input_message']))
+ return -2
+
+ # Check that the error message doesn't exceed the maximum length.
+ if (options.get('error_message')
+ and len(options['error_message']) > 255):
+ warn("Length of error message '%s' exceeds Excel's limit of 255"
+ % force_unicode(options['error_message']))
+ return -2
+
+ # Check that the input list doesn't exceed the maximum length.
+ if options['validate'] == 'list' and type(options['value']) is list:
+ formula = self._csv_join(*options['value'])
+ if len(formula) > 255:
+ warn("Length of list items '%s' exceeds Excel's limit of "
+ "255, use a formula range instead"
+ % force_unicode(formula))
+ return -2
+
+ # Set some defaults if they haven't been defined by the user.
+ if 'ignore_blank' not in options:
+ options['ignore_blank'] = 1
+ if 'dropdown' not in options:
+ options['dropdown'] = 1
+ if 'show_input' not in options:
+ options['show_input'] = 1
+ if 'show_error' not in options:
+ options['show_error'] = 1
+
+ # These are the cells to which the validation is applied.
+ options['cells'] = [[first_row, first_col, last_row, last_col]]
+
+ # A (for now) undocumented parameter to pass additional cell ranges.
+ if 'other_cells' in options:
+ options['cells'].extend(options['other_cells'])
+
+ # Store the validation information until we close the worksheet.
+ self.validations.append(options)
+
+ @convert_range_args
+ def conditional_format(self, first_row, first_col, last_row, last_col,
+ options=None):
+ """
+ Add a conditional format to a worksheet.
+
+ Args:
+ first_row: The first row of the cell range. (zero indexed).
+ first_col: The first column of the cell range.
+ last_row: The last row of the cell range. (zero indexed).
+ last_col: The last column of the cell range.
+ options: Conditional format options.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+ -2: Incorrect parameter or option.
+ """
+ # Check that row and col are valid without storing the values.
+ if self._check_dimensions(first_row, first_col, True, True):
+ return -1
+ if self._check_dimensions(last_row, last_col, True, True):
+ return -1
+
+ if options is None:
+ options = {}
+
+ # Copy the user defined options so they aren't modified.
+ options = options.copy()
+
+ # List of valid input parameters.
+ valid_parameter = {
+ 'type': True,
+ 'format': True,
+ 'criteria': True,
+ 'value': True,
+ 'minimum': True,
+ 'maximum': True,
+ 'min_type': True,
+ 'mid_type': True,
+ 'max_type': True,
+ 'min_value': True,
+ 'mid_value': True,
+ 'max_value': True,
+ 'min_color': True,
+ 'mid_color': True,
+ 'max_color': True,
+ 'min_length': True,
+ 'max_length': True,
+ 'multi_range': True,
+ 'bar_color': 1}
+
+ # Check for valid input parameters.
+ for param_key in options.keys():
+ if param_key not in valid_parameter:
+ warn("Unknown parameter '%s' in conditional_formatting()" %
+ param_key)
+ return -2
+
+ # 'type' is a required parameter.
+ if 'type' not in options:
+ warn("Parameter 'type' is required in conditional_formatting()")
+ return -2
+
+ # List of valid validation types.
+ valid_type = {
+ 'cell': 'cellIs',
+ 'date': 'date',
+ 'time': 'time',
+ 'average': 'aboveAverage',
+ 'duplicate': 'duplicateValues',
+ 'unique': 'uniqueValues',
+ 'top': 'top10',
+ 'bottom': 'top10',
+ 'text': 'text',
+ 'time_period': 'timePeriod',
+ 'blanks': 'containsBlanks',
+ 'no_blanks': 'notContainsBlanks',
+ 'errors': 'containsErrors',
+ 'no_errors': 'notContainsErrors',
+ '2_color_scale': '2_color_scale',
+ '3_color_scale': '3_color_scale',
+ 'data_bar': 'dataBar',
+ 'formula': 'expression'}
+
+ # Check for valid validation types.
+ if options['type'] not in valid_type:
+ warn("Unknown validation type '%s' for parameter 'type' "
+ "in conditional_formatting()" % options['type'])
+ return -2
+ else:
+ if options['type'] == 'bottom':
+ options['direction'] = 'bottom'
+ options['type'] = valid_type[options['type']]
+
+ # List of valid criteria types.
+ criteria_type = {
+ 'between': 'between',
+ 'not between': 'notBetween',
+ 'equal to': 'equal',
+ '=': 'equal',
+ '==': 'equal',
+ 'not equal to': 'notEqual',
+ '!=': 'notEqual',
+ '<>': 'notEqual',
+ 'greater than': 'greaterThan',
+ '>': 'greaterThan',
+ 'less than': 'lessThan',
+ '<': 'lessThan',
+ 'greater than or equal to': 'greaterThanOrEqual',
+ '>=': 'greaterThanOrEqual',
+ 'less than or equal to': 'lessThanOrEqual',
+ '<=': 'lessThanOrEqual',
+ 'containing': 'containsText',
+ 'not containing': 'notContains',
+ 'begins with': 'beginsWith',
+ 'ends with': 'endsWith',
+ 'yesterday': 'yesterday',
+ 'today': 'today',
+ 'last 7 days': 'last7Days',
+ 'last week': 'lastWeek',
+ 'this week': 'thisWeek',
+ 'continue week': 'continueWeek',
+ 'last month': 'lastMonth',
+ 'this month': 'thisMonth',
+ 'continue month': 'continueMonth'}
+
+ # Check for valid criteria types.
+ if 'criteria' in options and options['criteria'] in criteria_type:
+ options['criteria'] = criteria_type[options['criteria']]
+
+ # Convert date/times value if required.
+ if options['type'] == 'date' or options['type'] == 'time':
+ options['type'] = 'cellIs'
+
+ if 'value' in options:
+ if not supported_datetime(options['value']):
+ warn("Conditional format 'value' must be a "
+ "datetime object.")
+ return -2
+ else:
+ date_time = self._convert_date_time(options['value'])
+ # Format date number to the same precision as Excel.
+ options['value'] = "%.16g" % date_time
+
+ if 'minimum' in options:
+ if not supported_datetime(options['minimum']):
+ warn("Conditional format 'minimum' must be a "
+ "datetime object.")
+ return -2
+ else:
+ date_time = self._convert_date_time(options['minimum'])
+ options['minimum'] = "%.16g" % date_time
+
+ if 'maximum' in options:
+ if not supported_datetime(options['maximum']):
+ warn("Conditional format 'maximum' must be a "
+ "datetime object.")
+ return -2
+ else:
+ date_time = self._convert_date_time(options['maximum'])
+ options['maximum'] = "%.16g" % date_time
+
+ # Swap last row/col for first row/col as necessary
+ if first_row > last_row:
+ first_row, last_row = last_row, first_row
+
+ if first_col > last_col:
+ first_col, last_col = last_col, first_col
+
+ # Set the formatting range.
+ # If the first and last cell are the same write a single cell.
+ if first_row == last_row and first_col == last_col:
+ cell_range = xl_rowcol_to_cell(first_row, first_col)
+ start_cell = cell_range
+ else:
+ cell_range = xl_range(first_row, first_col, last_row, last_col)
+ start_cell = xl_rowcol_to_cell(first_row, first_col)
+
+ # Override with user defined multiple range if provided.
+ if 'multi_range' in options:
+ cell_range = options['multi_range']
+ cell_range = cell_range.replace('$', '')
+
+ # Get the dxf format index.
+ if 'format' in options and options['format']:
+ options['format'] = options['format']._get_dxf_index()
+
+ # Set the priority based on the order of adding.
+ options['priority'] = self.dxf_priority
+ self.dxf_priority += 1
+
+ # Special handling of text criteria.
+ if options['type'] == 'text':
+
+ if options['criteria'] == 'containsText':
+ options['type'] = 'containsText'
+ options['formula'] = ('NOT(ISERROR(SEARCH("%s",%s)))'
+ % (options['value'], start_cell))
+ elif options['criteria'] == 'notContains':
+ options['type'] = 'notContainsText'
+ options['formula'] = ('ISERROR(SEARCH("%s",%s))'
+ % (options['value'], start_cell))
+ elif options['criteria'] == 'beginsWith':
+ options['type'] = 'beginsWith'
+ options['formula'] = ('LEFT(%s,%d)="%s"'
+ % (start_cell,
+ len(options['value']),
+ options['value']))
+ elif options['criteria'] == 'endsWith':
+ options['type'] = 'endsWith'
+ options['formula'] = ('RIGHT(%s,%d)="%s"'
+ % (start_cell,
+ len(options['value']),
+ options['value']))
+ else:
+ warn("Invalid text criteria 'options['criteria']' "
+ "in conditional_formatting()")
+
+ # Special handling of time time_period criteria.
+ if options['type'] == 'timePeriod':
+
+ if options['criteria'] == 'yesterday':
+ options['formula'] = 'FLOOR(%s,1)=TODAY()-1' % start_cell
+
+ elif options['criteria'] == 'today':
+ options['formula'] = 'FLOOR(%s,1)=TODAY()' % start_cell
+
+ elif options['criteria'] == 'tomorrow':
+ options['formula'] = 'FLOOR(%s,1)=TODAY()+1' % start_cell
+
+ elif options['criteria'] == 'last7Days':
+ options['formula'] = \
+ ('AND(TODAY()-FLOOR(%s,1)<=6,FLOOR(%s,1)<=TODAY())' %
+ (start_cell, start_cell))
+
+ elif options['criteria'] == 'lastWeek':
+ options['formula'] = \
+ ('AND(TODAY()-ROUNDDOWN(%s,0)>=(WEEKDAY(TODAY())),'
+ 'TODAY()-ROUNDDOWN(%s,0)<(WEEKDAY(TODAY())+7))' %
+ (start_cell, start_cell))
+
+ elif options['criteria'] == 'thisWeek':
+ options['formula'] = \
+ ('AND(TODAY()-ROUNDDOWN(%s,0)<=WEEKDAY(TODAY())-1,'
+ 'ROUNDDOWN(%s,0)-TODAY()<=7-WEEKDAY(TODAY()))' %
+ (start_cell, start_cell))
+
+ elif options['criteria'] == 'continueWeek':
+ options['formula'] = \
+ ('AND(ROUNDDOWN(%s,0)-TODAY()>(7-WEEKDAY(TODAY())),'
+ 'ROUNDDOWN(%s,0)-TODAY()<(15-WEEKDAY(TODAY())))' %
+ (start_cell, start_cell))
+
+ elif options['criteria'] == 'lastMonth':
+ options['formula'] = \
+ ('AND(MONTH(%s)=MONTH(TODAY())-1,OR(YEAR(%s)=YEAR('
+ 'TODAY()),AND(MONTH(%s)=1,YEAR(A1)=YEAR(TODAY())-1)))' %
+ (start_cell, start_cell, start_cell))
+
+ elif options['criteria'] == 'thisMonth':
+ options['formula'] = \
+ ('AND(MONTH(%s)=MONTH(TODAY()),YEAR(%s)=YEAR(TODAY()))' %
+ (start_cell, start_cell))
+
+ elif options['criteria'] == 'continueMonth':
+ options['formula'] = \
+ ('AND(MONTH(%s)=MONTH(TODAY())+1,OR(YEAR(%s)=YEAR('
+ 'TODAY()),AND(MONTH(%s)=12,YEAR(%s)=YEAR(TODAY())+1)))' %
+ (start_cell, start_cell, start_cell, start_cell))
+
+ else:
+ warn("Invalid time_period criteria 'options['criteria']' "
+ "in conditional_formatting()")
+
+ # Special handling of blanks/error types.
+ if options['type'] == 'containsBlanks':
+ options['formula'] = 'LEN(TRIM(%s))=0' % start_cell
+
+ if options['type'] == 'notContainsBlanks':
+ options['formula'] = 'LEN(TRIM(%s))>0' % start_cell
+
+ if options['type'] == 'containsErrors':
+ options['formula'] = 'ISERROR(%s)' % start_cell
+
+ if options['type'] == 'notContainsErrors':
+ options['formula'] = 'NOT(ISERROR(%s))' % start_cell
+
+ # Special handling for 2 color scale.
+ if options['type'] == '2_color_scale':
+ options['type'] = 'colorScale'
+
+ # Color scales don't use any additional formatting.
+ options['format'] = None
+
+ # Turn off 3 color parameters.
+ options['mid_type'] = None
+ options['mid_color'] = None
+
+ options.setdefault('min_type', 'min')
+ options.setdefault('max_type', 'max')
+ options.setdefault('min_value', 0)
+ options.setdefault('max_value', 0)
+ options.setdefault('min_color', '#FF7128')
+ options.setdefault('max_color', '#FFEF9C')
+
+ options['min_color'] = xl_color(options['min_color'])
+ options['max_color'] = xl_color(options['max_color'])
+
+ # Special handling for 3 color scale.
+ if options['type'] == '3_color_scale':
+ options['type'] = 'colorScale'
+
+ # Color scales don't use any additional formatting.
+ options['format'] = None
+
+ options.setdefault('min_type', 'min')
+ options.setdefault('mid_type', 'percentile')
+ options.setdefault('max_type', 'max')
+ options.setdefault('min_value', 0)
+ options.setdefault('max_value', 0)
+ options.setdefault('min_color', '#F8696B')
+ options.setdefault('mid_color', '#FFEB84')
+ options.setdefault('max_color', '#63BE7B')
+
+ options['min_color'] = xl_color(options['min_color'])
+ options['mid_color'] = xl_color(options['mid_color'])
+ options['max_color'] = xl_color(options['max_color'])
+
+ # Set a default mid value.
+ if 'mid_value' not in options:
+ options['mid_value'] = 50
+
+ # Special handling for data bar.
+ if options['type'] == 'dataBar':
+
+ # Color scales don't use any additional formatting.
+ options['format'] = None
+
+ options.setdefault('min_type', 'min')
+ options.setdefault('max_type', 'max')
+ options.setdefault('min_value', 0)
+ options.setdefault('max_value', 0)
+ options.setdefault('bar_color', '#638EC6')
+
+ options['bar_color'] = xl_color(options['bar_color'])
+
+ # Store the validation information until we close the worksheet.
+ if cell_range in self.cond_formats:
+ self.cond_formats[cell_range].append(options)
+ else:
+ self.cond_formats[cell_range] = [options]
+
+ @convert_range_args
+ def add_table(self, first_row, first_col, last_row, last_col,
+ options=None):
+ """
+ Add an Excel table to a worksheet.
+
+ Args:
+ first_row: The first row of the cell range. (zero indexed).
+ first_col: The first column of the cell range.
+ last_row: The last row of the cell range. (zero indexed).
+ last_col: The last column of the cell range.
+ options: Table format options. (Optional)
+
+ Returns:
+ 0: Success.
+ -1: Not supported in optimization mode.
+ -2: Row or column is out of worksheet bounds.
+ -3: Incorrect parameter or option.
+ """
+ table = {}
+ col_formats = {}
+
+ if options is None:
+ options = {}
+
+ if self.optimization == 1:
+ warn("add_table() isn't supported when set_optimization() is on")
+ return -1
+
+ # Check that row and col are valid without storing the values.
+ if self._check_dimensions(first_row, first_col, True, True):
+ return -2
+ if self._check_dimensions(last_row, last_col, True, True):
+ return -2
+
+ # List of valid input parameters.
+ valid_parameter = {
+ 'autofilter': True,
+ 'banded_columns': True,
+ 'banded_rows': True,
+ 'columns': True,
+ 'data': True,
+ 'first_column': True,
+ 'header_row': True,
+ 'last_column': True,
+ 'name': True,
+ 'style': True,
+ 'total_row': True,
+ }
+
+ # Check for valid input parameters.
+ for param_key in options.keys():
+ if param_key not in valid_parameter:
+ warn("Unknown parameter '%s' in add_table()" % param_key)
+ return -3
+
+ # Turn on Excel's defaults.
+ options['banded_rows'] = options.get('banded_rows', True)
+ options['header_row'] = options.get('header_row', True)
+ options['autofilter'] = options.get('autofilter', True)
+
+ # Set the table options.
+ table['show_first_col'] = options.get('first_column', False)
+ table['show_last_col'] = options.get('last_column', False)
+ table['show_row_stripes'] = options.get('banded_rows', False)
+ table['show_col_stripes'] = options.get('banded_columns', False)
+ table['header_row_count'] = options.get('header_row', 0)
+ table['totals_row_shown'] = options.get('total_row', False)
+
+ # Set the table name.
+ if 'name' in options:
+ name = options['name']
+ table['name'] = name
+
+ if ' ' in name:
+ warn("Name '%s' in add_table() cannot contain spaces"
+ % force_unicode(name))
+ return -3
+
+ # Warn if the name contains invalid chars as defined by Excel.
+ if (not re.match(r'^[\w\\][\w\\.]*$', name, re.UNICODE)
+ or re.match(r'^\d', name)):
+ warn("Invalid Excel characters in add_table(): '%s'"
+ % force_unicode(name))
+ return -1
+
+ # Warn if the name looks like a cell name.
+ if re.match(r'^[a-zA-Z][a-zA-Z]?[a-dA-D]?[0-9]+$', name):
+ warn("Name looks like a cell name in add_table(): '%s'"
+ % force_unicode(name))
+ return -1
+
+ # Warn if the name looks like a R1C1 cell reference.
+ if (re.match(r'^[rcRC]$', name)
+ or re.match(r'^[rcRC]\d+[rcRC]\d+$', name)):
+ warn("Invalid name '%s' like a RC cell ref in add_table()"
+ % force_unicode(name))
+ return -1
+
+ # Set the table style.
+ if 'style' in options:
+ table['style'] = options['style']
+ # Remove whitespace from style name.
+ table['style'] = table['style'].replace(' ', '')
+ else:
+ table['style'] = "TableStyleMedium9"
+
+ # Swap last row/col for first row/col as necessary.
+ if first_row > last_row:
+ (first_row, last_row) = (last_row, first_row)
+ if first_col > last_col:
+ (first_col, last_col) = (last_col, first_col)
+
+ # Set the data range rows (without the header and footer).
+ first_data_row = first_row
+ last_data_row = last_row
+
+ if options.get('header_row'):
+ first_data_row += 1
+
+ if options.get('total_row'):
+ last_data_row -= 1
+
+ # Set the table and autofilter ranges.
+ table['range'] = xl_range(first_row, first_col,
+ last_row, last_col)
+
+ table['a_range'] = xl_range(first_row, first_col,
+ last_data_row, last_col)
+
+ # If the header row if off the default is to turn autofilter off.
+ if not options['header_row']:
+ options['autofilter'] = 0
+
+ # Set the autofilter range.
+ if options['autofilter']:
+ table['autofilter'] = table['a_range']
+
+ # Add the table columns.
+ col_id = 1
+ table['columns'] = []
+ seen_names = {}
+
+ for col_num in range(first_col, last_col + 1):
+ # Set up the default column data.
+ col_data = {
+ 'id': col_id,
+ 'name': 'Column' + str(col_id),
+ 'total_string': '',
+ 'total_function': '',
+ 'total_value': 0,
+ 'formula': '',
+ 'format': None,
+ 'name_format': None,
+ }
+
+ # Overwrite the defaults with any user defined values.
+ if 'columns' in options:
+ # Check if there are user defined values for this column.
+ if col_id <= len(options['columns']):
+ user_data = options['columns'][col_id - 1]
+ else:
+ user_data = None
+
+ if user_data:
+ # Get the column format.
+ xformat = user_data.get('format', None)
+
+ # Map user defined values to internal values.
+ if user_data.get('header'):
+ col_data['name'] = user_data['header']
+
+ # Excel requires unique case insensitive header names.
+ header_name = col_data['name']
+ name = header_name.lower()
+ if name in seen_names:
+ warn("Duplicate header name in add_table(): '%s'"
+ % force_unicode(name))
+ return -1
+ else:
+ seen_names[name] = True
+
+ col_data['name_format'] = user_data.get('header_format')
+
+ # Handle the column formula.
+ if 'formula' in user_data and user_data['formula']:
+ formula = user_data['formula']
+
+ # Remove the formula '=' sign if it exists.
+ if formula.startswith('='):
+ formula = formula.lstrip('=')
+
+ # Covert Excel 2010 "@" ref to 2007 "#This Row".
+ formula = formula.replace('@', '[#This Row],')
+
+ col_data['formula'] = formula
+
+ for row in range(first_data_row, last_data_row + 1):
+ self.write_formula(row, col_num, formula, xformat)
+
+ # Handle the function for the total row.
+ if user_data.get('total_function'):
+ function = user_data['total_function']
+
+ # Massage the function name.
+ function = function.lower()
+ function = function.replace('_', '')
+ function = function.replace(' ', '')
+
+ if function == 'countnums':
+ function = 'countNums'
+ if function == 'stddev':
+ function = 'stdDev'
+
+ col_data['total_function'] = function
+
+ formula = \
+ self._table_function_to_formula(function,
+ col_data['name'])
+
+ value = user_data.get('total_value', 0)
+
+ self.write_formula(last_row, col_num, formula, xformat,
+ value)
+
+ elif user_data.get('total_string'):
+ # Total label only (not a function).
+ total_string = user_data['total_string']
+ col_data['total_string'] = total_string
+
+ self.write_string(last_row, col_num, total_string,
+ user_data.get('format'))
+
+ # Get the dxf format index.
+ if xformat is not None:
+ col_data['format'] = xformat._get_dxf_index()
+
+ # Store the column format for writing the cell data.
+ # It doesn't matter if it is undefined.
+ col_formats[col_id - 1] = xformat
+
+ # Store the column data.
+ table['columns'].append(col_data)
+
+ # Write the column headers to the worksheet.
+ if options['header_row']:
+ self.write_string(first_row, col_num, col_data['name'],
+ col_data['name_format'])
+
+ col_id += 1
+
+ # Write the cell data if supplied.
+ if 'data' in options:
+ data = options['data']
+
+ i = 0 # For indexing the row data.
+ for row in range(first_data_row, last_data_row + 1):
+ j = 0 # For indexing the col data.
+ for col in range(first_col, last_col + 1):
+ if i < len(data) and j < len(data[i]):
+ token = data[i][j]
+ if j in col_formats:
+ self.write(row, col, token, col_formats[j])
+ else:
+ self.write(row, col, token, None)
+ j += 1
+ i += 1
+
+ # Store the table data.
+ self.tables.append(table)
+
+ return table
+
+ @convert_cell_args
+ def add_sparkline(self, row, col, options):
+ """
+ Add sparklines to the worksheet.
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ options: Sparkline formatting options.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+ -2: Incorrect parameter or option.
+
+ """
+
+ # Check that row and col are valid without storing the values.
+ if self._check_dimensions(row, col, True, True):
+ return -1
+
+ sparkline = {'locations': [xl_rowcol_to_cell(row, col)]}
+
+ # List of valid input parameters.
+ valid_parameters = {
+ 'location': True,
+ 'range': True,
+ 'type': True,
+ 'high_point': True,
+ 'low_point': True,
+ 'negative_points': True,
+ 'first_point': True,
+ 'last_point': True,
+ 'markers': True,
+ 'style': True,
+ 'series_color': True,
+ 'negative_color': True,
+ 'markers_color': True,
+ 'first_color': True,
+ 'last_color': True,
+ 'high_color': True,
+ 'low_color': True,
+ 'max': True,
+ 'min': True,
+ 'axis': True,
+ 'reverse': True,
+ 'empty_cells': True,
+ 'show_hidden': True,
+ 'plot_hidden': True,
+ 'date_axis': True,
+ 'weight': True,
+ }
+
+ # Check for valid input parameters.
+ for param_key in options.keys():
+ if param_key not in valid_parameters:
+ warn("Unknown parameter '%s' in add_sparkline()" % param_key)
+ return -1
+
+ # 'range' is a required parameter.
+ if 'range' not in options:
+ warn("Parameter 'range' is required in add_sparkline()")
+ return -2
+
+ # Handle the sparkline type.
+ spark_type = options.get('type', 'line')
+
+ if spark_type not in ('line', 'column', 'win_loss'):
+ warn("Parameter 'type' must be 'line', 'column' "
+ "or 'win_loss' in add_sparkline()")
+ return -2
+
+ if spark_type == 'win_loss':
+ spark_type = 'stacked'
+ sparkline['type'] = spark_type
+
+ # We handle single location/range values or list of values.
+ if 'location' in options:
+ if type(options['location']) is list:
+ sparkline['locations'] = options['location']
+ else:
+ sparkline['locations'] = [options['location']]
+
+ if type(options['range']) is list:
+ sparkline['ranges'] = options['range']
+ else:
+ sparkline['ranges'] = [options['range']]
+
+ range_count = len(sparkline['ranges'])
+ location_count = len(sparkline['locations'])
+
+ # The ranges and locations must match.
+ if range_count != location_count:
+ warn("Must have the same number of location and range "
+ "parameters in add_sparkline()")
+ return -2
+
+ # Store the count.
+ sparkline['count'] = len(sparkline['locations'])
+
+ # Get the worksheet name for the range conversion below.
+ sheetname = quote_sheetname(self.name)
+
+ # Cleanup the input ranges.
+ new_ranges = []
+ for spark_range in sparkline['ranges']:
+
+ # Remove the absolute reference $ symbols.
+ spark_range = spark_range.replace('$', '')
+
+ # Remove the = from formula.
+ spark_range = spark_range.lstrip('=')
+
+ # Convert a simple range into a full Sheet1!A1:D1 range.
+ if '!' not in spark_range:
+ spark_range = sheetname + "!" + spark_range
+
+ new_ranges.append(spark_range)
+
+ sparkline['ranges'] = new_ranges
+
+ # Cleanup the input locations.
+ new_locations = []
+ for location in sparkline['locations']:
+ location = location.replace('$', '')
+ new_locations.append(location)
+
+ sparkline['locations'] = new_locations
+
+ # Map options.
+ sparkline['high'] = options.get('high_point')
+ sparkline['low'] = options.get('low_point')
+ sparkline['negative'] = options.get('negative_points')
+ sparkline['first'] = options.get('first_point')
+ sparkline['last'] = options.get('last_point')
+ sparkline['markers'] = options.get('markers')
+ sparkline['min'] = options.get('min')
+ sparkline['max'] = options.get('max')
+ sparkline['axis'] = options.get('axis')
+ sparkline['reverse'] = options.get('reverse')
+ sparkline['hidden'] = options.get('show_hidden')
+ sparkline['weight'] = options.get('weight')
+
+ # Map empty cells options.
+ empty = options.get('empty_cells', '')
+
+ if empty == 'zero':
+ sparkline['empty'] = 0
+ elif empty == 'connect':
+ sparkline['empty'] = 'span'
+ else:
+ sparkline['empty'] = 'gap'
+
+ # Map the date axis range.
+ date_range = options.get('date_axis')
+
+ if date_range and '!' not in date_range:
+ date_range = sheetname + "!" + date_range
+
+ sparkline['date_axis'] = date_range
+
+ # Set the sparkline styles.
+ style_id = options.get('style', 0)
+ style = get_sparkline_style(style_id)
+
+ sparkline['series_color'] = style['series']
+ sparkline['negative_color'] = style['negative']
+ sparkline['markers_color'] = style['markers']
+ sparkline['first_color'] = style['first']
+ sparkline['last_color'] = style['last']
+ sparkline['high_color'] = style['high']
+ sparkline['low_color'] = style['low']
+
+ # Override the style colors with user defined colors.
+ self._set_spark_color(sparkline, options, 'series_color')
+ self._set_spark_color(sparkline, options, 'negative_color')
+ self._set_spark_color(sparkline, options, 'markers_color')
+ self._set_spark_color(sparkline, options, 'first_color')
+ self._set_spark_color(sparkline, options, 'last_color')
+ self._set_spark_color(sparkline, options, 'high_color')
+ self._set_spark_color(sparkline, options, 'low_color')
+
+ self.sparklines.append(sparkline)
+
+ @convert_range_args
+ def set_selection(self, first_row, first_col, last_row, last_col):
+ """
+ Set the selected cell or cells in a worksheet
+
+ Args:
+ first_row: The first row of the cell range. (zero indexed).
+ first_col: The first column of the cell range.
+ last_row: The last row of the cell range. (zero indexed).
+ last_col: The last column of the cell range.
+
+ Returns:
+ 0: Nothing.
+ """
+ pane = None
+
+ # Range selection. Do this before swapping max/min to allow the
+ # selection direction to be reversed.
+ active_cell = xl_rowcol_to_cell(first_row, first_col)
+
+ # Swap last row/col for first row/col if necessary
+ if first_row > last_row:
+ (first_row, last_row) = (last_row, first_row)
+
+ if first_col > last_col:
+ (first_col, last_col) = (last_col, first_col)
+
+ # If the first and last cell are the same write a single cell.
+ if (first_row == last_row) and (first_col == last_col):
+ sqref = active_cell
+ else:
+ sqref = xl_range(first_row, first_col, last_row, last_col)
+
+ # Selection isn't set for cell A1.
+ if sqref == 'A1':
+ return
+
+ self.selections = [[pane, active_cell, sqref]]
+
+ def outline_settings(self, outline_on=1, outline_below=1, outline_right=1,
+ outline_style=0):
+ """
+ Control outline settings.
+
+ Args:
+ outline_on: Outlines are visible. Optional, defaults to True.
+ outline_below: Show row outline symbols below the outline bar.
+ Optional, defaults to True.
+ outline_right: Show column outline symbols to the right of the
+ outline bar. Optional, defaults to True.
+ outline_style: Use Automatic style. Optional, defaults to False.
+
+ Returns:
+ 0: Nothing.
+ """
+ self.outline_on = outline_on
+ self.outline_below = outline_below
+ self.outline_right = outline_right
+ self.outline_style = outline_style
+
+ self.outline_changed = True
+
+ @convert_cell_args
+ def freeze_panes(self, row, col, top_row=None, left_col=None, pane_type=0):
+ """
+ Create worksheet panes and mark them as frozen.
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ top_row: Topmost visible row in scrolling region of pane.
+ left_col: Leftmost visible row in scrolling region of pane.
+
+ Returns:
+ 0: Nothing.
+
+ """
+ if top_row is None:
+ top_row = row
+
+ if left_col is None:
+ left_col = col
+
+ self.panes = [row, col, top_row, left_col, pane_type]
+
+ @convert_cell_args
+ def split_panes(self, x, y, top_row=None, left_col=None):
+ """
+ Create worksheet panes and mark them as split.
+
+ Args:
+ x: The position for the vertical split.
+ y: The position for the horizontal split.
+ top_row: Topmost visible row in scrolling region of pane.
+ left_col: Leftmost visible row in scrolling region of pane.
+
+ Returns:
+ 0: Nothing.
+
+ """
+ # Same as freeze panes with a different pane type.
+ self.freeze_panes(x, y, top_row, left_col, 2)
+
+ def set_zoom(self, zoom=100):
+ """
+ Set the worksheet zoom factor.
+
+ Args:
+ zoom: Scale factor: 10 <= zoom <= 400.
+
+ Returns:
+ Nothing.
+
+ """
+ # Ensure the zoom scale is in Excel's range.
+ if zoom < 10 or zoom > 400:
+ warn("Zoom factor %d outside range: 10 <= zoom <= 400" % zoom)
+ zoom = 100
+
+ self.zoom = int(zoom)
+
+ def right_to_left(self):
+ """
+ Display the worksheet right to left for some versions of Excel.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.is_right_to_left = 1
+
+ def hide_zero(self):
+ """
+ Hide zero values in worksheet cells.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.show_zeros = 0
+
+ def set_tab_color(self, color):
+ """
+ Set the color of the worksheet tab.
+
+ Args:
+ color: A #RGB color index.
+
+ Returns:
+ Nothing.
+
+ """
+ self.tab_color = xl_color(color)
+
+ def protect(self, password='', options=None):
+ """
+ Set the password and protection options of the worksheet.
+
+ Args:
+ password: An optional password string.
+ options: A dictionary of worksheet objects to protect.
+
+ Returns:
+ Nothing.
+
+ """
+ if password != '':
+ password = self._encode_password(password)
+
+ if not options:
+ options = {}
+
+ # Default values for objects that can be protected.
+ defaults = {
+ 'sheet': True,
+ 'content': False,
+ 'objects': False,
+ 'scenarios': False,
+ 'format_cells': False,
+ 'format_columns': False,
+ 'format_rows': False,
+ 'insert_columns': False,
+ 'insert_rows': False,
+ 'insert_hyperlinks': False,
+ 'delete_columns': False,
+ 'delete_rows': False,
+ 'select_locked_cells': True,
+ 'sort': False,
+ 'autofilter': False,
+ 'pivot_tables': False,
+ 'select_unlocked_cells': True}
+
+ # Overwrite the defaults with user specified values.
+ for key in (options.keys()):
+
+ if key in defaults:
+ defaults[key] = options[key]
+ else:
+ warn("Unknown protection object: '%s'" % key)
+
+ # Set the password after the user defined values.
+ defaults['password'] = password
+
+ self.protect_options = defaults
+
+ @convert_cell_args
+ def insert_button(self, row, col, options={}):
+ """
+ Insert a button form object into the worksheet.
+
+ Args:
+ row: The cell row (zero indexed).
+ col: The cell column (zero indexed).
+ options: Button formatting options.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+
+ """
+ button = self._button_params(row, col, options)
+
+ self.buttons_list.append(button)
+
+ self.has_vml = 1
+
+ ###########################################################################
+ #
+ # Public API. Page Setup methods.
+ #
+ ###########################################################################
+ def set_landscape(self):
+ """
+ Set the page orientation as landscape.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.orientation = 0
+ self.page_setup_changed = True
+
+ def set_portrait(self):
+ """
+ Set the page orientation as portrait.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.orientation = 1
+ self.page_setup_changed = True
+
+ def set_page_view(self):
+ """
+ Set the page view mode.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.page_view = 1
+
+ def set_paper(self, paper_size):
+ """
+ Set the paper type. US Letter = 1, A4 = 9.
+
+ Args:
+ paper_size: Paper index.
+
+ Returns:
+ Nothing.
+
+ """
+ if paper_size:
+ self.paper_size = paper_size
+ self.page_setup_changed = True
+
+ def center_horizontally(self):
+ """
+ Center the page horizontally.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.print_options_changed = True
+ self.hcenter = 1
+
+ def center_vertically(self):
+ """
+ Center the page vertically.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.print_options_changed = True
+ self.vcenter = 1
+
+ def set_margins(self, left=0.7, right=0.7, top=0.75, bottom=0.75):
+ """
+ Set all the page margins in inches.
+
+ Args:
+ left: Left margin.
+ right: Right margin.
+ top: Top margin.
+ bottom: Bottom margin.
+
+ Returns:
+ Nothing.
+
+ """
+ self.margin_left = left
+ self.margin_right = right
+ self.margin_top = top
+ self.margin_bottom = bottom
+
+ def set_header(self, header='', options=None, margin=None):
+ """
+ Set the page header caption and optional margin.
+
+ Args:
+ header: Header string.
+ margin: Header margin.
+ options: Header options, mainly for images.
+
+ Returns:
+ Nothing.
+
+ """
+ header_orig = header
+ header = header.replace('&[Picture]', '&G')
+
+ if len(header) >= 255:
+ warn('Header string must be less than 255 characters')
+ return
+
+ if options is not None:
+ # For backward compatibility allow options to be the margin.
+ if not isinstance(options, dict):
+ options = {'margin': options}
+ else:
+ options = {}
+
+ # For backward compatibility.
+ if margin is not None:
+ options['margin'] = margin
+
+ # Reset the list in case the function is called more than once.
+ self.header_images = []
+
+ if options.get('image_left'):
+ self.header_images.append([options.get('image_left'),
+ options.get('image_data_left'),
+ 'LH'])
+
+ if options.get('image_center'):
+ self.header_images.append([options.get('image_center'),
+ options.get('image_data_center'),
+ 'CH'])
+
+ if options.get('image_right'):
+ self.header_images.append([options.get('image_right'),
+ options.get('image_data_right'),
+ 'RH'])
+
+ placeholder_count = header.count('&G')
+ image_count = len(self.header_images)
+
+ if placeholder_count != image_count:
+ warn("Number of header images (%s) doesn't match placeholder "
+ "count (%s) in string: %s"
+ % (image_count, placeholder_count, header_orig))
+ self.header_images = []
+ return
+
+ if 'align_with_margins' in options:
+ self.header_footer_aligns = options['align_with_margins']
+
+ if 'scale_with_doc' in options:
+ self.header_footer_scales = options['scale_with_doc']
+
+ self.header = header
+ self.margin_header = options.get('margin', 0.3)
+ self.header_footer_changed = True
+
+ if image_count:
+ self.has_header_vml = True
+
+ def set_footer(self, footer='', options=None, margin=None):
+ """
+ Set the page footer caption and optional margin.
+
+ Args:
+ footer: Footer string.
+ margin: Footer margin.
+ options: Footer options, mainly for images.
+
+ Returns:
+ Nothing.
+
+ """
+ footer_orig = footer
+ footer = footer.replace('&[Picture]', '&G')
+
+ if len(footer) >= 255:
+ warn('Footer string must be less than 255 characters')
+ return
+
+ if options is not None:
+ # For backward compatibility allow options to be the margin.
+ if not isinstance(options, dict):
+ options = {'margin': options}
+ else:
+ options = {}
+
+ # For backward compatibility.
+ if margin is not None:
+ options['margin'] = margin
+
+ # Reset the list in case the function is called more than once.
+ self.footer_images = []
+
+ if options.get('image_left'):
+ self.footer_images.append([options.get('image_left'),
+ options.get('image_data_left'),
+ 'LF'])
+
+ if options.get('image_center'):
+ self.footer_images.append([options.get('image_center'),
+ options.get('image_data_center'),
+ 'CF'])
+
+ if options.get('image_right'):
+ self.footer_images.append([options.get('image_right'),
+ options.get('image_data_right'),
+ 'RF'])
+
+ placeholder_count = footer.count('&G')
+ image_count = len(self.footer_images)
+
+ if placeholder_count != image_count:
+ warn("Number of footer images (%s) doesn't match placeholder "
+ "count (%s) in string: %s"
+ % (image_count, placeholder_count, footer_orig))
+ self.footer_images = []
+ return
+
+ if 'align_with_margins' in options:
+ self.header_footer_aligns = options['align_with_margins']
+
+ if 'scale_with_doc' in options:
+ self.header_footer_scales = options['scale_with_doc']
+
+ self.footer = footer
+ self.margin_footer = options.get('margin', 0.3)
+ self.header_footer_changed = True
+
+ if image_count:
+ self.has_header_vml = True
+
+ def repeat_rows(self, first_row, last_row=None):
+ """
+ Set the rows to repeat at the top of each printed page.
+
+ Args:
+ first_row: Start row for range.
+ last_row: End row for range.
+
+ Returns:
+ Nothing.
+
+ """
+ if last_row is None:
+ last_row = first_row
+
+ # Convert rows to 1 based.
+ first_row += 1
+ last_row += 1
+
+ # Create the row range area like: $1:$2.
+ area = '$%d:$%d' % (first_row, last_row)
+
+ # Build up the print titles area "Sheet1!$1:$2"
+ sheetname = quote_sheetname(self.name)
+ self.repeat_row_range = sheetname + '!' + area
+
+ @convert_column_args
+ def repeat_columns(self, first_col, last_col=None):
+ """
+ Set the columns to repeat at the left hand side of each printed page.
+
+ Args:
+ first_col: Start column for range.
+ last_col: End column for range.
+
+ Returns:
+ Nothing.
+
+ """
+ if last_col is None:
+ last_col = first_col
+
+ # Convert to A notation.
+ first_col = xl_col_to_name(first_col, 1)
+ last_col = xl_col_to_name(last_col, 1)
+
+ # Create a column range like $C:$D.
+ area = first_col + ':' + last_col
+
+ # Build up the print area range "=Sheet2!$C:$D"
+ sheetname = quote_sheetname(self.name)
+ self.repeat_col_range = sheetname + "!" + area
+
+ def hide_gridlines(self, option=1):
+ """
+ Set the option to hide gridlines on the screen and the printed page.
+
+ Args:
+ option: 0 : Don't hide gridlines
+ 1 : Hide printed gridlines only
+ 2 : Hide screen and printed gridlines
+
+ Returns:
+ Nothing.
+
+ """
+ if option == 0:
+ self.print_gridlines = 1
+ self.screen_gridlines = 1
+ self.print_options_changed = True
+ elif option == 1:
+ self.print_gridlines = 0
+ self.screen_gridlines = 1
+ else:
+ self.print_gridlines = 0
+ self.screen_gridlines = 0
+
+ def print_row_col_headers(self):
+ """
+ Set the option to print the row and column headers on the printed page.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.print_headers = 1
+ self.print_options_changed = True
+
+ @convert_range_args
+ def print_area(self, first_row, first_col, last_row, last_col):
+ """
+ Set the print area in the current worksheet.
+
+ Args:
+ first_row: The first row of the cell range. (zero indexed).
+ first_col: The first column of the cell range.
+ last_row: The last row of the cell range. (zero indexed).
+ last_col: The last column of the cell range.
+
+ Returns:
+ 0: Success.
+ -1: Row or column is out of worksheet bounds.
+
+ """
+ # Set the print area in the current worksheet.
+
+ # Ignore max print area since it is the same as no area for Excel.
+ if (first_row == 0 and first_col == 0
+ and last_row == self.xls_rowmax - 1
+ and last_col == self.xls_colmax - 1):
+ return
+
+ # Build up the print area range "Sheet1!$A$1:$C$13".
+ area = self._convert_name_area(first_row, first_col,
+ last_row, last_col)
+ self.print_area_range = area
+
+ def print_across(self):
+ """
+ Set the order in which pages are printed.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+
+ """
+ self.page_order = 1
+ self.page_setup_changed = True
+
+ def fit_to_pages(self, width, height):
+ """
+ Fit the printed area to a specific number of pages both vertically and
+ horizontally.
+
+ Args:
+ width: Number of pages horizontally.
+ height: Number of pages vertically.
+
+ Returns:
+ Nothing.
+
+ """
+ self.fit_page = 1
+ self.fit_width = width
+ self.fit_height = height
+ self.page_setup_changed = True
+
+ def set_start_page(self, start_page):
+ """
+ Set the start page number when printing.
+
+ Args:
+ start_page: Start page number.
+
+ Returns:
+ Nothing.
+
+ """
+ self.page_start = start_page
+
+ def set_print_scale(self, scale):
+ """
+ Set the scale factor for the printed page.
+
+ Args:
+ scale: Print scale. 10 <= scale <= 400.
+
+ Returns:
+ Nothing.
+
+ """
+ # Confine the scale to Excel's range.
+ if scale < 10 or scale > 400:
+ warn("Print scale '%d' outside range: 10 <= scale <= 400" % scale)
+ return
+
+ # Turn off "fit to page" option when print scale is on.
+ self.fit_page = 0
+
+ self.print_scale = int(scale)
+ self.page_setup_changed = True
+
+ def set_h_pagebreaks(self, breaks):
+ """
+ Set the horizontal page breaks on a worksheet.
+
+ Args:
+ breaks: List of rows where the page breaks should be added.
+
+ Returns:
+ Nothing.
+
+ """
+ self.hbreaks = breaks
+
+ def set_v_pagebreaks(self, breaks):
+ """
+ Set the horizontal page breaks on a worksheet.
+
+ Args:
+ breaks: List of columns where the page breaks should be added.
+
+ Returns:
+ Nothing.
+
+ """
+ self.vbreaks = breaks
+
+ def set_vba_name(self, name=None):
+ """
+ Set the VBA name for the worksheet. By default this is the
+ same as the sheet name: i.e., Sheet1 etc.
+
+ Args:
+ name: The VBA name for the worksheet.
+
+ Returns:
+ Nothing.
+
+ """
+ if name is not None:
+ self.vba_codename = name
+ else:
+ self.vba_codename = self.name
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+ def _initialize(self, init_data):
+ self.name = init_data['name']
+ self.index = init_data['index']
+ self.str_table = init_data['str_table']
+ self.worksheet_meta = init_data['worksheet_meta']
+ self.optimization = init_data['optimization']
+ self.tmpdir = init_data['tmpdir']
+ self.date_1904 = init_data['date_1904']
+ self.strings_to_numbers = init_data['strings_to_numbers']
+ self.strings_to_formulas = init_data['strings_to_formulas']
+ self.strings_to_urls = init_data['strings_to_urls']
+ self.nan_inf_to_errors = init_data['nan_inf_to_errors']
+ self.default_date_format = init_data['default_date_format']
+ self.default_url_format = init_data['default_url_format']
+ self.excel2003_style = init_data['excel2003_style']
+ self.remove_timezone = init_data['remove_timezone']
+
+ if self.excel2003_style:
+ self.original_row_height = 12.75
+ self.default_row_height = 12.75
+ self.default_row_pixels = 17
+ self.margin_left = 0.75
+ self.margin_right = 0.75
+ self.margin_top = 1
+ self.margin_bottom = 1
+ self.margin_header = 0.5
+ self.margin_footer = 0.5
+ self.header_footer_aligns = False
+
+ # Open a temp filehandle to store row data in optimization mode.
+ if self.optimization == 1:
+ # This is sub-optimal but we need to create a temp file
+ # with utf8 encoding in Python < 3.
+ (fd, filename) = tempfile.mkstemp(dir=self.tmpdir)
+ os.close(fd)
+ self.row_data_filename = filename
+ self.row_data_fh = codecs.open(filename, 'w+', 'utf-8')
+
+ # Set as the worksheet filehandle until the file is assembled.
+ self.fh = self.row_data_fh
+
+ def _assemble_xml_file(self):
+ # Assemble and write the XML file.
+
+ # Write the XML declaration.
+ self._xml_declaration()
+
+ # Write the root worksheet element.
+ self._write_worksheet()
+
+ # Write the worksheet properties.
+ self._write_sheet_pr()
+
+ # Write the worksheet dimensions.
+ self._write_dimension()
+
+ # Write the sheet view properties.
+ self._write_sheet_views()
+
+ # Write the sheet format properties.
+ self._write_sheet_format_pr()
+
+ # Write the sheet column info.
+ self._write_cols()
+
+ # Write the worksheet data such as rows columns and cells.
+ if self.optimization == 0:
+ self._write_sheet_data()
+ else:
+ self._write_optimized_sheet_data()
+
+ # Write the sheetProtection element.
+ self._write_sheet_protection()
+
+ # Write the phoneticPr element.
+ if self.excel2003_style:
+ self._write_phonetic_pr()
+
+ # Write the autoFilter element.
+ self._write_auto_filter()
+
+ # Write the mergeCells element.
+ self._write_merge_cells()
+
+ # Write the conditional formats.
+ self._write_conditional_formats()
+
+ # Write the dataValidations element.
+ self._write_data_validations()
+
+ # Write the hyperlink element.
+ self._write_hyperlinks()
+
+ # Write the printOptions element.
+ self._write_print_options()
+
+ # Write the worksheet page_margins.
+ self._write_page_margins()
+
+ # Write the worksheet page setup.
+ self._write_page_setup()
+
+ # Write the headerFooter element.
+ self._write_header_footer()
+
+ # Write the rowBreaks element.
+ self._write_row_breaks()
+
+ # Write the colBreaks element.
+ self._write_col_breaks()
+
+ # Write the drawing element.
+ self._write_drawings()
+
+ # Write the legacyDrawing element.
+ self._write_legacy_drawing()
+
+ # Write the legacyDrawingHF element.
+ self._write_legacy_drawing_hf()
+
+ # Write the tableParts element.
+ self._write_table_parts()
+
+ # Write the extLst and sparklines.
+ self._write_ext_sparklines()
+
+ # Close the worksheet tag.
+ self._xml_end_tag('worksheet')
+
+ # Close the file.
+ self._xml_close()
+
+ def _check_dimensions(self, row, col, ignore_row=False, ignore_col=False):
+ # Check that row and col are valid and store the max and min
+ # values for use in other methods/elements. The ignore_row /
+ # ignore_col flags is used to indicate that we wish to perform
+ # the dimension check without storing the value. The ignore
+ # flags are use by set_row() and data_validate.
+
+ # Check that the row/col are within the worksheet bounds.
+ if row < 0 or col < 0:
+ return -1
+ if row >= self.xls_rowmax or col >= self.xls_colmax:
+ return -1
+
+ # In optimization mode we don't change dimensions for rows
+ # that are already written.
+ if not ignore_row and not ignore_col and self.optimization == 1:
+ if row < self.previous_row:
+ return -2
+
+ if not ignore_row:
+ if self.dim_rowmin is None or row < self.dim_rowmin:
+ self.dim_rowmin = row
+ if self.dim_rowmax is None or row > self.dim_rowmax:
+ self.dim_rowmax = row
+
+ if not ignore_col:
+ if self.dim_colmin is None or col < self.dim_colmin:
+ self.dim_colmin = col
+ if self.dim_colmax is None or col > self.dim_colmax:
+ self.dim_colmax = col
+
+ return 0
+
+ def _convert_date_time(self, dt_obj):
+ # Convert a datetime object to an Excel serial date and time.
+ return datetime_to_excel_datetime(dt_obj,
+ self.date_1904,
+ self.remove_timezone)
+
+ def _convert_name_area(self, row_num_1, col_num_1, row_num_2, col_num_2):
+ # Convert zero indexed rows and columns to the format required by
+ # worksheet named ranges, eg, "Sheet1!$A$1:$C$13".
+
+ range1 = ''
+ range2 = ''
+ area = ''
+ row_col_only = 0
+
+ # Convert to A1 notation.
+ col_char_1 = xl_col_to_name(col_num_1, 1)
+ col_char_2 = xl_col_to_name(col_num_2, 1)
+ row_char_1 = '$' + str(row_num_1 + 1)
+ row_char_2 = '$' + str(row_num_2 + 1)
+
+ # We need to handle special cases that refer to rows or columns only.
+ if row_num_1 == 0 and row_num_2 == self.xls_rowmax - 1:
+ range1 = col_char_1
+ range2 = col_char_2
+ row_col_only = 1
+ elif col_num_1 == 0 and col_num_2 == self.xls_colmax - 1:
+ range1 = row_char_1
+ range2 = row_char_2
+ row_col_only = 1
+ else:
+ range1 = col_char_1 + row_char_1
+ range2 = col_char_2 + row_char_2
+
+ # A repeated range is only written once (if it isn't a special case).
+ if range1 == range2 and not row_col_only:
+ area = range1
+ else:
+ area = range1 + ':' + range2
+
+ # Build up the print area range "Sheet1!$A$1:$C$13".
+ sheetname = quote_sheetname(self.name)
+ area = sheetname + "!" + area
+
+ return area
+
+ def _sort_pagebreaks(self, breaks):
+ # This is an internal method used to filter elements of a list of
+ # pagebreaks used in the _store_hbreak() and _store_vbreak() methods.
+ # It:
+ # 1. Removes duplicate entries from the list.
+ # 2. Sorts the list.
+ # 3. Removes 0 from the list if present.
+ if not breaks:
+ return
+
+ breaks_set = set(breaks)
+
+ if 0 in breaks_set:
+ breaks_set.remove(0)
+
+ breaks_list = list(breaks_set)
+ breaks_list.sort()
+
+ # The Excel 2007 specification says that the maximum number of page
+ # breaks is 1026. However, in practice it is actually 1023.
+ max_num_breaks = 1023
+ if len(breaks_list) > max_num_breaks:
+ breaks_list = breaks_list[:max_num_breaks]
+
+ return breaks_list
+
+ def _extract_filter_tokens(self, expression):
+ # Extract the tokens from the filter expression. The tokens are mainly
+ # non-whitespace groups. The only tricky part is to extract string
+ # tokens that contain whitespace and/or quoted double quotes (Excel's
+ # escaped quotes).
+ #
+ # Examples: 'x < 2000'
+ # 'x > 2000 and x < 5000'
+ # 'x = "foo"'
+ # 'x = "foo bar"'
+ # 'x = "foo "" bar"'
+ #
+ if not expression:
+ return []
+
+ token_re = re.compile(r'"(?:[^"]|"")*"|\S+')
+ tokens = token_re.findall(expression)
+
+ new_tokens = []
+ # Remove single leading and trailing quotes and un-escape other quotes.
+ for token in tokens:
+ if token.startswith('"'):
+ token = token[1:]
+
+ if token.endswith('"'):
+ token = token[:-1]
+
+ token = token.replace('""', '"')
+
+ new_tokens.append(token)
+
+ return new_tokens
+
+ def _parse_filter_expression(self, expression, tokens):
+ # Converts the tokens of a possibly conditional expression into 1 or 2
+ # sub expressions for further parsing.
+ #
+ # Examples:
+ # ('x', '==', 2000) -> exp1
+ # ('x', '>', 2000, 'and', 'x', '<', 5000) -> exp1 and exp2
+
+ if len(tokens) == 7:
+ # The number of tokens will be either 3 (for 1 expression)
+ # or 7 (for 2 expressions).
+ conditional = tokens[3]
+
+ if re.match('(and|&&)', conditional):
+ conditional = 0
+ elif re.match('(or|\|\|)', conditional):
+ conditional = 1
+ else:
+ warn("Token '%s' is not a valid conditional "
+ "in filter expression '%s'" % (conditional, expression))
+
+ expression_1 = self._parse_filter_tokens(expression, tokens[0:3])
+ expression_2 = self._parse_filter_tokens(expression, tokens[4:7])
+
+ return expression_1 + [conditional] + expression_2
+ else:
+ return self._parse_filter_tokens(expression, tokens)
+
+ def _parse_filter_tokens(self, expression, tokens):
+ # Parse the 3 tokens of a filter expression and return the operator
+ # and token. The use of numbers instead of operators is a legacy of
+ # Spreadsheet::WriteExcel.
+ operators = {
+ '==': 2,
+ '=': 2,
+ '=~': 2,
+ 'eq': 2,
+
+ '!=': 5,
+ '!~': 5,
+ 'ne': 5,
+ '<>': 5,
+
+ '<': 1,
+ '<=': 3,
+ '>': 4,
+ '>=': 6,
+ }
+
+ operator = operators.get(tokens[1], None)
+ token = tokens[2]
+
+ # Special handling of "Top" filter expressions.
+ if re.match('top|bottom', tokens[0].lower()):
+ value = int(tokens[1])
+
+ if value < 1 or value > 500:
+ warn("The value '%d' in expression '%s' "
+ "must be in the range 1 to 500" % (value, expression))
+
+ token = token.lower()
+
+ if token != 'items' and token != '%':
+ warn("The type '%s' in expression '%s' "
+ "must be either 'items' or '%'" % (token, expression))
+
+ if tokens[0].lower() == 'top':
+ operator = 30
+ else:
+ operator = 32
+
+ if tokens[2] == '%':
+ operator += 1
+
+ token = str(value)
+
+ if not operator and tokens[0]:
+ warn("Token '%s' is not a valid operator "
+ "in filter expression '%s'" % (token[0], expression))
+
+ # Special handling for Blanks/NonBlanks.
+ if re.match('blanks|nonblanks', token.lower()):
+ # Only allow Equals or NotEqual in this context.
+ if operator != 2 and operator != 5:
+ warn("The operator '%s' in expression '%s' "
+ "is not valid in relation to Blanks/NonBlanks'"
+ % (tokens[1], expression))
+
+ token = token.lower()
+
+ # The operator should always be 2 (=) to flag a "simple" equality
+ # in the binary record. Therefore we convert <> to =.
+ if token == 'blanks':
+ if operator == 5:
+ token = ' '
+ else:
+ if operator == 5:
+ operator = 2
+ token = 'blanks'
+ else:
+ operator = 5
+ token = ' '
+
+ # if the string token contains an Excel match character then change the
+ # operator type to indicate a non "simple" equality.
+ if operator == 2 and re.search('[*?]', token):
+ operator = 22
+
+ return [operator, token]
+
+ def _encode_password(self, plaintext):
+ # Encode the worksheet protection "password" as a simple hash.
+ # Based on the algorithm by Daniel Rentz of OpenOffice.
+ i = 0
+ count = len(plaintext)
+ digits = []
+
+ for char in plaintext:
+ i += 1
+ char = ord(char) << i
+ low_15 = char & 0x7fff
+ high_15 = char & 0x7fff << 15
+ high_15 >>= 15
+ char = low_15 | high_15
+ digits.append(char)
+
+ password_hash = 0x0000
+
+ for digit in digits:
+ password_hash ^= digit
+
+ password_hash ^= count
+ password_hash ^= 0xCE4B
+
+ return "%X" % password_hash
+
+ def _prepare_image(self, index, image_id, drawing_id, width, height,
+ name, image_type, x_dpi, y_dpi):
+ # Set up images/drawings.
+ drawing_type = 2
+ (row, col, _, x_offset, y_offset,
+ x_scale, y_scale, url, tip, anchor, _) = self.images[index]
+
+ width *= x_scale
+ height *= y_scale
+
+ # Scale by non 96dpi resolutions.
+ width *= 96.0 / x_dpi
+ height *= 96.0 / y_dpi
+
+ dimensions = self._position_object_emus(col, row, x_offset, y_offset,
+ width, height)
+
+ # Convert from pixels to emus.
+ width = int(0.5 + (width * 9525))
+ height = int(0.5 + (height * 9525))
+
+ # Create a Drawing obj to use with worksheet unless one already exists.
+ if not self.drawing:
+ drawing = Drawing()
+ drawing.embedded = 1
+ self.drawing = drawing
+
+ self.external_drawing_links.append(['/drawing',
+ '../drawings/drawing'
+ + str(drawing_id)
+ + '.xml', None])
+ else:
+ drawing = self.drawing
+
+ drawing_object = [drawing_type]
+ drawing_object.extend(dimensions)
+ drawing_object.extend([width, height, name, None, url, tip, anchor])
+
+ drawing._add_drawing_object(drawing_object)
+
+ if url:
+ rel_type = "/hyperlink"
+ target_mode = "External"
+
+ if re.match('(ftp|http)s?://', url):
+ target = url
+
+ if re.match('external:', url):
+ target = url.replace('external:', '')
+
+ if re.match("internal:", url):
+ target = url.replace('internal:', '#')
+ target_mode = None
+
+ self.drawing_links.append([rel_type, target, target_mode])
+
+ self.drawing_links.append(['/image',
+ '../media/image'
+ + str(image_id) + '.'
+ + image_type])
+
+ def _prepare_shape(self, index, drawing_id):
+ # Set up shapes/drawings.
+ drawing_type = 3
+
+ (row, col, x_offset, y_offset,
+ x_scale, y_scale, text, options) = self.shapes[index]
+
+ width = options.get('width', self.default_col_pixels * 3)
+ height = options.get('height', self.default_row_pixels * 6)
+
+ width *= x_scale
+ height *= y_scale
+
+ dimensions = self._position_object_emus(col, row, x_offset, y_offset,
+ width, height)
+
+ # Convert from pixels to emus.
+ width = int(0.5 + (width * 9525))
+ height = int(0.5 + (height * 9525))
+
+ # Create a Drawing obj to use with worksheet unless one already exists.
+ if not self.drawing:
+ drawing = Drawing()
+ drawing.embedded = 1
+ self.drawing = drawing
+
+ self.external_drawing_links.append(['/drawing',
+ '../drawings/drawing'
+ + str(drawing_id)
+ + '.xml', None])
+ else:
+ drawing = self.drawing
+
+ shape = Shape('rect', 'TextBox', options)
+ shape.text = text
+
+ drawing_object = [drawing_type]
+ drawing_object.extend(dimensions)
+ drawing_object.extend([width, height, None, shape, None,
+ None, None])
+
+ drawing._add_drawing_object(drawing_object)
+
+ def _prepare_header_image(self, image_id, width, height, name, image_type,
+ position, x_dpi, y_dpi):
+ # Set up an image without a drawing object for header/footer images.
+
+ # Strip the extension from the filename.
+ name = re.sub('\..*$', '', name)
+
+ self.header_images_list.append([width, height, name, position,
+ x_dpi, y_dpi])
+
+ self.vml_drawing_links.append(['/image',
+ '../media/image'
+ + str(image_id) + '.'
+ + image_type])
+
+ def _prepare_chart(self, index, chart_id, drawing_id):
+ # Set up chart/drawings.
+ drawing_type = 1
+
+ (row, col, chart, x_offset, y_offset, x_scale, y_scale) = \
+ self.charts[index]
+
+ chart.id = chart_id - 1
+
+ # Use user specified dimensions, if any.
+ width = int(0.5 + (chart.width * x_scale))
+ height = int(0.5 + (chart.height * y_scale))
+
+ dimensions = self._position_object_emus(col, row, x_offset, y_offset,
+ width, height)
+
+ # Set the chart name for the embedded object if it has been specified.
+ name = chart.chart_name
+
+ # Create a Drawing obj to use with worksheet unless one already exists.
+ if not self.drawing:
+ drawing = Drawing()
+ drawing.embedded = 1
+ self.drawing = drawing
+
+ self.external_drawing_links.append(['/drawing',
+ '../drawings/drawing'
+ + str(drawing_id)
+ + '.xml'])
+ else:
+ drawing = self.drawing
+
+ drawing_object = [drawing_type]
+ drawing_object.extend(dimensions)
+ drawing_object.extend([width, height, name, None])
+
+ drawing._add_drawing_object(drawing_object)
+
+ self.drawing_links.append(['/chart',
+ '../charts/chart'
+ + str(chart_id)
+ + '.xml'])
+
+ def _position_object_emus(self, col_start, row_start, x1, y1,
+ width, height):
+ # Calculate the vertices that define the position of a graphical
+ # object within the worksheet in EMUs.
+ #
+ # The vertices are expressed as English Metric Units (EMUs). There are
+ # 12,700 EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per
+ # pixel
+ (col_start, row_start, x1, y1,
+ col_end, row_end, x2, y2, x_abs, y_abs) = \
+ self._position_object_pixels(col_start, row_start, x1, y1,
+ width, height)
+
+ # Convert the pixel values to EMUs. See above.
+ x1 = int(0.5 + 9525 * x1)
+ y1 = int(0.5 + 9525 * y1)
+ x2 = int(0.5 + 9525 * x2)
+ y2 = int(0.5 + 9525 * y2)
+ x_abs = int(0.5 + 9525 * x_abs)
+ y_abs = int(0.5 + 9525 * y_abs)
+
+ return (col_start, row_start, x1, y1, col_end, row_end, x2, y2,
+ x_abs, y_abs)
+
+ # Calculate the vertices that define the position of a graphical object
+ # within the worksheet in pixels.
+ #
+ # +------------+------------+
+ # | A | B |
+ # +-----+------------+------------+
+ # | |(x1,y1) | |
+ # | 1 |(A1)._______|______ |
+ # | | | | |
+ # | | | | |
+ # +-----+----| OBJECT |-----+
+ # | | | | |
+ # | 2 | |______________. |
+ # | | | (B2)|
+ # | | | (x2,y2)|
+ # +---- +------------+------------+
+ #
+ # Example of an object that covers some of the area from cell A1 to B2.
+ #
+ # Based on the width and height of the object we need to calculate 8 vars:
+ #
+ # col_start, row_start, col_end, row_end, x1, y1, x2, y2.
+ #
+ # We also calculate the absolute x and y position of the top left vertex of
+ # the object. This is required for images.
+ #
+ # The width and height of the cells that the object occupies can be
+ # variable and have to be taken into account.
+ #
+ # The values of col_start and row_start are passed in from the calling
+ # function. The values of col_end and row_end are calculated by
+ # subtracting the width and height of the object from the width and
+ # height of the underlying cells.
+ #
+ def _position_object_pixels(self, col_start, row_start, x1, y1,
+ width, height):
+ # col_start # Col containing upper left corner of object.
+ # x1 # Distance to left side of object.
+ #
+ # row_start # Row containing top left corner of object.
+ # y1 # Distance to top of object.
+ #
+ # col_end # Col containing lower right corner of object.
+ # x2 # Distance to right side of object.
+ #
+ # row_end # Row containing bottom right corner of object.
+ # y2 # Distance to bottom of object.
+ #
+ # width # Width of object frame.
+ # height # Height of object frame.
+ #
+ # x_abs # Absolute distance to left side of object.
+ # y_abs # Absolute distance to top side of object.
+ x_abs = 0
+ y_abs = 0
+
+ # Adjust start column for negative offsets.
+ while x1 < 0 and col_start > 0:
+ x1 += self._size_col(col_start - 1)
+ col_start -= 1
+
+ # Adjust start row for negative offsets.
+ while y1 < 0 and row_start > 0:
+ y1 += self._size_row(row_start - 1)
+ row_start -= 1
+
+ # Ensure that the image isn't shifted off the page at top left.
+ if x1 < 0:
+ x1 = 0
+
+ if y1 < 0:
+ y1 = 0
+
+ # Calculate the absolute x offset of the top-left vertex.
+ if self.col_size_changed:
+ for col_id in range(col_start):
+ x_abs += self._size_col(col_id)
+ else:
+ # Optimization for when the column widths haven't changed.
+ x_abs += self.default_col_pixels * col_start
+
+ x_abs += x1
+
+ # Calculate the absolute y offset of the top-left vertex.
+ if self.row_size_changed:
+ for row_id in range(row_start):
+ y_abs += self._size_row(row_id)
+ else:
+ # Optimization for when the row heights haven't changed.
+ y_abs += self.default_row_pixels * row_start
+
+ y_abs += y1
+
+ # Adjust start column for offsets that are greater than the col width.
+ while x1 >= self._size_col(col_start):
+ x1 -= self._size_col(col_start)
+ col_start += 1
+
+ # Adjust start row for offsets that are greater than the row height.
+ while y1 >= self._size_row(row_start):
+ y1 -= self._size_row(row_start)
+ row_start += 1
+
+ # Initialize end cell to the same as the start cell.
+ col_end = col_start
+ row_end = row_start
+
+ width = width + x1
+ height = height + y1
+
+ # Subtract the underlying cell widths to find end cell of the object.
+ while width >= self._size_col(col_end):
+ width -= self._size_col(col_end)
+ col_end += 1
+
+ # Subtract the underlying cell heights to find end cell of the object.
+
+ while height >= self._size_row(row_end):
+ height -= self._size_row(row_end)
+ row_end += 1
+
+ # The end vertices are whatever is left from the width and height.
+ x2 = width
+ y2 = height
+
+ return ([col_start, row_start, x1, y1, col_end, row_end, x2, y2,
+ x_abs, y_abs])
+
+ def _size_col(self, col):
+ # Convert the width of a cell from user's units to pixels. Excel rounds
+ # the column width to the nearest pixel. If the width hasn't been set
+ # by the user we use the default value. If the column is hidden it
+ # has a value of zero.
+ max_digit_width = 7 # For Calabri 11.
+ padding = 5
+ pixels = 0
+
+ # Look up the cell value to see if it has been changed.
+ if col in self.col_sizes and self.col_sizes[col] is not None:
+ width = self.col_sizes[col]
+
+ # Convert to pixels.
+ if width == 0:
+ pixels = 0
+ elif width < 1:
+ pixels = int(width * (max_digit_width + padding) + 0.5)
+ else:
+ pixels = int(width * max_digit_width + 0.5) + padding
+ else:
+ pixels = self.default_col_pixels
+
+ return pixels
+
+ def _size_row(self, row):
+ # Convert the height of a cell from user's units to pixels. If the
+ # height hasn't been set by the user we use the default value. If
+ # the row is hidden it has a value of zero.
+ pixels = 0
+
+ # Look up the cell value to see if it has been changed
+ if row in self.row_sizes:
+ height = self.row_sizes[row]
+
+ if height == 0:
+ pixels = 0
+ else:
+ pixels = int(4.0 / 3.0 * height)
+ else:
+ pixels = int(4.0 / 3.0 * self.default_row_height)
+
+ return pixels
+
+ def _comment_params(self, row, col, string, options):
+ # This method handles the additional optional parameters to
+ # write_comment() as well as calculating the comment object
+ # position and vertices.
+ default_width = 128
+ default_height = 74
+
+ params = {
+ 'author': None,
+ 'color': '#ffffe1',
+ 'start_cell': None,
+ 'start_col': None,
+ 'start_row': None,
+ 'visible': None,
+ 'width': default_width,
+ 'height': default_height,
+ 'x_offset': None,
+ 'x_scale': 1,
+ 'y_offset': None,
+ 'y_scale': 1,
+ }
+
+ # Overwrite the defaults with any user supplied values. Incorrect or
+ # misspelled parameters are silently ignored.
+ for key in options.keys():
+ params[key] = options[key]
+
+ # Ensure that a width and height have been set.
+ if not params['width']:
+ params['width'] = default_width
+ if not params['height']:
+ params['height'] = default_height
+
+ # Set the comment background color.
+ params['color'] = xl_color(params['color']).lower()
+
+ # Convert from Excel XML style color to XML html style color.
+ params['color'] = params['color'].replace('ff', '#', 1)
+
+ # Convert a cell reference to a row and column.
+ if params['start_cell'] is not None:
+ (start_row, start_col) = xl_cell_to_rowcol(params['start_cell'])
+ params['start_row'] = start_row
+ params['start_col'] = start_col
+
+ # Set the default start cell and offsets for the comment. These are
+ # generally fixed in relation to the parent cell. However there are
+ # some edge cases for cells at the, er, edges.
+ row_max = self.xls_rowmax
+ col_max = self.xls_colmax
+
+ if params['start_row'] is None:
+ if row == 0:
+ params['start_row'] = 0
+ elif row == row_max - 3:
+ params['start_row'] = row_max - 7
+ elif row == row_max - 2:
+ params['start_row'] = row_max - 6
+ elif row == row_max - 1:
+ params['start_row'] = row_max - 5
+ else:
+ params['start_row'] = row - 1
+
+ if params['y_offset'] is None:
+ if row == 0:
+ params['y_offset'] = 2
+ elif row == row_max - 3:
+ params['y_offset'] = 16
+ elif row == row_max - 2:
+ params['y_offset'] = 16
+ elif row == row_max - 1:
+ params['y_offset'] = 14
+ else:
+ params['y_offset'] = 10
+
+ if params['start_col'] is None:
+ if col == col_max - 3:
+ params['start_col'] = col_max - 6
+ elif col == col_max - 2:
+ params['start_col'] = col_max - 5
+ elif col == col_max - 1:
+ params['start_col'] = col_max - 4
+ else:
+ params['start_col'] = col + 1
+
+ if params['x_offset'] is None:
+ if col == col_max - 3:
+ params['x_offset'] = 49
+ elif col == col_max - 2:
+ params['x_offset'] = 49
+ elif col == col_max - 1:
+ params['x_offset'] = 49
+ else:
+ params['x_offset'] = 15
+
+ # Scale the size of the comment box if required.
+ if params['x_scale']:
+ params['width'] = params['width'] * params['x_scale']
+
+ if params['y_scale']:
+ params['height'] = params['height'] * params['y_scale']
+
+ # Round the dimensions to the nearest pixel.
+ params['width'] = int(0.5 + params['width'])
+ params['height'] = int(0.5 + params['height'])
+
+ # Calculate the positions of the comment object.
+ vertices = self._position_object_pixels(
+ params['start_col'], params['start_row'], params['x_offset'],
+ params['y_offset'], params['width'], params['height'])
+
+ # Add the width and height for VML.
+ vertices.append(params['width'])
+ vertices.append(params['height'])
+
+ return ([row, col, string, params['author'],
+ params['visible'], params['color']] + [vertices])
+
+ def _button_params(self, row, col, options):
+ # This method handles the parameters passed to insert_button() as well
+ # as calculating the comment object position and vertices.
+
+ default_height = self.default_row_pixels
+ default_width = self.default_col_pixels
+
+ button_number = 1 + len(self.buttons_list)
+ button = {'row': row, 'col': col, 'font': {}}
+ params = {}
+
+ # Overwrite the defaults with any user supplied values. Incorrect or
+ # misspelled parameters are silently ignored.
+ for key in options.keys():
+ params[key] = options[key]
+
+ # Set the button caption.
+ caption = params.get('caption')
+
+ # Set a default caption if none was specified by user.
+ if caption is None:
+ caption = 'Button %d' % button_number
+
+ button['font']['caption'] = caption
+
+ # Set the macro name.
+ if params.get('macro'):
+ button['macro'] = '[0]!' + params['macro']
+ else:
+ button['macro'] = '[0]!Button%d_Click' % button_number
+
+ # Ensure that a width and height have been set.
+ params['width'] = params.get('width', default_width)
+ params['height'] = params.get('height', default_height)
+
+ # Set the x/y offsets.
+ params['x_offset'] = params.get('x_offset', 0)
+ params['y_offset'] = params.get('y_offset', 0)
+
+ # Scale the size of the button if required.
+ params['width'] = params['width'] * params.get('x_scale', 1)
+ params['height'] = params['height'] * params.get('y_scale', 1)
+
+ # Round the dimensions to the nearest pixel.
+ params['width'] = int(0.5 + params['width'])
+ params['height'] = int(0.5 + params['height'])
+
+ params['start_row'] = row
+ params['start_col'] = col
+
+ # Calculate the positions of the button object.
+ vertices = self._position_object_pixels(
+ params['start_col'], params['start_row'], params['x_offset'],
+ params['y_offset'], params['width'], params['height'])
+
+ # Add the width and height for VML.
+ vertices.append(params['width'])
+ vertices.append(params['height'])
+
+ button['vertices'] = vertices
+
+ return button
+
+ def _prepare_vml_objects(self, vml_data_id, vml_shape_id, vml_drawing_id,
+ comment_id):
+ comments = []
+ # Sort the comments into row/column order for easier comparison
+ # testing and set the external links for comments and buttons.
+ row_nums = sorted(self.comments.keys())
+
+ for row in row_nums:
+ col_nums = sorted(self.comments[row].keys())
+
+ for col in col_nums:
+ # Set comment visibility if required and not user defined.
+ if self.comments_visible:
+ if self.comments[row][col][4] is None:
+ self.comments[row][col][4] = 1
+
+ # Set comment author if not already user defined.
+ if self.comments[row][col][3] is None:
+ self.comments[row][col][3] = self.comments_author
+
+ comments.append(self.comments[row][col])
+
+ self.external_vml_links.append(['/vmlDrawing',
+ '../drawings/vmlDrawing'
+ + str(vml_drawing_id)
+ + '.vml'])
+
+ if self.has_comments:
+ self.comments_list = comments
+
+ self.external_comment_links.append(['/comments',
+ '../comments'
+ + str(comment_id)
+ + '.xml'])
+
+ count = len(comments)
+ start_data_id = vml_data_id
+
+ # The VML o:idmap data id contains a comma separated range when there
+ # is more than one 1024 block of comments, like this: data="1,2".
+ for i in range(int(count / 1024)):
+ vml_data_id = '%s,%d' % (vml_data_id, start_data_id + i + 1)
+
+ self.vml_data_id = vml_data_id
+ self.vml_shape_id = vml_shape_id
+
+ return count
+
+ def _prepare_header_vml_objects(self, vml_header_id, vml_drawing_id):
+ # Set up external linkage for VML header/footer images.
+
+ self.vml_header_id = vml_header_id
+
+ self.external_vml_links.append(['/vmlDrawing',
+ '../drawings/vmlDrawing'
+ + str(vml_drawing_id) + '.vml'])
+
+ def _prepare_tables(self, table_id, seen):
+ # Set the table ids for the worksheet tables.
+ for table in self.tables:
+ table['id'] = table_id
+
+ if table.get('name') is None:
+ # Set a default name.
+ table['name'] = 'Table' + str(table_id)
+
+ # Check for duplicate table names.
+ name = table['name'].lower()
+
+ if name in seen:
+ raise Exception("invalid duplicate table name '%s' found." %
+ table['name'])
+ else:
+ seen[name] = True
+
+ # Store the link used for the rels file.
+ self.external_table_links.append(['/table',
+ '../tables/table'
+ + str(table_id)
+ + '.xml'])
+ table_id += 1
+
+ def _table_function_to_formula(self, function, col_name):
+ # Convert a table total function to a worksheet formula.
+ formula = ''
+
+ subtotals = {
+ 'average': 101,
+ 'countNums': 102,
+ 'count': 103,
+ 'max': 104,
+ 'min': 105,
+ 'stdDev': 107,
+ 'sum': 109,
+ 'var': 110,
+ }
+
+ if function in subtotals:
+ func_num = subtotals[function]
+ formula = "SUBTOTAL(%s,[%s])" % (func_num, col_name)
+ else:
+ warn("Unsupported function '%s' in add_table()" % function)
+
+ return formula
+
+ def _set_spark_color(self, sparkline, options, user_color):
+ # Set the sparkline color.
+ if user_color not in options:
+ return
+
+ sparkline[user_color] = {'rgb': xl_color(options[user_color])}
+
+ def _get_range_data(self, row_start, col_start, row_end, col_end):
+ # Returns a range of data from the worksheet _table to be used in
+ # chart cached data. Strings are returned as SST ids and decoded
+ # in the workbook. Return None for data that doesn't exist since
+ # Excel can chart series with data missing.
+
+ if self.optimization:
+ return ()
+
+ data = []
+
+ # Iterate through the table data.
+ for row_num in range(row_start, row_end + 1):
+ # Store None if row doesn't exist.
+ if row_num not in self.table:
+ data.append(None)
+ continue
+
+ for col_num in range(col_start, col_end + 1):
+
+ if col_num in self.table[row_num]:
+ cell = self.table[row_num][col_num]
+
+ if type(cell).__name__ == 'Number':
+ # Return a number with Excel's precision.
+ data.append("%.16g" % cell.number)
+
+ elif type(cell).__name__ == 'String':
+ # Return a string from it's shared string index.
+ index = cell.string
+ string = self.str_table._get_shared_string(index)
+
+ data.append(string)
+
+ elif (type(cell).__name__ == 'Formula'
+ or type(cell).__name__ == 'ArrayFormula'):
+ # Return the formula value.
+ value = cell.value
+
+ if value is None:
+ value = 0
+
+ data.append(value)
+
+ elif type(cell).__name__ == 'Blank':
+ # Return a empty cell.
+ data.append('')
+ else:
+
+ # Store None if column doesn't exist.
+ data.append(None)
+
+ return data
+
+ def _csv_join(self, *items):
+ # Create a csv string for use with data validation formulas and lists.
+
+ # Convert non string types to string.
+ items = [str(item) if not isinstance(item, str_types) else item
+ for item in items]
+
+ return ','.join(items)
+
+ def _escape_url(self, url):
+ # Don't escape URL if it looks already escaped.
+ if re.search('%[0-9a-fA-F]{2}', url):
+ return url
+
+ # Can't use url.quote() here because it doesn't match Excel.
+ url = url.replace('%', '%25')
+ url = url.replace('"', '%22')
+ url = url.replace(' ', '%20')
+ url = url.replace('<', '%3c')
+ url = url.replace('>', '%3e')
+ url = url.replace('[', '%5b')
+ url = url.replace(']', '%5d')
+ url = url.replace('^', '%5e')
+ url = url.replace('`', '%60')
+ url = url.replace('{', '%7b')
+ url = url.replace('}', '%7d')
+
+ return url
+
+ ###########################################################################
+ #
+ # The following font methods are, more or less, duplicated from the
+ # Styles class. Not the cleanest version of reuse but works for now.
+ #
+ ###########################################################################
+ def _write_font(self, xf_format):
+ # Write the <font> element.
+ xml_writer = self.rstring
+
+ xml_writer._xml_start_tag('rPr')
+
+ # Handle the main font properties.
+ if xf_format.bold:
+ xml_writer._xml_empty_tag('b')
+ if xf_format.italic:
+ xml_writer._xml_empty_tag('i')
+ if xf_format.font_strikeout:
+ xml_writer._xml_empty_tag('strike')
+ if xf_format.font_outline:
+ xml_writer._xml_empty_tag('outline')
+ if xf_format.font_shadow:
+ xml_writer._xml_empty_tag('shadow')
+
+ # Handle the underline variants.
+ if xf_format.underline:
+ self._write_underline(xf_format.underline)
+
+ # Handle super/subscript.
+ if xf_format.font_script == 1:
+ self._write_vert_align('superscript')
+ if xf_format.font_script == 2:
+ self._write_vert_align('subscript')
+
+ # Write the font size
+ xml_writer._xml_empty_tag('sz', [('val', xf_format.font_size)])
+
+ # Handle colors.
+ if xf_format.theme:
+ self._write_color('theme', xf_format.theme)
+ elif xf_format.color_indexed:
+ self._write_color('indexed', xf_format.color_indexed)
+ elif xf_format.font_color:
+ color = self._get_palette_color(xf_format.font_color)
+ self._write_rstring_color('rgb', color)
+ else:
+ self._write_rstring_color('theme', 1)
+
+ # Write some other font properties related to font families.
+ xml_writer._xml_empty_tag('rFont', [('val', xf_format.font_name)])
+ xml_writer._xml_empty_tag('family', [('val', xf_format.font_family)])
+
+ if xf_format.font_name == 'Calibri' and not xf_format.hyperlink:
+ xml_writer._xml_empty_tag('scheme',
+ [('val', xf_format.font_scheme)])
+
+ xml_writer._xml_end_tag('rPr')
+
+ def _write_underline(self, underline):
+ # Write the underline font element.
+ attributes = []
+
+ # Handle the underline variants.
+ if underline == 2:
+ attributes = [('val', 'double')]
+ elif underline == 33:
+ attributes = [('val', 'singleAccounting')]
+ elif underline == 34:
+ attributes = [('val', 'doubleAccounting')]
+
+ self.rstring._xml_empty_tag('u', attributes)
+
+ def _write_vert_align(self, val):
+ # Write the <vertAlign> font sub-element.
+ attributes = [('val', val)]
+
+ self.rstring._xml_empty_tag('vertAlign', attributes)
+
+ def _write_rstring_color(self, name, value):
+ # Write the <color> element.
+ attributes = [(name, value)]
+
+ self.rstring._xml_empty_tag('color', attributes)
+
+ def _get_palette_color(self, color):
+ # Convert the RGB color.
+ if color[0] == '#':
+ color = color[1:]
+
+ return "FF" + color.upper()
+
+ def _isnan(self, x):
+ # Workaround for lack of math.isnan in Python 2.5/Jython.
+ return x != x
+
+ def _isinf(self, x):
+ # Workaround for lack of math.isinf in Python 2.5/Jython.
+ return (x - x) != 0
+
+ def _opt_close(self):
+ # Close the row data filehandle in optimization mode.
+ if not self.row_data_fh_closed:
+ self.row_data_fh.close()
+ self.row_data_fh_closed = True
+
+ def _opt_reopen(self):
+ # Reopen the row data filehandle in optimization mode.
+ if self.row_data_fh_closed:
+ filename = self.row_data_filename
+ self.row_data_fh = codecs.open(filename, 'a+', 'utf-8')
+ self.row_data_fh_closed = False
+ self.fh = self.row_data_fh
+
+ ###########################################################################
+ #
+ # XML methods.
+ #
+ ###########################################################################
+
+ def _write_worksheet(self):
+ # Write the <worksheet> element. This is the root element.
+
+ schema = 'http://schemas.openxmlformats.org/'
+ xmlns = schema + 'spreadsheetml/2006/main'
+ xmlns_r = schema + 'officeDocument/2006/relationships'
+ xmlns_mc = schema + 'markup-compatibility/2006'
+ ms_schema = 'http://schemas.microsoft.com/'
+ xmlns_x14ac = ms_schema + 'office/spreadsheetml/2009/9/ac'
+
+ attributes = [
+ ('xmlns', xmlns),
+ ('xmlns:r', xmlns_r)]
+
+ # Add some extra attributes for Excel 2010. Mainly for sparklines.
+ if self.excel_version == 2010:
+ attributes.append(('xmlns:mc', xmlns_mc))
+ attributes.append(('xmlns:x14ac', xmlns_x14ac))
+ attributes.append(('mc:Ignorable', 'x14ac'))
+
+ self._xml_start_tag('worksheet', attributes)
+
+ def _write_dimension(self):
+ # Write the <dimension> element. This specifies the range of
+ # cells in the worksheet. As a special case, empty
+ # spreadsheets use 'A1' as a range.
+
+ if self.dim_rowmin is None and self.dim_colmin is None:
+ # If the min dimensions are not defined then no dimensions
+ # have been set and we use the default 'A1'.
+ ref = 'A1'
+
+ elif self.dim_rowmin is None and self.dim_colmin is not None:
+ # If the row dimensions aren't set but the column
+ # dimensions are set then they have been changed via
+ # set_column().
+
+ if self.dim_colmin == self.dim_colmax:
+ # The dimensions are a single cell and not a range.
+ ref = xl_rowcol_to_cell(0, self.dim_colmin)
+ else:
+ # The dimensions are a cell range.
+ cell_1 = xl_rowcol_to_cell(0, self.dim_colmin)
+ cell_2 = xl_rowcol_to_cell(0, self.dim_colmax)
+ ref = cell_1 + ':' + cell_2
+
+ elif (self.dim_rowmin == self.dim_rowmax and
+ self.dim_colmin == self.dim_colmax):
+ # The dimensions are a single cell and not a range.
+ ref = xl_rowcol_to_cell(self.dim_rowmin, self.dim_colmin)
+ else:
+ # The dimensions are a cell range.
+ cell_1 = xl_rowcol_to_cell(self.dim_rowmin, self.dim_colmin)
+ cell_2 = xl_rowcol_to_cell(self.dim_rowmax, self.dim_colmax)
+ ref = cell_1 + ':' + cell_2
+
+ self._xml_empty_tag('dimension', [('ref', ref)])
+
+ def _write_sheet_views(self):
+ # Write the <sheetViews> element.
+ self._xml_start_tag('sheetViews')
+
+ # Write the sheetView element.
+ self._write_sheet_view()
+
+ self._xml_end_tag('sheetViews')
+
+ def _write_sheet_view(self):
+ # Write the <sheetViews> element.
+ attributes = []
+
+ # Hide screen gridlines if required
+ if not self.screen_gridlines:
+ attributes.append(('showGridLines', 0))
+
+ # Hide zeroes in cells.
+ if not self.show_zeros:
+ attributes.append(('showZeros', 0))
+
+ # Display worksheet right to left for Hebrew, Arabic and others.
+ if self.is_right_to_left:
+ attributes.append(('rightToLeft', 1))
+
+ # Show that the sheet tab is selected.
+ if self.selected:
+ attributes.append(('tabSelected', 1))
+
+ # Turn outlines off. Also required in the outlinePr element.
+ if not self.outline_on:
+ attributes.append(("showOutlineSymbols", 0))
+
+ # Set the page view/layout mode if required.
+ if self.page_view:
+ attributes.append(('view', 'pageLayout'))
+
+ # Set the zoom level.
+ if self.zoom != 100:
+ if not self.page_view:
+ attributes.append(('zoomScale', self.zoom))
+ if self.zoom_scale_normal:
+ attributes.append(('zoomScaleNormal', self.zoom))
+
+ attributes.append(('workbookViewId', 0))
+
+ if self.panes or len(self.selections):
+ self._xml_start_tag('sheetView', attributes)
+ self._write_panes()
+ self._write_selections()
+ self._xml_end_tag('sheetView')
+ else:
+ self._xml_empty_tag('sheetView', attributes)
+
+ def _write_sheet_format_pr(self):
+ # Write the <sheetFormatPr> element.
+ default_row_height = self.default_row_height
+ row_level = self.outline_row_level
+ col_level = self.outline_col_level
+
+ attributes = [('defaultRowHeight', default_row_height)]
+
+ if self.default_row_height != self.original_row_height:
+ attributes.append(('customHeight', 1))
+
+ if self.default_row_zeroed:
+ attributes.append(('zeroHeight', 1))
+
+ if row_level:
+ attributes.append(('outlineLevelRow', row_level))
+ if col_level:
+ attributes.append(('outlineLevelCol', col_level))
+
+ if self.excel_version == 2010:
+ attributes.append(('x14ac:dyDescent', '0.25'))
+
+ self._xml_empty_tag('sheetFormatPr', attributes)
+
+ def _write_cols(self):
+ # Write the <cols> element and <col> sub elements.
+
+ # Exit unless some column have been formatted.
+ if not self.colinfo:
+ return
+
+ self._xml_start_tag('cols')
+
+ for col in sorted(self.colinfo.keys()):
+ self._write_col_info(self.colinfo[col])
+
+ self._xml_end_tag('cols')
+
+ def _write_col_info(self, col_info):
+ # Write the <col> element.
+
+ (col_min, col_max, width, cell_format,
+ hidden, level, collapsed) = col_info
+
+ custom_width = 1
+ xf_index = 0
+
+ # Get the cell_format index.
+ if cell_format:
+ xf_index = cell_format._get_xf_index()
+
+ # Set the Excel default column width.
+ if width is None:
+ if not hidden:
+ width = 8.43
+ custom_width = 0
+ else:
+ width = 0
+ elif width == 8.43:
+ # Width is defined but same as default.
+ custom_width = 0
+
+ # Convert column width from user units to character width.
+ if width > 0:
+ # For Calabri 11.
+ max_digit_width = 7
+ padding = 5
+
+ if width < 1:
+ width = int((int(width * (max_digit_width + padding) + 0.5))
+ / float(max_digit_width) * 256.0) / 256.0
+ else:
+ width = int((int(width * max_digit_width + 0.5) + padding)
+ / float(max_digit_width) * 256.0) / 256.0
+
+ attributes = [
+ ('min', col_min + 1),
+ ('max', col_max + 1),
+ ('width', "%.16g" % width)]
+
+ if xf_index:
+ attributes.append(('style', xf_index))
+ if hidden:
+ attributes.append(('hidden', '1'))
+ if custom_width:
+ attributes.append(('customWidth', '1'))
+ if level:
+ attributes.append(('outlineLevel', level))
+ if collapsed:
+ attributes.append(('collapsed', '1'))
+
+ self._xml_empty_tag('col', attributes)
+
+ def _write_sheet_data(self):
+ # Write the <sheetData> element.
+
+ if self.dim_rowmin is None:
+ # If the dimensions aren't defined there is no data to write.
+ self._xml_empty_tag('sheetData')
+ else:
+ self._xml_start_tag('sheetData')
+ self._write_rows()
+ self._xml_end_tag('sheetData')
+
+ def _write_optimized_sheet_data(self):
+ # Write the <sheetData> element when the memory optimization is on.
+ # In this case we read the data stored in the temp file and rewrite
+ # it to the XML sheet file.
+ if self.dim_rowmin is None:
+ # If the dimensions aren't defined then there is no data to write.
+ self._xml_empty_tag('sheetData')
+ else:
+ self._xml_start_tag('sheetData')
+
+ # Rewind the filehandle that was used for temp row data.
+ buff_size = 65536
+ self.row_data_fh.seek(0)
+ data = self.row_data_fh.read(buff_size)
+
+ while data:
+ self.fh.write(data)
+ data = self.row_data_fh.read(buff_size)
+
+ self.row_data_fh.close()
+ os.unlink(self.row_data_filename)
+
+ self._xml_end_tag('sheetData')
+
+ def _write_page_margins(self):
+ # Write the <pageMargins> element.
+ attributes = [
+ ('left', self.margin_left),
+ ('right', self.margin_right),
+ ('top', self.margin_top),
+ ('bottom', self.margin_bottom),
+ ('header', self.margin_header),
+ ('footer', self.margin_footer)]
+
+ self._xml_empty_tag('pageMargins', attributes)
+
+ def _write_page_setup(self):
+ # Write the <pageSetup> element.
+ #
+ # The following is an example taken from Excel.
+ #
+ # <pageSetup
+ # paperSize="9"
+ # scale="110"
+ # fitToWidth="2"
+ # fitToHeight="2"
+ # pageOrder="overThenDown"
+ # orientation="portrait"
+ # blackAndWhite="1"
+ # draft="1"
+ # horizontalDpi="200"
+ # verticalDpi="200"
+ # r:id="rId1"
+ # />
+ #
+ attributes = []
+
+ # Skip this element if no page setup has changed.
+ if not self.page_setup_changed:
+ return
+
+ # Set paper size.
+ if self.paper_size:
+ attributes.append(('paperSize', self.paper_size))
+
+ # Set the print_scale.
+ if self.print_scale != 100:
+ attributes.append(('scale', self.print_scale))
+
+ # Set the "Fit to page" properties.
+ if self.fit_page and self.fit_width != 1:
+ attributes.append(('fitToWidth', self.fit_width))
+
+ if self.fit_page and self.fit_height != 1:
+ attributes.append(('fitToHeight', self.fit_height))
+
+ # Set the page print direction.
+ if self.page_order:
+ attributes.append(('pageOrder', "overThenDown"))
+
+ # Set start page for printing.
+ if self.page_start > 1:
+ attributes.append(('firstPageNumber', self.page_start))
+
+ # Set page orientation.
+ if self.orientation:
+ attributes.append(('orientation', 'portrait'))
+ else:
+ attributes.append(('orientation', 'landscape'))
+
+ # Set start page for printing.
+ if self.page_start != 0:
+ attributes.append(('useFirstPageNumber', '1'))
+
+ # Set the DPI. Mainly only for testing.
+ if self.vertical_dpi:
+ attributes.append(('verticalDpi', self.vertical_dpi))
+
+ if self.horizontal_dpi:
+ attributes.append(('horizontalDpi', self.horizontal_dpi))
+
+ self._xml_empty_tag('pageSetup', attributes)
+
+ def _write_print_options(self):
+ # Write the <printOptions> element.
+ attributes = []
+
+ if not self.print_options_changed:
+ return
+
+ # Set horizontal centering.
+ if self.hcenter:
+ attributes.append(('horizontalCentered', 1))
+
+ # Set vertical centering.
+ if self.vcenter:
+ attributes.append(('verticalCentered', 1))
+
+ # Enable row and column headers.
+ if self.print_headers:
+ attributes.append(('headings', 1))
+
+ # Set printed gridlines.
+ if self.print_gridlines:
+ attributes.append(('gridLines', 1))
+
+ self._xml_empty_tag('printOptions', attributes)
+
+ def _write_header_footer(self):
+ # Write the <headerFooter> element.
+ attributes = []
+
+ if not self.header_footer_scales:
+ attributes.append(('scaleWithDoc', 0))
+
+ if not self.header_footer_aligns:
+ attributes.append(('alignWithMargins', 0))
+
+ if self.header_footer_changed:
+ self._xml_start_tag('headerFooter', attributes)
+ if self.header:
+ self._write_odd_header()
+ if self.footer:
+ self._write_odd_footer()
+ self._xml_end_tag('headerFooter')
+ elif self.excel2003_style:
+ self._xml_empty_tag('headerFooter', attributes)
+
+ def _write_odd_header(self):
+ # Write the <headerFooter> element.
+ self._xml_data_element('oddHeader', self.header)
+
+ def _write_odd_footer(self):
+ # Write the <headerFooter> element.
+ self._xml_data_element('oddFooter', self.footer)
+
+ def _write_rows(self):
+ # Write out the worksheet data as a series of rows and cells.
+ self._calculate_spans()
+
+ for row_num in range(self.dim_rowmin, self.dim_rowmax + 1):
+
+ if (row_num in self.set_rows or row_num in self.comments
+ or self.table[row_num]):
+ # Only process rows with formatting, cell data and/or comments.
+
+ span_index = int(row_num / 16)
+
+ if span_index in self.row_spans:
+ span = self.row_spans[span_index]
+ else:
+ span = None
+
+ if self.table[row_num]:
+ # Write the cells if the row contains data.
+ if row_num not in self.set_rows:
+ self._write_row(row_num, span)
+ else:
+ self._write_row(row_num, span, self.set_rows[row_num])
+
+ for col_num in range(self.dim_colmin, self.dim_colmax + 1):
+ if col_num in self.table[row_num]:
+ col_ref = self.table[row_num][col_num]
+ self._write_cell(row_num, col_num, col_ref)
+
+ self._xml_end_tag('row')
+
+ elif row_num in self.comments:
+ # Row with comments in cells.
+ self._write_empty_row(row_num, span,
+ self.set_rows[row_num])
+ else:
+ # Blank row with attributes only.
+ self._write_empty_row(row_num, span,
+ self.set_rows[row_num])
+
+ def _write_single_row(self, current_row_num=0):
+ # Write out the worksheet data as a single row with cells.
+ # This method is used when memory optimization is on. A single
+ # row is written and the data table is reset. That way only
+ # one row of data is kept in memory at any one time. We don't
+ # write span data in the optimized case since it is optional.
+
+ # Set the new previous row as the current row.
+ row_num = self.previous_row
+ self.previous_row = current_row_num
+
+ if (row_num in self.set_rows or row_num in self.comments
+ or self.table[row_num]):
+ # Only process rows with formatting, cell data and/or comments.
+
+ # No span data in optimized mode.
+ span = None
+
+ if self.table[row_num]:
+ # Write the cells if the row contains data.
+ if row_num not in self.set_rows:
+ self._write_row(row_num, span)
+ else:
+ self._write_row(row_num, span, self.set_rows[row_num])
+
+ for col_num in range(self.dim_colmin, self.dim_colmax + 1):
+ if col_num in self.table[row_num]:
+ col_ref = self.table[row_num][col_num]
+ self._write_cell(row_num, col_num, col_ref)
+
+ self._xml_end_tag('row')
+ else:
+ # Row attributes or comments only.
+ self._write_empty_row(row_num, span, self.set_rows[row_num])
+
+ # Reset table.
+ self.table.clear()
+
+ def _calculate_spans(self):
+ # Calculate the "spans" attribute of the <row> tag. This is an
+ # XLSX optimization and isn't strictly required. However, it
+ # makes comparing files easier. The span is the same for each
+ # block of 16 rows.
+ spans = {}
+ span_min = None
+ span_max = None
+
+ for row_num in range(self.dim_rowmin, self.dim_rowmax + 1):
+
+ if row_num in self.table:
+ # Calculate spans for cell data.
+ for col_num in range(self.dim_colmin, self.dim_colmax + 1):
+ if col_num in self.table[row_num]:
+ if span_min is None:
+ span_min = col_num
+ span_max = col_num
+ else:
+ if col_num < span_min:
+ span_min = col_num
+ if col_num > span_max:
+ span_max = col_num
+
+ if row_num in self.comments:
+ # Calculate spans for comments.
+ for col_num in range(self.dim_colmin, self.dim_colmax + 1):
+ if (row_num in self.comments
+ and col_num in self.comments[row_num]):
+ if span_min is None:
+ span_min = col_num
+ span_max = col_num
+ else:
+ if col_num < span_min:
+ span_min = col_num
+ if col_num > span_max:
+ span_max = col_num
+
+ if ((row_num + 1) % 16 == 0) or row_num == self.dim_rowmax:
+ span_index = int(row_num / 16)
+
+ if span_min is not None:
+ span_min += 1
+ span_max += 1
+ spans[span_index] = "%s:%s" % (span_min, span_max)
+ span_min = None
+
+ self.row_spans = spans
+
+ def _write_row(self, row, spans, properties=None, empty_row=False):
+ # Write the <row> element.
+ xf_index = 0
+
+ if properties:
+ height, cell_format, hidden, level, collapsed = properties
+ else:
+ height, cell_format, hidden, level, collapsed = None, None, 0, 0, 0
+
+ if height is None:
+ height = self.default_row_height
+
+ attributes = [('r', row + 1)]
+
+ # Get the cell_format index.
+ if cell_format:
+ xf_index = cell_format._get_xf_index()
+
+ # Add row attributes where applicable.
+ if spans:
+ attributes.append(('spans', spans))
+
+ if xf_index:
+ attributes.append(('s', xf_index))
+
+ if cell_format:
+ attributes.append(('customFormat', 1))
+
+ if height != self.original_row_height:
+ attributes.append(('ht', height))
+
+ if hidden:
+ attributes.append(('hidden', 1))
+
+ if height != self.original_row_height:
+ attributes.append(('customHeight', 1))
+
+ if level:
+ attributes.append(('outlineLevel', level))
+
+ if collapsed:
+ attributes.append(('collapsed', 1))
+
+ if self.excel_version == 2010:
+ attributes.append(('x14ac:dyDescent', '0.25'))
+
+ if empty_row:
+ self._xml_empty_tag_unencoded('row', attributes)
+ else:
+ self._xml_start_tag_unencoded('row', attributes)
+
+ def _write_empty_row(self, row, spans, properties=None):
+ # Write and empty <row> element.
+ self._write_row(row, spans, properties, empty_row=True)
+
+ def _write_cell(self, row, col, cell):
+ # Write the <cell> element.
+ # Note. This is the innermost loop so efficiency is important.
+
+ error_codes = ['#DIV/0!', '#N/A', '#NAME?', '#NULL!',
+ '#NUM!', '#REF!', '#VALUE!']
+
+ cell_range = xl_rowcol_to_cell_fast(row, col)
+
+ attributes = [('r', cell_range)]
+
+ if cell.format:
+ # Add the cell format index.
+ xf_index = cell.format._get_xf_index()
+ attributes.append(('s', xf_index))
+ elif row in self.set_rows and self.set_rows[row][1]:
+ # Add the row format.
+ row_xf = self.set_rows[row][1]
+ attributes.append(('s', row_xf._get_xf_index()))
+ elif col in self.col_formats:
+ # Add the column format.
+ col_xf = self.col_formats[col]
+ attributes.append(('s', col_xf._get_xf_index()))
+
+ # Write the various cell types.
+ if type(cell).__name__ == 'Number':
+ # Write a number.
+ self._xml_number_element(cell.number, attributes)
+
+ elif type(cell).__name__ == 'String':
+ # Write a string.
+ string = cell.string
+
+ if not self.optimization:
+ # Write a shared string.
+ self._xml_string_element(string, attributes)
+ else:
+ # Write an optimized in-line string.
+
+ # Escape control characters. See SharedString.pm for details.
+ string = re.sub('(_x[0-9a-fA-F]{4}_)', r'_x005F\1', string)
+ string = re.sub(r'([\x00-\x08\x0B-\x1F])',
+ lambda match: "_x%04X_" %
+ ord(match.group(1)), string)
+
+ # Write any rich strings without further tags.
+ if re.search('^<r>', string) and re.search('</r>$', string):
+ self._xml_rich_inline_string(string, attributes)
+ else:
+ # Add attribute to preserve leading or trailing whitespace.
+ preserve = 0
+ if re.search('^\s', string) or re.search('\s$', string):
+ preserve = 1
+
+ self._xml_inline_string(string, preserve, attributes)
+
+ elif type(cell).__name__ == 'Formula':
+ # Write a formula. First check the formula value type.
+ value = cell.value
+ if type(cell.value) == bool:
+ attributes.append(('t', 'b'))
+ if cell.value:
+ value = 1
+ else:
+ value = 0
+
+ elif isinstance(cell.value, str_types):
+ if cell.value in error_codes:
+ attributes.append(('t', 'e'))
+ else:
+ attributes.append(('t', 'str'))
+
+ self._xml_formula_element(cell.formula, value, attributes)
+
+ elif type(cell).__name__ == 'ArrayFormula':
+ # Write a array formula.
+
+ # First check if the formula value is a string.
+ try:
+ float(cell.value)
+ except ValueError:
+ attributes.append(('t', 'str'))
+
+ # Write an array formula.
+ self._xml_start_tag('c', attributes)
+ self._write_cell_array_formula(cell.formula, cell.range)
+ self._write_cell_value(cell.value)
+ self._xml_end_tag('c')
+
+ elif type(cell).__name__ == 'Blank':
+ # Write a empty cell.
+ self._xml_empty_tag('c', attributes)
+
+ elif type(cell).__name__ == 'Boolean':
+ # Write a boolean cell.
+ attributes.append(('t', 'b'))
+ self._xml_start_tag('c', attributes)
+ self._write_cell_value(cell.boolean)
+ self._xml_end_tag('c')
+
+ def _write_cell_value(self, value):
+ # Write the cell value <v> element.
+ if value is None:
+ value = ''
+
+ self._xml_data_element('v', value)
+
+ def _write_cell_array_formula(self, formula, cell_range):
+ # Write the cell array formula <f> element.
+ attributes = [
+ ('t', 'array'),
+ ('ref', cell_range)
+ ]
+
+ self._xml_data_element('f', formula, attributes)
+
+ def _write_sheet_pr(self):
+ # Write the <sheetPr> element for Sheet level properties.
+ attributes = []
+
+ if (not self.fit_page
+ and not self.filter_on
+ and not self.tab_color
+ and not self.outline_changed
+ and not self.vba_codename):
+ return
+
+ if self.vba_codename:
+ attributes.append(('codeName', self.vba_codename))
+
+ if self.filter_on:
+ attributes.append(('filterMode', 1))
+
+ if (self.fit_page
+ or self.tab_color
+ or self.outline_changed):
+ self._xml_start_tag('sheetPr', attributes)
+ self._write_tab_color()
+ self._write_outline_pr()
+ self._write_page_set_up_pr()
+ self._xml_end_tag('sheetPr')
+ else:
+ self._xml_empty_tag('sheetPr', attributes)
+
+ def _write_page_set_up_pr(self):
+ # Write the <pageSetUpPr> element.
+ if not self.fit_page:
+ return
+
+ attributes = [('fitToPage', 1)]
+ self._xml_empty_tag('pageSetUpPr', attributes)
+
+ def _write_tab_color(self):
+ # Write the <tabColor> element.
+ color = self.tab_color
+
+ if not color:
+ return
+
+ attributes = [('rgb', color)]
+
+ self._xml_empty_tag('tabColor', attributes)
+
+ def _write_outline_pr(self):
+ # Write the <outlinePr> element.
+ attributes = []
+
+ if not self.outline_changed:
+ return
+
+ if self.outline_style:
+ attributes.append(("applyStyles", 1))
+ if not self.outline_below:
+ attributes.append(("summaryBelow", 0))
+ if not self.outline_right:
+ attributes.append(("summaryRight", 0))
+ if not self.outline_on:
+ attributes.append(("showOutlineSymbols", 0))
+
+ self._xml_empty_tag('outlinePr', attributes)
+
+ def _write_row_breaks(self):
+ # Write the <rowBreaks> element.
+ page_breaks = self._sort_pagebreaks(self.hbreaks)
+
+ if not page_breaks:
+ return
+
+ count = len(page_breaks)
+
+ attributes = [
+ ('count', count),
+ ('manualBreakCount', count),
+ ]
+
+ self._xml_start_tag('rowBreaks', attributes)
+
+ for row_num in page_breaks:
+ self._write_brk(row_num, 16383)
+
+ self._xml_end_tag('rowBreaks')
+
+ def _write_col_breaks(self):
+ # Write the <colBreaks> element.
+ page_breaks = self._sort_pagebreaks(self.vbreaks)
+
+ if not page_breaks:
+ return
+
+ count = len(page_breaks)
+
+ attributes = [
+ ('count', count),
+ ('manualBreakCount', count),
+ ]
+
+ self._xml_start_tag('colBreaks', attributes)
+
+ for col_num in page_breaks:
+ self._write_brk(col_num, 1048575)
+
+ self._xml_end_tag('colBreaks')
+
+ def _write_brk(self, brk_id, brk_max):
+ # Write the <brk> element.
+ attributes = [
+ ('id', brk_id),
+ ('max', brk_max),
+ ('man', 1)]
+
+ self._xml_empty_tag('brk', attributes)
+
+ def _write_merge_cells(self):
+ # Write the <mergeCells> element.
+ merged_cells = self.merge
+ count = len(merged_cells)
+
+ if not count:
+ return
+
+ attributes = [('count', count)]
+
+ self._xml_start_tag('mergeCells', attributes)
+
+ for merged_range in merged_cells:
+
+ # Write the mergeCell element.
+ self._write_merge_cell(merged_range)
+
+ self._xml_end_tag('mergeCells')
+
+ def _write_merge_cell(self, merged_range):
+ # Write the <mergeCell> element.
+ (row_min, col_min, row_max, col_max) = merged_range
+
+ # Convert the merge dimensions to a cell range.
+ cell_1 = xl_rowcol_to_cell(row_min, col_min)
+ cell_2 = xl_rowcol_to_cell(row_max, col_max)
+ ref = cell_1 + ':' + cell_2
+
+ attributes = [('ref', ref)]
+
+ self._xml_empty_tag('mergeCell', attributes)
+
+ def _write_hyperlinks(self):
+ # Process any stored hyperlinks in row/col order and write the
+ # <hyperlinks> element. The attributes are different for internal
+ # and external links.
+ hlink_refs = []
+ display = None
+
+ # Sort the hyperlinks into row order.
+ row_nums = sorted(self.hyperlinks.keys())
+
+ # Exit if there are no hyperlinks to process.
+ if not row_nums:
+ return
+
+ # Iterate over the rows.
+ for row_num in row_nums:
+ # Sort the hyperlinks into column order.
+ col_nums = sorted(self.hyperlinks[row_num].keys())
+
+ # Iterate over the columns.
+ for col_num in col_nums:
+ # Get the link data for this cell.
+ link = self.hyperlinks[row_num][col_num]
+ link_type = link["link_type"]
+
+ # If the cell isn't a string then we have to add the url as
+ # the string to display.
+ if (self.table
+ and self.table[row_num]
+ and self.table[row_num][col_num]):
+ cell = self.table[row_num][col_num]
+ if type(cell).__name__ != 'String':
+ display = link["url"]
+
+ if link_type == 1:
+ # External link with rel file relationship.
+ self.rel_count += 1
+
+ hlink_refs.append([link_type,
+ row_num,
+ col_num,
+ self.rel_count,
+ link["str"],
+ display,
+ link["tip"]])
+
+ # Links for use by the packager.
+ self.external_hyper_links.append(['/hyperlink',
+ link["url"], 'External'])
+ else:
+ # Internal link with rel file relationship.
+ hlink_refs.append([link_type,
+ row_num,
+ col_num,
+ link["url"],
+ link["str"],
+ link["tip"]])
+
+ # Write the hyperlink elements.
+ self._xml_start_tag('hyperlinks')
+
+ for args in hlink_refs:
+ link_type = args.pop(0)
+
+ if link_type == 1:
+ self._write_hyperlink_external(*args)
+ elif link_type == 2:
+ self._write_hyperlink_internal(*args)
+
+ self._xml_end_tag('hyperlinks')
+
+ def _write_hyperlink_external(self, row, col, id_num, location=None,
+ display=None, tooltip=None):
+ # Write the <hyperlink> element for external links.
+ ref = xl_rowcol_to_cell(row, col)
+ r_id = 'rId' + str(id_num)
+
+ attributes = [
+ ('ref', ref),
+ ('r:id', r_id)]
+
+ if location is not None:
+ attributes.append(('location', location))
+ if display is not None:
+ attributes.append(('display', display))
+ if tooltip is not None:
+ attributes.append(('tooltip', tooltip))
+
+ self._xml_empty_tag('hyperlink', attributes)
+
+ def _write_hyperlink_internal(self, row, col, location=None, display=None,
+ tooltip=None):
+ # Write the <hyperlink> element for internal links.
+ ref = xl_rowcol_to_cell(row, col)
+
+ attributes = [
+ ('ref', ref),
+ ('location', location)]
+
+ if tooltip is not None:
+ attributes.append(('tooltip', tooltip))
+ attributes.append(('display', display))
+
+ self._xml_empty_tag('hyperlink', attributes)
+
+ def _write_auto_filter(self):
+ # Write the <autoFilter> element.
+ if not self.autofilter_ref:
+ return
+
+ attributes = [('ref', self.autofilter_ref)]
+
+ if self.filter_on:
+ # Autofilter defined active filters.
+ self._xml_start_tag('autoFilter', attributes)
+ self._write_autofilters()
+ self._xml_end_tag('autoFilter')
+
+ else:
+ # Autofilter defined without active filters.
+ self._xml_empty_tag('autoFilter', attributes)
+
+ def _write_autofilters(self):
+ # Function to iterate through the columns that form part of an
+ # autofilter range and write the appropriate filters.
+ (col1, col2) = self.filter_range
+
+ for col in range(col1, col2 + 1):
+ # Skip if column doesn't have an active filter.
+ if col not in self.filter_cols:
+ continue
+
+ # Retrieve the filter tokens and write the autofilter records.
+ tokens = self.filter_cols[col]
+ filter_type = self.filter_type[col]
+
+ # Filters are relative to first column in the autofilter.
+ self._write_filter_column(col - col1, filter_type, tokens)
+
+ def _write_filter_column(self, col_id, filter_type, filters):
+ # Write the <filterColumn> element.
+ attributes = [('colId', col_id)]
+
+ self._xml_start_tag('filterColumn', attributes)
+
+ if filter_type == 1:
+ # Type == 1 is the new XLSX style filter.
+ self._write_filters(filters)
+ else:
+ # Type == 0 is the classic "custom" filter.
+ self._write_custom_filters(filters)
+
+ self._xml_end_tag('filterColumn')
+
+ def _write_filters(self, filters):
+ # Write the <filters> element.
+
+ if len(filters) == 1 and filters[0] == 'blanks':
+ # Special case for blank cells only.
+ self._xml_empty_tag('filters', [('blank', 1)])
+ else:
+ # General case.
+ self._xml_start_tag('filters')
+
+ for autofilter in filters:
+ self._write_filter(autofilter)
+
+ self._xml_end_tag('filters')
+
+ def _write_filter(self, val):
+ # Write the <filter> element.
+ attributes = [('val', val)]
+
+ self._xml_empty_tag('filter', attributes)
+
+ def _write_custom_filters(self, tokens):
+ # Write the <customFilters> element.
+ if len(tokens) == 2:
+ # One filter expression only.
+ self._xml_start_tag('customFilters')
+ self._write_custom_filter(*tokens)
+ self._xml_end_tag('customFilters')
+ else:
+ # Two filter expressions.
+ attributes = []
+
+ # Check if the "join" operand is "and" or "or".
+ if tokens[2] == 0:
+ attributes = [('and', 1)]
+ else:
+ attributes = [('and', 0)]
+
+ # Write the two custom filters.
+ self._xml_start_tag('customFilters', attributes)
+ self._write_custom_filter(tokens[0], tokens[1])
+ self._write_custom_filter(tokens[3], tokens[4])
+ self._xml_end_tag('customFilters')
+
+ def _write_custom_filter(self, operator, val):
+ # Write the <customFilter> element.
+ attributes = []
+
+ operators = {
+ 1: 'lessThan',
+ 2: 'equal',
+ 3: 'lessThanOrEqual',
+ 4: 'greaterThan',
+ 5: 'notEqual',
+ 6: 'greaterThanOrEqual',
+ 22: 'equal',
+ }
+
+ # Convert the operator from a number to a descriptive string.
+ if operators[operator] is not None:
+ operator = operators[operator]
+ else:
+ warn("Unknown operator = %s" % operator)
+
+ # The 'equal' operator is the default attribute and isn't stored.
+ if not operator == 'equal':
+ attributes.append(('operator', operator))
+ attributes.append(('val', val))
+
+ self._xml_empty_tag('customFilter', attributes)
+
+ def _write_sheet_protection(self):
+ # Write the <sheetProtection> element.
+ attributes = []
+
+ if not self.protect_options:
+ return
+
+ options = self.protect_options
+
+ if options['password']:
+ attributes.append(('password', options['password']))
+ if options['sheet']:
+ attributes.append(('sheet', 1))
+ if options['content']:
+ attributes.append(('content', 1))
+ if not options['objects']:
+ attributes.append(('objects', 1))
+ if not options['scenarios']:
+ attributes.append(('scenarios', 1))
+ if options['format_cells']:
+ attributes.append(('formatCells', 0))
+ if options['format_columns']:
+ attributes.append(('formatColumns', 0))
+ if options['format_rows']:
+ attributes.append(('formatRows', 0))
+ if options['insert_columns']:
+ attributes.append(('insertColumns', 0))
+ if options['insert_rows']:
+ attributes.append(('insertRows', 0))
+ if options['insert_hyperlinks']:
+ attributes.append(('insertHyperlinks', 0))
+ if options['delete_columns']:
+ attributes.append(('deleteColumns', 0))
+ if options['delete_rows']:
+ attributes.append(('deleteRows', 0))
+ if not options['select_locked_cells']:
+ attributes.append(('selectLockedCells', 1))
+ if options['sort']:
+ attributes.append(('sort', 0))
+ if options['autofilter']:
+ attributes.append(('autoFilter', 0))
+ if options['pivot_tables']:
+ attributes.append(('pivotTables', 0))
+ if not options['select_unlocked_cells']:
+ attributes.append(('selectUnlockedCells', 1))
+
+ self._xml_empty_tag('sheetProtection', attributes)
+
+ def _write_drawings(self):
+ # Write the <drawing> elements.
+ if not self.drawing:
+ return
+
+ self.rel_count += 1
+ self._write_drawing(self.rel_count)
+
+ def _write_drawing(self, drawing_id):
+ # Write the <drawing> element.
+ r_id = 'rId' + str(drawing_id)
+
+ attributes = [('r:id', r_id)]
+
+ self._xml_empty_tag('drawing', attributes)
+
+ def _write_legacy_drawing(self):
+ # Write the <legacyDrawing> element.
+ if not self.has_vml:
+ return
+
+ # Increment the relationship id for any drawings or comments.
+ self.rel_count += 1
+ r_id = 'rId' + str(self.rel_count)
+
+ attributes = [('r:id', r_id)]
+
+ self._xml_empty_tag('legacyDrawing', attributes)
+
+ def _write_legacy_drawing_hf(self):
+ # Write the <legacyDrawingHF> element.
+ if not self.has_header_vml:
+ return
+
+ # Increment the relationship id for any drawings or comments.
+ self.rel_count += 1
+ r_id = 'rId' + str(self.rel_count)
+
+ attributes = [('r:id', r_id)]
+
+ self._xml_empty_tag('legacyDrawingHF', attributes)
+
+ def _write_data_validations(self):
+ # Write the <dataValidations> element.
+ validations = self.validations
+ count = len(validations)
+
+ if not count:
+ return
+
+ attributes = [('count', count)]
+
+ self._xml_start_tag('dataValidations', attributes)
+
+ for validation in validations:
+
+ # Write the dataValidation element.
+ self._write_data_validation(validation)
+
+ self._xml_end_tag('dataValidations')
+
+ def _write_data_validation(self, options):
+ # Write the <dataValidation> element.
+ sqref = ''
+ attributes = []
+
+ # Set the cell range(s) for the data validation.
+ for cells in options['cells']:
+
+ # Add a space between multiple cell ranges.
+ if sqref != '':
+ sqref += ' '
+
+ (row_first, col_first, row_last, col_last) = cells
+
+ # Swap last row/col for first row/col as necessary
+ if row_first > row_last:
+ (row_first, row_last) = (row_last, row_first)
+
+ if col_first > col_last:
+ (col_first, col_last) = (col_last, col_first)
+
+ # If the first and last cell are the same write a single cell.
+ if (row_first == row_last) and (col_first == col_last):
+ sqref += xl_rowcol_to_cell(row_first, col_first)
+ else:
+ sqref += xl_range(row_first, col_first, row_last, col_last)
+
+ if options['validate'] != 'none':
+ attributes.append(('type', options['validate']))
+
+ if options['criteria'] != 'between':
+ attributes.append(('operator', options['criteria']))
+
+ if 'error_type' in options:
+ if options['error_type'] == 1:
+ attributes.append(('errorStyle', 'warning'))
+ if options['error_type'] == 2:
+ attributes.append(('errorStyle', 'information'))
+
+ if options['ignore_blank']:
+ attributes.append(('allowBlank', 1))
+
+ if not options['dropdown']:
+ attributes.append(('showDropDown', 1))
+
+ if options['show_input']:
+ attributes.append(('showInputMessage', 1))
+
+ if options['show_error']:
+ attributes.append(('showErrorMessage', 1))
+
+ if 'error_title' in options:
+ attributes.append(('errorTitle', options['error_title']))
+
+ if 'error_message' in options:
+ attributes.append(('error', options['error_message']))
+
+ if 'input_title' in options:
+ attributes.append(('promptTitle', options['input_title']))
+
+ if 'input_message' in options:
+ attributes.append(('prompt', options['input_message']))
+
+ attributes.append(('sqref', sqref))
+
+ if options['validate'] == 'none':
+ self._xml_empty_tag('dataValidation', attributes)
+ else:
+ self._xml_start_tag('dataValidation', attributes)
+
+ # Write the formula1 element.
+ self._write_formula_1(options['value'])
+
+ # Write the formula2 element.
+ if options['maximum'] is not None:
+ self._write_formula_2(options['maximum'])
+
+ self._xml_end_tag('dataValidation')
+
+ def _write_formula_1(self, formula):
+ # Write the <formula1> element.
+
+ if type(formula) is list:
+ formula = self._csv_join(*formula)
+ formula = '"%s"' % formula
+ else:
+ # Check if the formula is a number.
+ try:
+ float(formula)
+ except ValueError:
+ # Not a number. Remove the formula '=' sign if it exists.
+ if formula.startswith('='):
+ formula = formula.lstrip('=')
+
+ self._xml_data_element('formula1', formula)
+
+ def _write_formula_2(self, formula):
+ # Write the <formula2> element.
+
+ # Check if the formula is a number.
+ try:
+ float(formula)
+ except ValueError:
+ # Not a number. Remove the formula '=' sign if it exists.
+ if formula.startswith('='):
+ formula = formula.lstrip('=')
+
+ self._xml_data_element('formula2', formula)
+
+ def _write_conditional_formats(self):
+ # Write the Worksheet conditional formats.
+ ranges = sorted(self.cond_formats.keys())
+
+ if not ranges:
+ return
+
+ for cond_range in ranges:
+ self._write_conditional_formatting(cond_range,
+ self.cond_formats[cond_range])
+
+ def _write_conditional_formatting(self, cond_range, params):
+ # Write the <conditionalFormatting> element.
+ attributes = [('sqref', cond_range)]
+ self._xml_start_tag('conditionalFormatting', attributes)
+ for param in params:
+ # Write the cfRule element.
+ self._write_cf_rule(param)
+ self._xml_end_tag('conditionalFormatting')
+
+ def _write_cf_rule(self, params):
+ # Write the <cfRule> element.
+ attributes = [('type', params['type'])]
+
+ if 'format' in params and params['format'] is not None:
+ attributes.append(('dxfId', params['format']))
+
+ attributes.append(('priority', params['priority']))
+
+ if params['type'] == 'cellIs':
+ attributes.append(('operator', params['criteria']))
+
+ self._xml_start_tag('cfRule', attributes)
+
+ if 'minimum' in params and 'maximum' in params:
+ self._write_formula(params['minimum'])
+ self._write_formula(params['maximum'])
+ else:
+ self._write_formula(params['value'])
+
+ self._xml_end_tag('cfRule')
+
+ elif params['type'] == 'aboveAverage':
+ if re.search('below', params['criteria']):
+ attributes.append(('aboveAverage', 0))
+
+ if re.search('equal', params['criteria']):
+ attributes.append(('equalAverage', 1))
+
+ if re.search('[123] std dev', params['criteria']):
+ match = re.search('([123]) std dev', params['criteria'])
+ attributes.append(('stdDev', match.group(1)))
+
+ self._xml_empty_tag('cfRule', attributes)
+
+ elif params['type'] == 'top10':
+ if 'criteria' in params and params['criteria'] == '%':
+ attributes.append(('percent', 1))
+
+ if 'direction' in params:
+ attributes.append(('bottom', 1))
+
+ rank = params['value'] or 10
+ attributes.append(('rank', rank))
+
+ self._xml_empty_tag('cfRule', attributes)
+
+ elif params['type'] == 'duplicateValues':
+ self._xml_empty_tag('cfRule', attributes)
+
+ elif params['type'] == 'uniqueValues':
+ self._xml_empty_tag('cfRule', attributes)
+
+ elif (params['type'] == 'containsText'
+ or params['type'] == 'notContainsText'
+ or params['type'] == 'beginsWith'
+ or params['type'] == 'endsWith'):
+ attributes.append(('operator', params['criteria']))
+ attributes.append(('text', params['value']))
+ self._xml_start_tag('cfRule', attributes)
+ self._write_formula(params['formula'])
+ self._xml_end_tag('cfRule')
+
+ elif params['type'] == 'timePeriod':
+ attributes.append(('timePeriod', params['criteria']))
+ self._xml_start_tag('cfRule', attributes)
+ self._write_formula(params['formula'])
+ self._xml_end_tag('cfRule')
+
+ elif (params['type'] == 'containsBlanks'
+ or params['type'] == 'notContainsBlanks'
+ or params['type'] == 'containsErrors'
+ or params['type'] == 'notContainsErrors'):
+ self._xml_start_tag('cfRule', attributes)
+ self._write_formula(params['formula'])
+ self._xml_end_tag('cfRule')
+
+ elif params['type'] == 'colorScale':
+ self._xml_start_tag('cfRule', attributes)
+ self._write_color_scale(params)
+ self._xml_end_tag('cfRule')
+
+ elif params['type'] == 'dataBar':
+ self._xml_start_tag('cfRule', attributes)
+ self._write_data_bar(params)
+ self._xml_end_tag('cfRule')
+
+ elif params['type'] == 'expression':
+ self._xml_start_tag('cfRule', attributes)
+ self._write_formula(params['criteria'])
+ self._xml_end_tag('cfRule')
+
+ def _write_formula(self, formula):
+ # Write the <formula> element.
+
+ # Check if the formula is a number.
+ try:
+ float(formula)
+ except ValueError:
+ # Not a number. Remove the formula '=' sign if it exists.
+ if formula.startswith('='):
+ formula = formula.lstrip('=')
+
+ self._xml_data_element('formula', formula)
+
+ def _write_color_scale(self, param):
+ # Write the <colorScale> element.
+
+ self._xml_start_tag('colorScale')
+
+ self._write_cfvo(param['min_type'], param['min_value'])
+
+ if param['mid_type'] is not None:
+ self._write_cfvo(param['mid_type'], param['mid_value'])
+
+ self._write_cfvo(param['max_type'], param['max_value'])
+
+ self._write_color('rgb', param['min_color'])
+
+ if param['mid_color'] is not None:
+ self._write_color('rgb', param['mid_color'])
+
+ self._write_color('rgb', param['max_color'])
+
+ self._xml_end_tag('colorScale')
+
+ def _write_data_bar(self, param):
+ # Write the <dataBar> element.
+ attributes = []
+
+ if 'min_length' in param:
+ attributes.append(('minLength', param['min_length']))
+
+ if 'max_length' in param:
+ attributes.append(('maxLength', param['max_length']))
+
+ self._xml_start_tag('dataBar', attributes)
+
+ self._write_cfvo(param['min_type'], param['min_value'])
+ self._write_cfvo(param['max_type'], param['max_value'])
+ self._write_color('rgb', param['bar_color'])
+
+ self._xml_end_tag('dataBar')
+
+ def _write_cfvo(self, cf_type, val):
+ # Write the <cfvo> element.
+ attributes = [('type', cf_type), ('val', val)]
+
+ self._xml_empty_tag('cfvo', attributes)
+
+ def _write_color(self, name, value):
+ # Write the <color> element.
+ attributes = [(name, value)]
+
+ self._xml_empty_tag('color', attributes)
+
+ def _write_selections(self):
+ # Write the <selection> elements.
+ for selection in self.selections:
+ self._write_selection(*selection)
+
+ def _write_selection(self, pane, active_cell, sqref):
+ # Write the <selection> element.
+ attributes = []
+
+ if pane:
+ attributes.append(('pane', pane))
+
+ if active_cell:
+ attributes.append(('activeCell', active_cell))
+
+ if sqref:
+ attributes.append(('sqref', sqref))
+
+ self._xml_empty_tag('selection', attributes)
+
+ def _write_panes(self):
+ # Write the frozen or split <pane> elements.
+ panes = self.panes
+
+ if not len(panes):
+ return
+
+ if panes[4] == 2:
+ self._write_split_panes(*panes)
+ else:
+ self._write_freeze_panes(*panes)
+
+ def _write_freeze_panes(self, row, col, top_row, left_col, pane_type):
+ # Write the <pane> element for freeze panes.
+ attributes = []
+
+ y_split = row
+ x_split = col
+ top_left_cell = xl_rowcol_to_cell(top_row, left_col)
+ active_pane = ''
+ state = ''
+ active_cell = ''
+ sqref = ''
+
+ # Move user cell selection to the panes.
+ if self.selections:
+ (_, active_cell, sqref) = self.selections[0]
+ self.selections = []
+
+ # Set the active pane.
+ if row and col:
+ active_pane = 'bottomRight'
+
+ row_cell = xl_rowcol_to_cell(row, 0)
+ col_cell = xl_rowcol_to_cell(0, col)
+
+ self.selections.append(['topRight', col_cell, col_cell])
+ self.selections.append(['bottomLeft', row_cell, row_cell])
+ self.selections.append(['bottomRight', active_cell, sqref])
+
+ elif col:
+ active_pane = 'topRight'
+ self.selections.append(['topRight', active_cell, sqref])
+
+ else:
+ active_pane = 'bottomLeft'
+ self.selections.append(['bottomLeft', active_cell, sqref])
+
+ # Set the pane type.
+ if pane_type == 0:
+ state = 'frozen'
+ elif pane_type == 1:
+ state = 'frozenSplit'
+ else:
+ state = 'split'
+
+ if x_split:
+ attributes.append(('xSplit', x_split))
+
+ if y_split:
+ attributes.append(('ySplit', y_split))
+
+ attributes.append(('topLeftCell', top_left_cell))
+ attributes.append(('activePane', active_pane))
+ attributes.append(('state', state))
+
+ self._xml_empty_tag('pane', attributes)
+
+ def _write_split_panes(self, row, col, top_row, left_col, pane_type):
+ # Write the <pane> element for split panes.
+ attributes = []
+ has_selection = 0
+ active_pane = ''
+ active_cell = ''
+ sqref = ''
+
+ y_split = row
+ x_split = col
+
+ # Move user cell selection to the panes.
+ if self.selections:
+ (_, active_cell, sqref) = self.selections[0]
+ self.selections = []
+ has_selection = 1
+
+ # Convert the row and col to 1/20 twip units with padding.
+ if y_split:
+ y_split = int(20 * y_split + 300)
+
+ if x_split:
+ x_split = self._calculate_x_split_width(x_split)
+
+ # For non-explicit topLeft definitions, estimate the cell offset based
+ # on the pixels dimensions. This is only a workaround and doesn't take
+ # adjusted cell dimensions into account.
+ if top_row == row and left_col == col:
+ top_row = int(0.5 + (y_split - 300) / 20 / 15)
+ left_col = int(0.5 + (x_split - 390) / 20 / 3 * 4 / 64)
+
+ top_left_cell = xl_rowcol_to_cell(top_row, left_col)
+
+ # If there is no selection set the active cell to the top left cell.
+ if not has_selection:
+ active_cell = top_left_cell
+ sqref = top_left_cell
+
+ # Set the Cell selections.
+ if row and col:
+ active_pane = 'bottomRight'
+
+ row_cell = xl_rowcol_to_cell(top_row, 0)
+ col_cell = xl_rowcol_to_cell(0, left_col)
+
+ self.selections.append(['topRight', col_cell, col_cell])
+ self.selections.append(['bottomLeft', row_cell, row_cell])
+ self.selections.append(['bottomRight', active_cell, sqref])
+
+ elif col:
+ active_pane = 'topRight'
+ self.selections.append(['topRight', active_cell, sqref])
+
+ else:
+ active_pane = 'bottomLeft'
+ self.selections.append(['bottomLeft', active_cell, sqref])
+
+ # Format splits to the same precision as Excel.
+ if x_split:
+ attributes.append(('xSplit', "%.16g" % x_split))
+
+ if y_split:
+ attributes.append(('ySplit', "%.16g" % y_split))
+
+ attributes.append(('topLeftCell', top_left_cell))
+
+ if has_selection:
+ attributes.append(('activePane', active_pane))
+
+ self._xml_empty_tag('pane', attributes)
+
+ def _calculate_x_split_width(self, width):
+ # Convert column width from user units to pane split width.
+
+ max_digit_width = 7 # For Calabri 11.
+ padding = 5
+
+ # Convert to pixels.
+ if width < 1:
+ pixels = int(width * (max_digit_width + padding) + 0.5)
+ else:
+ pixels = int(width * max_digit_width + 0.5) + padding
+
+ # Convert to points.
+ points = pixels * 3 / 4
+
+ # Convert to twips (twentieths of a point).
+ twips = points * 20
+
+ # Add offset/padding.
+ width = twips + 390
+
+ return width
+
+ def _write_table_parts(self):
+ # Write the <tableParts> element.
+ tables = self.tables
+ count = len(tables)
+
+ # Return if worksheet doesn't contain any tables.
+ if not count:
+ return
+
+ attributes = [('count', count,)]
+
+ self._xml_start_tag('tableParts', attributes)
+
+ for _ in tables:
+
+ # Write the tablePart element.
+ self.rel_count += 1
+ self._write_table_part(self.rel_count)
+
+ self._xml_end_tag('tableParts')
+
+ def _write_table_part(self, r_id):
+ # Write the <tablePart> element.
+
+ r_id = 'rId' + str(r_id)
+
+ attributes = [('r:id', r_id,)]
+
+ self._xml_empty_tag('tablePart', attributes)
+
+ def _write_ext_sparklines(self):
+ # Write the <extLst> element and sparkline sub-elements.
+ sparklines = self.sparklines
+ count = len(sparklines)
+
+ # Return if worksheet doesn't contain any sparklines.
+ if not count:
+ return
+
+ # Write the extLst element.
+ self._xml_start_tag('extLst')
+
+ # Write the ext element.
+ self._write_ext()
+
+ # Write the x14:sparklineGroups element.
+ self._write_sparkline_groups()
+
+ # Write the sparkline elements.
+ for sparkline in reversed(sparklines):
+
+ # Write the x14:sparklineGroup element.
+ self._write_sparkline_group(sparkline)
+
+ # Write the x14:colorSeries element.
+ self._write_color_series(sparkline['series_color'])
+
+ # Write the x14:colorNegative element.
+ self._write_color_negative(sparkline['negative_color'])
+
+ # Write the x14:colorAxis element.
+ self._write_color_axis()
+
+ # Write the x14:colorMarkers element.
+ self._write_color_markers(sparkline['markers_color'])
+
+ # Write the x14:colorFirst element.
+ self._write_color_first(sparkline['first_color'])
+
+ # Write the x14:colorLast element.
+ self._write_color_last(sparkline['last_color'])
+
+ # Write the x14:colorHigh element.
+ self._write_color_high(sparkline['high_color'])
+
+ # Write the x14:colorLow element.
+ self._write_color_low(sparkline['low_color'])
+
+ if sparkline['date_axis']:
+ self._xml_data_element('xm:f', sparkline['date_axis'])
+
+ self._write_sparklines(sparkline)
+
+ self._xml_end_tag('x14:sparklineGroup')
+
+ self._xml_end_tag('x14:sparklineGroups')
+ self._xml_end_tag('ext')
+ self._xml_end_tag('extLst')
+
+ def _write_sparklines(self, sparkline):
+ # Write the <x14:sparklines> element and <x14:sparkline> sub-elements.
+
+ # Write the sparkline elements.
+ self._xml_start_tag('x14:sparklines')
+
+ for i in range(sparkline['count']):
+ spark_range = sparkline['ranges'][i]
+ location = sparkline['locations'][i]
+
+ self._xml_start_tag('x14:sparkline')
+ self._xml_data_element('xm:f', spark_range)
+ self._xml_data_element('xm:sqref', location)
+ self._xml_end_tag('x14:sparkline')
+
+ self._xml_end_tag('x14:sparklines')
+
+ def _write_ext(self):
+ # Write the <ext> element.
+ schema = 'http://schemas.microsoft.com/office/'
+ xmlns_x_14 = schema + 'spreadsheetml/2009/9/main'
+ uri = '{05C60535-1F16-4fd2-B633-F4F36F0B64E0}'
+
+ attributes = [
+ ('xmlns:x14', xmlns_x_14),
+ ('uri', uri),
+ ]
+
+ self._xml_start_tag('ext', attributes)
+
+ def _write_sparkline_groups(self):
+ # Write the <x14:sparklineGroups> element.
+ xmlns_xm = 'http://schemas.microsoft.com/office/excel/2006/main'
+
+ attributes = [('xmlns:xm', xmlns_xm)]
+
+ self._xml_start_tag('x14:sparklineGroups', attributes)
+
+ def _write_sparkline_group(self, options):
+ # Write the <x14:sparklineGroup> element.
+ #
+ # Example for order.
+ #
+ # <x14:sparklineGroup
+ # manualMax="0"
+ # manualMin="0"
+ # lineWeight="2.25"
+ # type="column"
+ # dateAxis="1"
+ # displayEmptyCellsAs="span"
+ # markers="1"
+ # high="1"
+ # low="1"
+ # first="1"
+ # last="1"
+ # negative="1"
+ # displayXAxis="1"
+ # displayHidden="1"
+ # minAxisType="custom"
+ # maxAxisType="custom"
+ # rightToLeft="1">
+ #
+ empty = options.get('empty')
+ attributes = []
+
+ if options.get('max') is not None:
+ if options['max'] == 'group':
+ options['cust_max'] = 'group'
+ else:
+ attributes.append(('manualMax', options['max']))
+ options['cust_max'] = 'custom'
+
+ if options.get('min') is not None:
+
+ if options['min'] == 'group':
+ options['cust_min'] = 'group'
+ else:
+ attributes.append(('manualMin', options['min']))
+ options['cust_min'] = 'custom'
+
+ # Ignore the default type attribute (line).
+ if options['type'] != 'line':
+ attributes.append(('type', options['type']))
+
+ if options.get('weight'):
+ attributes.append(('lineWeight', options['weight']))
+
+ if options.get('date_axis'):
+ attributes.append(('dateAxis', 1))
+
+ if empty:
+ attributes.append(('displayEmptyCellsAs', empty))
+
+ if options.get('markers'):
+ attributes.append(('markers', 1))
+
+ if options.get('high'):
+ attributes.append(('high', 1))
+
+ if options.get('low'):
+ attributes.append(('low', 1))
+
+ if options.get('first'):
+ attributes.append(('first', 1))
+
+ if options.get('last'):
+ attributes.append(('last', 1))
+
+ if options.get('negative'):
+ attributes.append(('negative', 1))
+
+ if options.get('axis'):
+ attributes.append(('displayXAxis', 1))
+
+ if options.get('hidden'):
+ attributes.append(('displayHidden', 1))
+
+ if options.get('cust_min'):
+ attributes.append(('minAxisType', options['cust_min']))
+
+ if options.get('cust_max'):
+ attributes.append(('maxAxisType', options['cust_max']))
+
+ if options.get('reverse'):
+ attributes.append(('rightToLeft', 1))
+
+ self._xml_start_tag('x14:sparklineGroup', attributes)
+
+ def _write_spark_color(self, element, color):
+ # Helper function for the sparkline color functions below.
+ attributes = []
+
+ if color.get('rgb'):
+ attributes.append(('rgb', color['rgb']))
+
+ if color.get('theme'):
+ attributes.append(('theme', color['theme']))
+
+ if color.get('tint'):
+ attributes.append(('tint', color['tint']))
+
+ self._xml_empty_tag(element, attributes)
+
+ def _write_color_series(self, color):
+ # Write the <x14:colorSeries> element.
+ self._write_spark_color('x14:colorSeries', color)
+
+ def _write_color_negative(self, color):
+ # Write the <x14:colorNegative> element.
+ self._write_spark_color('x14:colorNegative', color)
+
+ def _write_color_axis(self):
+ # Write the <x14:colorAxis> element.
+ self._write_spark_color('x14:colorAxis', {'rgb': 'FF000000'})
+
+ def _write_color_markers(self, color):
+ # Write the <x14:colorMarkers> element.
+ self._write_spark_color('x14:colorMarkers', color)
+
+ def _write_color_first(self, color):
+ # Write the <x14:colorFirst> element.
+ self._write_spark_color('x14:colorFirst', color)
+
+ def _write_color_last(self, color):
+ # Write the <x14:colorLast> element.
+ self._write_spark_color('x14:colorLast', color)
+
+ def _write_color_high(self, color):
+ # Write the <x14:colorHigh> element.
+ self._write_spark_color('x14:colorHigh', color)
+
+ def _write_color_low(self, color):
+ # Write the <x14:colorLow> element.
+ self._write_spark_color('x14:colorLow', color)
+
+ def _write_phonetic_pr(self):
+ # Write the <phoneticPr> element.
+ attributes = [
+ ('fontId', '0'),
+ ('type', 'noConversion'),
+ ]
+
+ self._xml_empty_tag('phoneticPr', attributes)
diff --git a/xlsxwriter/xmlwriter.py b/xlsxwriter/xmlwriter.py
new file mode 100644
index 0000000..1410910
--- /dev/null
+++ b/xlsxwriter/xmlwriter.py
@@ -0,0 +1,210 @@
+###############################################################################
+#
+# XMLwriter - A base class for XlsxWriter classes.
+#
+# Used in conjunction with XlsxWriter.
+#
+# Copyright 2013-2016, John McNamara, jmcnamara at cpan.org
+#
+
+# Standard packages.
+import re
+import codecs
+
+# Standard packages in Python 2/3 compatibility mode.
+from .compatibility import StringIO
+
+
+class XMLwriter(object):
+ """
+ Simple XML writer class.
+
+ """
+
+ def __init__(self):
+ self.fh = None
+ self.escapes = re.compile('["&<>\n]')
+ self.internal_fh = False
+
+ def _set_filehandle(self, filehandle):
+ # Set the writer filehandle directly. Mainly for testing.
+ self.fh = filehandle
+ self.internal_fh = False
+
+ def _set_xml_writer(self, filename):
+ # Set the XML writer filehandle for the object.
+ if isinstance(filename, StringIO):
+ self.internal_fh = False
+ self.fh = filename
+ else:
+ self.internal_fh = True
+ self.fh = codecs.open(filename, 'w', 'utf-8')
+
+ def _xml_close(self):
+ # Close the XML filehandle if we created it.
+ if self.internal_fh:
+ self.fh.close()
+
+ def _xml_declaration(self):
+ # Write the XML declaration.
+ self.fh.write(
+ """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n""")
+
+ def _xml_start_tag(self, tag, attributes=[]):
+ # Write an XML start tag with optional attributes.
+ for key, value in attributes:
+ value = self._escape_attributes(value)
+ tag += ' %s="%s"' % (key, value)
+
+ self.fh.write("<%s>" % tag)
+
+ def _xml_start_tag_unencoded(self, tag, attributes=[]):
+ # Write an XML start tag with optional, unencoded, attributes.
+ # This is a minor speed optimization for elements that don't
+ # need encoding.
+ for key, value in attributes:
+ tag += ' %s="%s"' % (key, value)
+
+ self.fh.write("<%s>" % tag)
+
+ def _xml_end_tag(self, tag):
+ # Write an XML end tag.
+ self.fh.write("</%s>" % tag)
+
+ def _xml_empty_tag(self, tag, attributes=[]):
+ # Write an empty XML tag with optional attributes.
+ for key, value in attributes:
+ value = self._escape_attributes(value)
+ tag += ' %s="%s"' % (key, value)
+
+ self.fh.write("<%s/>" % tag)
+
+ def _xml_empty_tag_unencoded(self, tag, attributes=[]):
+ # Write an empty XML tag with optional, unencoded, attributes.
+ # This is a minor speed optimization for elements that don't
+ # need encoding.
+ for key, value in attributes:
+ tag += ' %s="%s"' % (key, value)
+
+ self.fh.write("<%s/>" % tag)
+
+ def _xml_data_element(self, tag, data, attributes=[]):
+ # Write an XML element containing data with optional attributes.
+ end_tag = tag
+
+ for key, value in attributes:
+ value = self._escape_attributes(value)
+ tag += ' %s="%s"' % (key, value)
+
+ data = self._escape_data(data)
+ self.fh.write("<%s>%s</%s>" % (tag, data, end_tag))
+
+ def _xml_string_element(self, index, attributes=[]):
+ # Optimized tag writer for <c> cell string elements in the inner loop.
+ attr = ''
+
+ for key, value in attributes:
+ value = self._escape_attributes(value)
+ attr += ' %s="%s"' % (key, value)
+
+ self.fh.write("""<c%s t="s"><v>%d</v></c>""" % (attr, index))
+
+ def _xml_si_element(self, string, attributes=[]):
+ # Optimized tag writer for shared strings <si> elements.
+ attr = ''
+
+ for key, value in attributes:
+ value = self._escape_attributes(value)
+ attr += ' %s="%s"' % (key, value)
+
+ string = self._escape_data(string)
+
+ self.fh.write("""<si><t%s>%s</t></si>""" % (attr, string))
+
+ def _xml_rich_si_element(self, string):
+ # Optimized tag writer for shared strings <si> rich string elements.
+
+ self.fh.write("""<si>%s</si>""" % string)
+
+ def _xml_number_element(self, number, attributes=[]):
+ # Optimized tag writer for <c> cell number elements in the inner loop.
+ attr = ''
+
+ for key, value in attributes:
+ value = self._escape_attributes(value)
+ attr += ' %s="%s"' % (key, value)
+
+ self.fh.write("""<c%s><v>%.16g</v></c>""" % (attr, number))
+
+ def _xml_formula_element(self, formula, result, attributes=[]):
+ # Optimized tag writer for <c> cell formula elements in the inner loop.
+ attr = ''
+
+ for key, value in attributes:
+ value = self._escape_attributes(value)
+ attr += ' %s="%s"' % (key, value)
+
+ self.fh.write("""<c%s><f>%s</f><v>%s</v></c>"""
+ % (attr, self._escape_data(formula),
+ self._escape_data(result)))
+
+ def _xml_inline_string(self, string, preserve, attributes=[]):
+ # Optimized tag writer for inlineStr cell elements in the inner loop.
+ attr = ''
+ t_attr = ''
+
+ # Set the <t> attribute to preserve whitespace.
+ if preserve:
+ t_attr = ' xml:space="preserve"'
+
+ for key, value in attributes:
+ value = self._escape_attributes(value)
+ attr += ' %s="%s"' % (key, value)
+
+ string = self._escape_data(string)
+
+ self.fh.write("""<c%s t="inlineStr"><is><t%s>%s</t></is></c>""" %
+ (attr, t_attr, string))
+
+ def _xml_rich_inline_string(self, string, attributes=[]):
+ # Optimized tag writer for rich inlineStr in the inner loop.
+ attr = ''
+
+ for key, value in attributes:
+ value = self._escape_attributes(value)
+ attr += ' %s="%s"' % (key, value)
+
+ self.fh.write("""<c%s t="inlineStr"><is>%s</is></c>""" %
+ (attr, string))
+
+ def _escape_attributes(self, attribute):
+ # Escape XML characters in attributes.
+ try:
+ if not self.escapes.search(attribute):
+ return attribute
+ except TypeError:
+ return attribute
+
+ attribute = attribute.replace('&', '&')
+ attribute = attribute.replace('"', '"')
+ attribute = attribute.replace('<', '<')
+ attribute = attribute.replace('>', '>')
+ attribute = attribute.replace('\n', '
')
+
+ return attribute
+
+ def _escape_data(self, data):
+ # Escape XML characters in data sections of tags. Note, this
+ # is different from _escape_attributes() in that double quotes
+ # are not escaped by Excel.
+ try:
+ if not self.escapes.search(data):
+ return data
+ except TypeError:
+ return data
+
+ data = data.replace('&', '&')
+ data = data.replace('<', '<')
+ data = data.replace('>', '>')
+
+ return data
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/python-xlsxwriter.git
More information about the debian-med-commit
mailing list