[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="&#xa0;" />
+<glyph unicode="&#x2000;" horiz-adv-x="652" />
+<glyph unicode="&#x2001;" horiz-adv-x="1304" />
+<glyph unicode="&#x2002;" horiz-adv-x="652" />
+<glyph unicode="&#x2003;" horiz-adv-x="1304" />
+<glyph unicode="&#x2004;" horiz-adv-x="434" />
+<glyph unicode="&#x2005;" horiz-adv-x="326" />
+<glyph unicode="&#x2006;" horiz-adv-x="217" />
+<glyph unicode="&#x2007;" horiz-adv-x="217" />
+<glyph unicode="&#x2008;" horiz-adv-x="163" />
+<glyph unicode="&#x2009;" horiz-adv-x="260" />
+<glyph unicode="&#x200a;" horiz-adv-x="72" />
+<glyph unicode="&#x202f;" horiz-adv-x="260" />
+<glyph unicode="&#x205f;" horiz-adv-x="326" />
+<glyph unicode="&#x20ac;" 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="&#x2212;" d="M1100 700h-900v-300h900v300z" />
+<glyph unicode="&#x2601;" 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="&#x2709;" d="M1200 1100h-1200l600 -603zM300 600l-300 -300v600zM1200 900v-600l-300 300zM800 500l400 -400h-1200l400 400l200 -200z" />
+<glyph unicode="&#x270f;" 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="&#xe000;" horiz-adv-x="500" d="M0 0z" />
+<glyph unicode="&#xe001;" d="M700 100h300v-100h-800v100h300v550l-500 550h1200l-500 -550v-550z" />
+<glyph unicode="&#xe002;" 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="&#xe003;" 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="&#xe005;" 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="&#xe006;" d="M791 522l145 -449l-384 275l-382 -275l146 447l-388 280h479l146 400h2l146 -400h472zM168 71l2 1z" />
+<glyph unicode="&#xe007;" 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="&#xe008;" 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="&#xe009;" 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="&#xe010;" 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="&#xe011;" 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="&#xe012;" 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="&#xe013;" d="M448 34l818 820l-212 212l-607 -607l-206 207l-212 -212z" />
+<glyph unicode="&#xe014;" d="M882 106l-282 282l-282 -282l-212 212l282 282l-282 282l212 212l282 -282l282 282l212 -212l-282 -282l282 -282z" />
+<glyph unicode="&#xe015;" 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="&#xe016;" 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="&#xe017;" 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="&#xe018;" d="M1200 1h-200v1200h200v-1200zM900 1h-200v800h200v-800zM600 1h-200v500h200v-500zM300 301h-200v-300h200v300z" />
+<glyph unicode="&#xe019;" 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="&#xe020;" 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="&#xe021;" d="M1301 601h-200v-600h-300v400h-300v-400h-300v600h-200l656 644z" />
+<glyph unicode="&#xe022;" 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="&#xe023;" 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="&#xe024;" d="M721 400h-242l-40 -400h-539l431 1200h209l-21 -300h162l-20 300h208l431 -1200h-538zM712 500l-27 300h-170l-27 -300h224z" />
+<glyph unicode="&#xe025;" d="M1100 400v-400h-1100v400h490l-290 300h200v500h300v-500h200l-290 -300h490zM988 300h-175v-100h175v100z" />
+<glyph unicode="&#xe026;" 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="&#xe027;" 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="&#xe028;" 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="&#xe029;" 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="&#xe030;" 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="&#xe031;" 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="&#xe032;" 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="&#xe034;" 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="&#xe035;" 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="&#xe036;" 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="&#xe037;" 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="&#xe038;" 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="&#xe039;" 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="&#xe040;" 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="&#xe041;" 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="&#xe042;" 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="&#xe043;" d="M1200 1200v-1000l-100 -100v1000h-750l-100 -100h750v-1000h-900v1025l175 175h925z" />
+<glyph unicode="&#xe045;" 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="&#xe047;" 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="&#xe048;" 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="&#xe049;" 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="&#xe050;" 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="&#xe051;" 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="&#xe052;" 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="&#xe053;" 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="&#xe054;" 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="&#xe055;" 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="&#xe056;" 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="&#xe057;" 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="&#xe058;" 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="&#xe059;" 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="&#xe060;" 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="&#xe062;" 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="&#xe063;" 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="&#xe064;" 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="&#xe065;" 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="&#xe066;" 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="&#xe067;" 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="&#xe068;" d="M900 303v198h-200v-200h195l-295 -300l-300 300h200v200h-200v-198l-300 300l300 296v-198h200v200h-200l300 300l295 -300h-195v-200h200v198l300 -296z" />
+<glyph unicode="&#xe069;" 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="&#xe070;" 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="&#xe071;" d="M1200 0l-500 488v-488l-564 550l564 550v-487l500 487v-1100z" />
+<glyph unicode="&#xe072;" d="M1100 550l-900 550v-1100z" />
+<glyph unicode="&#xe073;" 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="&#xe074;" 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="&#xe075;" d="M500 0v488l-500 -488v1100l500 -487v487l564 -550z" />
+<glyph unicode="&#xe076;" 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="&#xe077;" 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="&#xe078;" 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="&#xe079;" d="M777 7l240 240l-353 353l353 353l-240 240l-592 -594z" />
+<glyph unicode="&#xe080;" d="M513 -46l-241 240l353 353l-353 353l241 240l572 -571l21 -22l-1 -1v-1z" />
+<glyph unicode="&#xe081;" 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="&#xe082;" 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="&#xe083;" 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="&#xe084;" 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="&#xe085;" 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="&#xe086;" 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="&#xe087;" 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="&#xe088;" 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="&#xe089;" 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="&#xe090;" 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="&#xe091;" d="M1200 400h-600v-301l-600 448l600 453v-300h600v-300z" />
+<glyph unicode="&#xe092;" d="M600 400h-600v300h600v300l600 -453l-600 -448v301z" />
+<glyph unicode="&#xe093;" d="M1098 600h-298v-600h-300v600h-296l450 600z" />
+<glyph unicode="&#xe094;" d="M998 600l-449 -600l-445 600h296v600h300v-600h298z" />
+<glyph unicode="&#xe095;" 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="&#xe096;" d="M1200 1200h-400l129 -129l-294 -294l142 -142l294 294l129 -129v400zM565 423l-294 -294l129 -129h-400v400l129 -129l294 294z" />
+<glyph unicode="&#xe097;" d="M871 730l129 -130h-400v400l129 -129l295 295l142 -141zM200 600h400v-400l-129 130l-295 -295l-142 141l295 295z" />
+<glyph unicode="&#xe101;" 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="&#xe102;" 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="&#xe103;" 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="&#xe105;" 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="&#xe106;" 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="&#xe107;" 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="&#xe108;" 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="&#xe110;" 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="&#xe111;" 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="&#xe112;" 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="&#xe113;" d="M902 184l226 227l-578 579l-580 -579l227 -227l352 353z" />
+<glyph unicode="&#xe114;" d="M650 218l578 579l-226 227l-353 -353l-352 353l-227 -227z" />
+<glyph unicode="&#xe115;" d="M1198 400v600h-796l215 -200h381v-400h-198l299 -283l299 283h-200zM-198 700l299 283l300 -283h-203v-400h385l215 -200h-800v600h-196z" />
+<glyph unicode="&#xe116;" 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="&#xe117;" 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="&#xe118;" 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="&#xe119;" d="M302 300h198v600h-198l298 300l298 -300h-198v-600h198l-298 -300z" />
+<glyph unicode="&#xe120;" d="M900 303v197h-600v-197l-300 297l300 298v-198h600v198l300 -298z" />
+<glyph unicode="&#xe121;" 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="&#xe122;" 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="&#xe124;" 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="&#xe125;" 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="&#xe126;" 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="&#xe127;" 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="&#xe128;" 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="&#xe129;" 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="&#xe130;" 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="&#xe131;" 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="&#xe132;" 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="&#xe133;" 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="&#xe134;" 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="&#xe135;" 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="&#xe137;" 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="&#xe138;" d="M1100 1200v-100h-1000v100h1000zM150 1000h900l-350 -500v-300l-200 -200v500z" />
+<glyph unicode="&#xe140;" 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="&#xe141;" 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="&#xe143;" 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="&#xe144;" 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="&#xe145;" 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="&#xe148;" 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="&#xe149;" 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="&#xe150;" d="M300 0l298 300h-198v900h-200v-900h-198zM900 1200l298 -300h-198v-900h-200v900h-198z" />
+<glyph unicode="&#xe151;" 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="&#xe152;" 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="&#xe153;" d="M300 0l298 300h-198v900h-200v-900h-198zM900 1100h-100v100h200v-500h-100v400zM1100 500v-500h-100v100h-200v400h300zM1001 400h-100v-200h100v200z" />
+<glyph unicode="&#xe154;" d="M300 0l298 300h-198v900h-200v-900h-198zM1100 1200v-500h-100v100h-200v400h300zM1001 1100h-100v-200h100v200zM900 400h-100v100h200v-500h-100v400z" />
+<glyph unicode="&#xe155;" d="M300 0l298 300h-198v900h-200v-900h-198zM900 1000h-200v200h200v-200zM1000 700h-300v200h300v-200zM1100 400h-400v200h400v-200zM1200 100h-500v200h500v-200z" />
+<glyph unicode="&#xe156;" d="M300 0l298 300h-198v900h-200v-900h-198zM1200 1000h-500v200h500v-200zM1100 700h-400v200h400v-200zM1000 400h-300v200h300v-200zM900 100h-200v200h200v-200z" />
+<glyph unicode="&#xe157;" 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="&#xe158;" 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="&#xe159;" 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="&#xe160;" 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="&#xe161;" 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="&#xe162;" 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="&#xe163;" 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="&#xe164;" 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="&#xe165;" 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="&#xe166;" 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="&#xe167;" 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="&#xe168;" 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="&#xe169;" 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="&#xe170;" 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="&#xe171;" d="M1200 1199v-1079l-475 272l-310 -393v416h-392zM1166 1148l-672 -712v-226z" />
+<glyph unicode="&#xe172;" 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="&#xe173;" 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="&#xe174;" 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="&#xe175;" 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="&#xe176;" 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="&#xe177;" 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="&#xe178;" 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="&#xe179;" 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="&#xe180;" 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="&#xe181;" 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="&#xe182;" 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="&#xe183;" 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="&#xe184;" d="M175 200h950l-125 150v250l100 100v400h-100v-200h-100v200h-200v-200h-100v200h-200v-200h-100v200h-100v-400l100 -100v-250zM1200 100v-100h-1100v100h1100z" />
+<glyph unicode="&#xe185;" 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="&#xe186;" 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="&#xe187;" 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="&#xe188;" 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="&#xe189;" 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="&#xe190;" 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="&#xe191;" 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="&#xe192;" 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="&#xe193;" 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="&#xe194;" 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="&#xe195;" 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="&#xe197;" 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="&#xe198;" 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="&#xe199;" d="M700 200h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170l-270 -300h400v-155l-75 -45h350l-75 45v155z" />
+<glyph unicode="&#xe200;" 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="&#x1f4bc;" 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="&#x1f4c5;" 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="&#x1f4cc;" 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="&#x1f4ce;" 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="&#x1f4f7;" 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="&#x1f512;" 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="&#x1f514;" 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="&#x1f516;" d="M200 0l450 444l450 -443v1150q0 20 -14.5 35t-35.5 15h-800q-21 0 -35.5 -15t-14.5 -35v-1151z" />
+<glyph unicode="&#x1f525;" 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="&#x1f527;" 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', '&#xA;')
+
+        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