write_xlsx 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. data/.document +5 -0
  2. data/.gitattributes +1 -0
  3. data/Gemfile +12 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +82 -0
  6. data/Rakefile +78 -0
  7. data/VERSION +1 -0
  8. data/examples/a_simple.rb +45 -0
  9. data/examples/array_formula.rb +33 -0
  10. data/examples/autofilter.rb +235 -0
  11. data/examples/chart_area.rb +59 -0
  12. data/examples/chart_bar.rb +59 -0
  13. data/examples/chart_column.rb +58 -0
  14. data/examples/chart_line.rb +59 -0
  15. data/examples/chart_pie.rb +49 -0
  16. data/examples/chart_scatter.rb +59 -0
  17. data/examples/chart_stock.rb +65 -0
  18. data/examples/colors.rb +130 -0
  19. data/examples/comments1.rb +12 -0
  20. data/examples/comments2.rb +335 -0
  21. data/examples/conditional_format.rb +67 -0
  22. data/examples/data_validate.rb +279 -0
  23. data/examples/defined_name.rb +28 -0
  24. data/examples/demo.rb +104 -0
  25. data/examples/diag_border.rb +26 -0
  26. data/examples/headers.rb +119 -0
  27. data/examples/hide_sheet.rb +30 -0
  28. data/examples/hyperlink1.rb +58 -0
  29. data/examples/indent.rb +28 -0
  30. data/examples/merge1.rb +38 -0
  31. data/examples/merge2.rb +48 -0
  32. data/examples/merge3.rb +43 -0
  33. data/examples/merge4.rb +82 -0
  34. data/examples/merge5.rb +70 -0
  35. data/examples/merge6.rb +48 -0
  36. data/examples/outline.rb +252 -0
  37. data/examples/properties.rb +33 -0
  38. data/examples/protection.rb +34 -0
  39. data/examples/rich_strings.rb +42 -0
  40. data/examples/right_to_left.rb +24 -0
  41. data/examples/tab_colors.rb +26 -0
  42. data/lib/write_xlsx.rb +77 -0
  43. data/lib/write_xlsx/chart.rb +3027 -0
  44. data/lib/write_xlsx/chart/area.rb +52 -0
  45. data/lib/write_xlsx/chart/bar.rb +126 -0
  46. data/lib/write_xlsx/chart/column.rb +132 -0
  47. data/lib/write_xlsx/chart/line.rb +51 -0
  48. data/lib/write_xlsx/chart/pie.rb +210 -0
  49. data/lib/write_xlsx/chart/scatter.rb +252 -0
  50. data/lib/write_xlsx/chart/stock.rb +134 -0
  51. data/lib/write_xlsx/chartsheet.rb +173 -0
  52. data/lib/write_xlsx/colors.rb +65 -0
  53. data/lib/write_xlsx/compatibility.rb +71 -0
  54. data/lib/write_xlsx/drawing.rb +547 -0
  55. data/lib/write_xlsx/format.rb +683 -0
  56. data/lib/write_xlsx/package/app.rb +218 -0
  57. data/lib/write_xlsx/package/comments.rb +221 -0
  58. data/lib/write_xlsx/package/content_types.rb +189 -0
  59. data/lib/write_xlsx/package/core.rb +196 -0
  60. data/lib/write_xlsx/package/packager.rb +510 -0
  61. data/lib/write_xlsx/package/relationships.rb +98 -0
  62. data/lib/write_xlsx/package/shared_strings.rb +96 -0
  63. data/lib/write_xlsx/package/styles.rb +705 -0
  64. data/lib/write_xlsx/package/theme.rb +45 -0
  65. data/lib/write_xlsx/package/vml.rb +386 -0
  66. data/lib/write_xlsx/package/xml_writer_simple.rb +90 -0
  67. data/lib/write_xlsx/utility.rb +113 -0
  68. data/lib/write_xlsx/workbook.rb +1488 -0
  69. data/lib/write_xlsx/worksheet.rb +6578 -0
  70. data/lib/write_xlsx/zip_file_utils.rb +98 -0
  71. data/test/chart/test_add_series.rb +113 -0
  72. data/test/chart/test_process_names.rb +27 -0
  73. data/test/chart/test_write_auto.rb +15 -0
  74. data/test/chart/test_write_ax_id.rb +15 -0
  75. data/test/chart/test_write_ax_pos.rb +15 -0
  76. data/test/chart/test_write_chart_space.rb +15 -0
  77. data/test/chart/test_write_cross_ax.rb +15 -0
  78. data/test/chart/test_write_crosses.rb +15 -0
  79. data/test/chart/test_write_format_code.rb +15 -0
  80. data/test/chart/test_write_idx.rb +15 -0
  81. data/test/chart/test_write_label_align.rb +15 -0
  82. data/test/chart/test_write_label_offset.rb +15 -0
  83. data/test/chart/test_write_lang.rb +15 -0
  84. data/test/chart/test_write_layout.rb +15 -0
  85. data/test/chart/test_write_legend.rb +16 -0
  86. data/test/chart/test_write_legend_pos.rb +15 -0
  87. data/test/chart/test_write_major_gridlines.rb +15 -0
  88. data/test/chart/test_write_marker.rb +17 -0
  89. data/test/chart/test_write_marker_size.rb +15 -0
  90. data/test/chart/test_write_marker_value.rb +16 -0
  91. data/test/chart/test_write_num_cache.rb +16 -0
  92. data/test/chart/test_write_num_fmt.rb +16 -0
  93. data/test/chart/test_write_number_format.rb +15 -0
  94. data/test/chart/test_write_order.rb +15 -0
  95. data/test/chart/test_write_orientation.rb +15 -0
  96. data/test/chart/test_write_page_margins.rb +15 -0
  97. data/test/chart/test_write_page_setup.rb +15 -0
  98. data/test/chart/test_write_plot_vis_only.rb +15 -0
  99. data/test/chart/test_write_pt.rb +16 -0
  100. data/test/chart/test_write_pt_count.rb +16 -0
  101. data/test/chart/test_write_series_formula.rb +16 -0
  102. data/test/chart/test_write_style.rb +41 -0
  103. data/test/chart/test_write_symbol.rb +16 -0
  104. data/test/chart/test_write_tick_lbl_pos.rb +16 -0
  105. data/test/chart/test_write_v.rb +16 -0
  106. data/test/drawing/test_drawing_chart_01.rb +50 -0
  107. data/test/drawing/test_drawing_image_01.rb +59 -0
  108. data/test/helper.rb +90 -0
  109. data/test/package/app/test_app01.rb +44 -0
  110. data/test/package/app/test_app02.rb +46 -0
  111. data/test/package/app/test_app03.rb +53 -0
  112. data/test/package/comments/test_comments01.rb +36 -0
  113. data/test/package/comments/test_write_text_t.rb +44 -0
  114. data/test/package/content_types/test_content_types.rb +35 -0
  115. data/test/package/content_types/test_write_default.rb +13 -0
  116. data/test/package/content_types/test_write_override.rb +13 -0
  117. data/test/package/core/test_core01.rb +28 -0
  118. data/test/package/core/test_core02.rb +42 -0
  119. data/test/package/relationships/test_relationships.rb +28 -0
  120. data/test/package/relationships/test_sheet_rels.rb +22 -0
  121. data/test/package/shared_strings/test_shared_strings01.rb +30 -0
  122. data/test/package/shared_strings/test_shared_strings02.rb +30 -0
  123. data/test/package/shared_strings/test_write_si.rb +13 -0
  124. data/test/package/shared_strings/test_write_sst.rb +15 -0
  125. data/test/package/styles/test_styles_01.rb +69 -0
  126. data/test/package/styles/test_styles_02.rb +104 -0
  127. data/test/package/styles/test_styles_03.rb +90 -0
  128. data/test/package/styles/test_styles_04.rb +216 -0
  129. data/test/package/styles/test_styles_05.rb +150 -0
  130. data/test/package/styles/test_styles_06.rb +104 -0
  131. data/test/package/styles/test_styles_07.rb +104 -0
  132. data/test/package/styles/test_styles_08.rb +109 -0
  133. data/test/package/styles/test_styles_09.rb +95 -0
  134. data/test/package/vml/test_vml_01.rb +42 -0
  135. data/test/package/vml/test_write_anchor.rb +14 -0
  136. data/test/package/vml/test_write_auto_fill.rb +14 -0
  137. data/test/package/vml/test_write_column.rb +14 -0
  138. data/test/package/vml/test_write_div.rb +14 -0
  139. data/test/package/vml/test_write_fill.rb +14 -0
  140. data/test/package/vml/test_write_idmap.rb +14 -0
  141. data/test/package/vml/test_write_move_with_cells.rb +14 -0
  142. data/test/package/vml/test_write_path.rb +22 -0
  143. data/test/package/vml/test_write_row.rb +14 -0
  144. data/test/package/vml/test_write_shadow.rb +14 -0
  145. data/test/package/vml/test_write_shapelayout.rb +14 -0
  146. data/test/package/vml/test_write_shapetype.rb +14 -0
  147. data/test/package/vml/test_write_size_with_cells.rb +14 -0
  148. data/test/package/vml/test_write_stroke.rb +14 -0
  149. data/test/package/vml/test_write_textbox.rb +14 -0
  150. data/test/perl_output/a_simple.xlsx +0 -0
  151. data/test/perl_output/array_formula.xlsx +0 -0
  152. data/test/perl_output/autofilter.xlsx +0 -0
  153. data/test/perl_output/chart_area.xlsx +0 -0
  154. data/test/perl_output/chart_bar.xlsx +0 -0
  155. data/test/perl_output/chart_column.xlsx +0 -0
  156. data/test/perl_output/chart_line.xlsx +0 -0
  157. data/test/perl_output/chart_pie.xlsx +0 -0
  158. data/test/perl_output/chart_scatter.xlsx +0 -0
  159. data/test/perl_output/chart_stock.xlsx +0 -0
  160. data/test/perl_output/comments1.xlsx +0 -0
  161. data/test/perl_output/comments2.xlsx +0 -0
  162. data/test/perl_output/conditional_format.xlsx +0 -0
  163. data/test/perl_output/data_validate.xlsx +0 -0
  164. data/test/perl_output/defined_name.xlsx +0 -0
  165. data/test/perl_output/demo.xlsx +0 -0
  166. data/test/perl_output/diag_border.xlsx +0 -0
  167. data/test/perl_output/fit_to_pages.xlsx +0 -0
  168. data/test/perl_output/headers.xlsx +0 -0
  169. data/test/perl_output/hide_sheet.xlsx +0 -0
  170. data/test/perl_output/hyperlink.xlsx +0 -0
  171. data/test/perl_output/indent.xlsx +0 -0
  172. data/test/perl_output/merge1.xlsx +0 -0
  173. data/test/perl_output/merge2.xlsx +0 -0
  174. data/test/perl_output/merge3.xlsx +0 -0
  175. data/test/perl_output/merge4.xlsx +0 -0
  176. data/test/perl_output/merge5.xlsx +0 -0
  177. data/test/perl_output/merge6.xlsx +0 -0
  178. data/test/perl_output/outline.xlsx +0 -0
  179. data/test/perl_output/print_scale.xlsx +0 -0
  180. data/test/perl_output/properties.xlsx +0 -0
  181. data/test/perl_output/protection.xlsx +0 -0
  182. data/test/perl_output/rich_strings.xlsx +0 -0
  183. data/test/perl_output/right_to_left.xlsx +0 -0
  184. data/test/perl_output/tab_colors.xlsx +0 -0
  185. data/test/test_delete_files.rb +37 -0
  186. data/test/test_example_match.rb +2281 -0
  187. data/test/test_xml_writer_simple.rb +63 -0
  188. data/test/workbook/test_get_chart_range.rb +59 -0
  189. data/test/workbook/test_sort_defined_names.rb +77 -0
  190. data/test/workbook/test_workbook_01.rb +29 -0
  191. data/test/workbook/test_workbook_02.rb +31 -0
  192. data/test/workbook/test_workbook_03.rb +31 -0
  193. data/test/workbook/test_workbook_new.rb +18 -0
  194. data/test/workbook/test_write_defined_name.rb +17 -0
  195. data/test/workbook/test_write_defined_names.rb +41 -0
  196. data/test/worksheet/test_calculate_spans.rb +58 -0
  197. data/test/worksheet/test_convert_date_time_01.rb +439 -0
  198. data/test/worksheet/test_convert_date_time_02.rb +478 -0
  199. data/test/worksheet/test_convert_date_time_03.rb +435 -0
  200. data/test/worksheet/test_extract_filter_tokens.rb +109 -0
  201. data/test/worksheet/test_parse_filter_expression.rb +143 -0
  202. data/test/worksheet/test_position_object.rb +50 -0
  203. data/test/worksheet/test_repeat_formula.rb +55 -0
  204. data/test/worksheet/test_worksheet_01.rb +32 -0
  205. data/test/worksheet/test_worksheet_02.rb +38 -0
  206. data/test/worksheet/test_worksheet_03.rb +44 -0
  207. data/test/worksheet/test_worksheet_04.rb +45 -0
  208. data/test/worksheet/test_write_array_formula_01.rb +99 -0
  209. data/test/worksheet/test_write_autofilter.rb +260 -0
  210. data/test/worksheet/test_write_brk.rb +18 -0
  211. data/test/worksheet/test_write_cell.rb +49 -0
  212. data/test/worksheet/test_write_cell_value.rb +33 -0
  213. data/test/worksheet/test_write_col_breaks.rb +27 -0
  214. data/test/worksheet/test_write_col_info.rb +95 -0
  215. data/test/worksheet/test_write_conditional_formatting.rb +72 -0
  216. data/test/worksheet/test_write_custom_filter.rb +18 -0
  217. data/test/worksheet/test_write_custom_filters.rb +25 -0
  218. data/test/worksheet/test_write_data_validation_01.rb +113 -0
  219. data/test/worksheet/test_write_data_validation_02.rb +528 -0
  220. data/test/worksheet/test_write_dimension.rb +94 -0
  221. data/test/worksheet/test_write_ext.rb +18 -0
  222. data/test/worksheet/test_write_ext_lst.rb +18 -0
  223. data/test/worksheet/test_write_filter.rb +18 -0
  224. data/test/worksheet/test_write_filter_column.rb +18 -0
  225. data/test/worksheet/test_write_filters.rb +32 -0
  226. data/test/worksheet/test_write_header_footer.rb +53 -0
  227. data/test/worksheet/test_write_hyperlink.rb +39 -0
  228. data/test/worksheet/test_write_hyperlinks.rb +27 -0
  229. data/test/worksheet/test_write_legacy_drawing.rb +19 -0
  230. data/test/worksheet/test_write_merge_cell.rb +18 -0
  231. data/test/worksheet/test_write_merge_cells.rb +192 -0
  232. data/test/worksheet/test_write_methods.rb +353 -0
  233. data/test/worksheet/test_write_mx_plv.rb +19 -0
  234. data/test/worksheet/test_write_page_margins.rb +98 -0
  235. data/test/worksheet/test_write_page_set_up_pr.rb +19 -0
  236. data/test/worksheet/test_write_page_setup.rb +54 -0
  237. data/test/worksheet/test_write_pane.rb +123 -0
  238. data/test/worksheet/test_write_phonetic_pr.rb +19 -0
  239. data/test/worksheet/test_write_print_options.rb +77 -0
  240. data/test/worksheet/test_write_row_breaks.rb +27 -0
  241. data/test/worksheet/test_write_row_element.rb +69 -0
  242. data/test/worksheet/test_write_selection.rb +18 -0
  243. data/test/worksheet/test_write_sheet_calc_pr.rb +18 -0
  244. data/test/worksheet/test_write_sheet_data.rb +18 -0
  245. data/test/worksheet/test_write_sheet_format_pr.rb +18 -0
  246. data/test/worksheet/test_write_sheet_pr.rb +36 -0
  247. data/test/worksheet/test_write_sheet_protection.rb +174 -0
  248. data/test/worksheet/test_write_sheet_view.rb +62 -0
  249. data/test/worksheet/test_write_sheet_view1.rb +64 -0
  250. data/test/worksheet/test_write_sheet_view2.rb +56 -0
  251. data/test/worksheet/test_write_sheet_view3.rb +83 -0
  252. data/test/worksheet/test_write_sheet_view4.rb +83 -0
  253. data/test/worksheet/test_write_sheet_view5.rb +74 -0
  254. data/test/worksheet/test_write_sheet_view6.rb +51 -0
  255. data/test/worksheet/test_write_sheet_view7.rb +71 -0
  256. data/test/worksheet/test_write_sheet_view8.rb +51 -0
  257. data/test/worksheet/test_write_sheet_view9.rb +51 -0
  258. data/test/worksheet/test_write_tab_color.rb +23 -0
  259. data/test/worksheet/test_write_worksheet.rb +19 -0
  260. data/write_xlsx.gemspec +308 -0
  261. metadata +363 -0
@@ -0,0 +1,98 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'write_xlsx/package/xml_writer_simple'
3
+ require 'write_xlsx/utility'
4
+
5
+ module Writexlsx
6
+ module Package
7
+ class Relationships
8
+
9
+ include Writexlsx::Utility
10
+
11
+ Schema_root = 'http://schemas.openxmlformats.org'
12
+ Package_schema = Schema_root + '/package/2006/relationships'
13
+ Document_schema = Schema_root + '/officeDocument/2006/relationships'
14
+
15
+ def initialize
16
+ @writer = Package::XMLWriterSimple.new
17
+ @rels = []
18
+ @id = 1
19
+ end
20
+
21
+ def set_xml_writer(filename)
22
+ @writer.set_xml_writer(filename)
23
+ end
24
+
25
+ def assemble_xml_file
26
+ write_xml_declaration
27
+ write_relationships
28
+ @writer.crlf
29
+ @writer.close
30
+ end
31
+
32
+ #
33
+ # Add container relationship to XLSX .rels xml files.
34
+ #
35
+ def add_document_relationship(type, target)
36
+ type = Document_schema + type
37
+ target = target
38
+
39
+ @rels.push([type, target])
40
+ end
41
+
42
+ #
43
+ # Add container relationship to XLSX .rels xml files.
44
+ #
45
+ def add_package_relationship(type, target)
46
+ type = Package_schema + type
47
+ target = target + '.xml'
48
+
49
+ @rels.push([type, target])
50
+ end
51
+
52
+ #
53
+ # Add worksheet relationship to sheet.rels xml files.
54
+ #
55
+ def add_worksheet_relationship(type, target, target_mode)
56
+ type = Document_schema + type
57
+ target = target
58
+
59
+ @rels.push([type, target, target_mode])
60
+ end
61
+
62
+ private
63
+
64
+ def write_xml_declaration
65
+ @writer.xml_decl
66
+ end
67
+
68
+ #
69
+ # Write the <Relationships> element.
70
+ #
71
+ def write_relationships
72
+ attributes = ['xmlns', Package_schema]
73
+
74
+ @writer.start_tag('Relationships', attributes)
75
+
76
+ @rels.each { |rel| write_relationship(*rel) }
77
+
78
+ @writer.end_tag('Relationships')
79
+ end
80
+
81
+ #
82
+ # Write the <Relationship> element.
83
+ #
84
+ def write_relationship(type, target, target_mode = nil)
85
+ attributes = [
86
+ 'Id', "rId#{@id}",
87
+ 'Type', type,
88
+ 'Target', target
89
+ ]
90
+ @id += 1
91
+
92
+ attributes << 'TargetMode' << target_mode if target_mode
93
+
94
+ @writer.empty_tag('Relationship', attributes)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,96 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'write_xlsx/package/xml_writer_simple'
3
+ require 'write_xlsx/utility'
4
+
5
+ module Writexlsx
6
+ module Package
7
+ class SharedStrings
8
+
9
+ include Writexlsx::Utility
10
+
11
+ attr_writer :string_count, :unique_count
12
+
13
+ def initialize
14
+ @writer = Package::XMLWriterSimple.new
15
+ @strings = []
16
+ @string_count = 0
17
+ @unique_count = 0
18
+ end
19
+
20
+ def set_xml_writer(filename)
21
+ @writer.set_xml_writer(filename)
22
+ end
23
+
24
+ def assemble_xml_file
25
+ write_xml_declaration
26
+
27
+ # Write the sst table.
28
+ write_sst
29
+
30
+ # Write the sst strings.
31
+ write_sst_strings
32
+
33
+ # Close the sst tag.
34
+ @writer.end_tag('sst')
35
+ @writer.crlf
36
+ @writer.close
37
+ end
38
+
39
+ #
40
+ # Add the array ref of strings to be written.
41
+ #
42
+ def add_strings(strings)
43
+ @strings = strings
44
+ end
45
+
46
+ private
47
+
48
+ def write_xml_declaration
49
+ @writer.xml_decl
50
+ end
51
+
52
+ #
53
+ # Write the <sst> element.
54
+ #
55
+ def write_sst
56
+ schema = 'http://schemas.openxmlformats.org'
57
+
58
+ attributes =
59
+ [
60
+ 'xmlns', schema + '/spreadsheetml/2006/main',
61
+ 'count', @string_count,
62
+ 'uniqueCount', @unique_count
63
+ ]
64
+
65
+ @writer.start_tag('sst', attributes)
66
+ end
67
+
68
+ #
69
+ # Write the sst string elements.
70
+ #
71
+ def write_sst_strings
72
+ @strings.each { |string| write_si(string) }
73
+ end
74
+
75
+ #
76
+ # Write the <si> element.
77
+ #
78
+ def write_si(string)
79
+ attributes = []
80
+
81
+ attributes << 'xml:space' << 'preserve' if string =~ /^[ \t]/ || string =~ /[ \t]$/
82
+
83
+ @writer.start_tag('si')
84
+
85
+ # Write any rich strings without further tags.
86
+ if string =~ %r{^<r>} && string =~ %r{</r>$}
87
+ @writer.io_write(string)
88
+ else
89
+ @writer.data_element('t', string, attributes)
90
+ end
91
+
92
+ @writer.end_tag('si')
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,705 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'write_xlsx/package/xml_writer_simple'
3
+ require 'write_xlsx/utility'
4
+
5
+ module Writexlsx
6
+ module Package
7
+ class Styles
8
+
9
+ include Writexlsx::Utility
10
+
11
+ def initialize
12
+ @writer = Package::XMLWriterSimple.new
13
+ @xf_formats = nil
14
+ @palette = []
15
+ @font_count = 0
16
+ @num_format_count = 0
17
+ @border_count = 0
18
+ @fill_count = 0
19
+ @custom_colors = []
20
+ @dxf_formats = []
21
+ end
22
+
23
+ def set_xml_writer(filename)
24
+ @writer.set_xml_writer(filename)
25
+ end
26
+
27
+ def assemble_xml_file
28
+ write_xml_declaration
29
+ write_style_sheet
30
+ write_num_fmts
31
+ write_fonts
32
+ write_fills
33
+ write_borders
34
+ write_cell_style_xfs
35
+ write_cell_xfs
36
+ write_cell_styles
37
+ write_dxfs
38
+ write_table_styles
39
+ write_colors
40
+ @writer.end_tag('styleSheet')
41
+ @writer.crlf
42
+ @writer.close
43
+ end
44
+
45
+ #
46
+ # Pass in the Format objects and other properties used to set the styles.
47
+ #
48
+ def set_style_properties(xf_formats, palette, font_count, num_format_count, border_count, fill_count, custom_colors, dxf_formats)
49
+ @xf_formats = xf_formats
50
+ @palette = palette
51
+ @font_count = font_count
52
+ @num_format_count = num_format_count
53
+ @border_count = border_count
54
+ @fill_count = fill_count
55
+ @custom_colors = custom_colors
56
+ @dxf_formats = dxf_formats
57
+ end
58
+
59
+ #
60
+ # Convert from an Excel internal colour index to a XML style #RRGGBB index
61
+ # based on the default or user defined values in the Workbook palette.
62
+ #
63
+ def get_palette_color(index)
64
+ palette = @palette
65
+
66
+ # Handle colours in #XXXXXX RGB format.
67
+ return "FF#{$1.upcase}" if index =~ /^#([0-9A-F]{6})$/i
68
+
69
+ # Adjust the colour index.
70
+ index -= 8
71
+
72
+ # Palette is passed in from the Workbook class.
73
+ rgb = @palette[index]
74
+
75
+ sprintf("FF%02X%02X%02X", *rgb[0, 3])
76
+ end
77
+
78
+ #
79
+ # Write the <styleSheet> element.
80
+ #
81
+ def write_style_sheet
82
+ xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
83
+
84
+ attributes = ['xmlns', xmlns]
85
+
86
+ @writer.start_tag('styleSheet', attributes)
87
+ end
88
+
89
+ #
90
+ # Write the <numFmts> element.
91
+ #
92
+ def write_num_fmts
93
+ count = @num_format_count
94
+
95
+ return if count == 0
96
+
97
+ attributes = ['count', count]
98
+
99
+ @writer.start_tag('numFmts', attributes)
100
+
101
+ # Write the numFmts elements.
102
+ @xf_formats.each do |format|
103
+ # Ignore built-in number formats, i.e., < 164.
104
+ next unless format.num_format_index >= 164
105
+ write_num_fmt(format.num_format_index, format.num_format)
106
+ end
107
+
108
+ @writer.end_tag('numFmts')
109
+ end
110
+
111
+ #
112
+ # Write the <numFmt> element.
113
+ #
114
+ def write_num_fmt(num_fmt_id, format_code)
115
+ format_codes = {
116
+ 0 => 'General',
117
+ 1 => '0',
118
+ 2 => '0.00',
119
+ 3 => '#,##0',
120
+ 4 => '#,##0.00',
121
+ 5 => '($#,##0_);($#,##0)',
122
+ 6 => '($#,##0_);[Red]($#,##0)',
123
+ 7 => '($#,##0.00_);($#,##0.00)',
124
+ 8 => '($#,##0.00_);[Red]($#,##0.00)',
125
+ 9 => '0%',
126
+ 10 => '0.00%',
127
+ 11 => '0.00E+00',
128
+ 12 => '# ?/?',
129
+ 13 => '# ??/??',
130
+ 14 => 'm/d/yy',
131
+ 15 => 'd-mmm-yy',
132
+ 16 => 'd-mmm',
133
+ 17 => 'mmm-yy',
134
+ 18 => 'h:mm AM/PM',
135
+ 19 => 'h:mm:ss AM/PM',
136
+ 20 => 'h:mm',
137
+ 21 => 'h:mm:ss',
138
+ 22 => 'm/d/yy h:mm',
139
+ 37 => '(#,##0_);(#,##0)',
140
+ 38 => '(#,##0_);[Red](#,##0)',
141
+ 39 => '(#,##0.00_);(#,##0.00)',
142
+ 40 => '(#,##0.00_);[Red](#,##0.00)',
143
+ 41 => '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',
144
+ 42 => '_($* #,##0_);_($* (#,##0);_($* "-"_);_(@_)',
145
+ 43 => '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',
146
+ 44 => '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)',
147
+ 45 => 'mm:ss',
148
+ 46 => '[h]:mm:ss',
149
+ 47 => 'mm:ss.0',
150
+ 48 => '##0.0E+0',
151
+ 49 => '@'
152
+ }
153
+
154
+ # Set the format code for built-in number formats.
155
+ if num_fmt_id < 164
156
+ if format_codes[num_fmt_id]
157
+ format_code = format_codes[num_fmt_id]
158
+ else
159
+ format_code = 'General'
160
+ end
161
+ end
162
+
163
+ attributes = [
164
+ 'numFmtId', num_fmt_id,
165
+ 'formatCode', format_code
166
+ ]
167
+
168
+ @writer.empty_tag('numFmt', attributes)
169
+ end
170
+
171
+ #
172
+ # Write the <fonts> element.
173
+ #
174
+ def write_fonts
175
+ count = @font_count
176
+
177
+ attributes = ['count', count]
178
+
179
+ @writer.start_tag('fonts', attributes)
180
+
181
+ # Write the font elements for format objects that have them.
182
+ @xf_formats.each { |format| write_font(format) unless format.has_font == 0 }
183
+
184
+ @writer.end_tag('fonts')
185
+ end
186
+
187
+ #
188
+ # Write the <font> element.
189
+ #
190
+ def write_font(format, dxf_format = nil)
191
+ @writer.start_tag('font')
192
+
193
+ # The condense and extend elements are mainly used in dxf formats.
194
+ write_condense unless format.font_condense == 0
195
+ write_extend unless format.font_extend == 0
196
+
197
+ @writer.empty_tag('b') if format.bold?
198
+ @writer.empty_tag('i') if format.italic?
199
+ @writer.empty_tag('strike') if format.strikeout?
200
+ @writer.empty_tag('outline') if format.outline?
201
+ @writer.empty_tag('shadow') if format.shadow?
202
+
203
+ # Handle the underline variants.
204
+ write_underline( format.underline ) if format.underline?
205
+
206
+ write_vert_align('superscript') if format.font_script == 1
207
+ write_vert_align('subscript') if format.font_script == 2
208
+
209
+ @writer.empty_tag('sz', ['val', format.size]) if !dxf_format
210
+
211
+ theme = format.theme
212
+ if theme != 0
213
+ write_color('theme', theme)
214
+ elsif format.color_indexed != 0
215
+ write_color('indexed', format.color_indexed)
216
+ elsif format.color != 0
217
+ color = get_palette_color(format.color)
218
+ write_color('rgb', color)
219
+ elsif !dxf_format
220
+ write_color('theme', 1)
221
+ end
222
+
223
+ if !dxf_format
224
+ @writer.empty_tag('name', ['val', format.font])
225
+ @writer.empty_tag('family', ['val', format.font_family])
226
+
227
+ if format.font == 'Calibri' && format.hyperlink == 0
228
+ @writer.empty_tag('scheme', ['val', format.font_scheme])
229
+ end
230
+ end
231
+
232
+ @writer.end_tag('font')
233
+ end
234
+
235
+ #
236
+ # _write_underline()
237
+ #
238
+ # Write the underline font element.
239
+ #
240
+ def write_underline(underline)
241
+ # Handle the underline variants.
242
+ if underline == 2
243
+ attributes = [val, 'double']
244
+ elsif underline == 33
245
+ attributes = [val, 'singleAccounting']
246
+ elsif underline == 34
247
+ attributes = [val, 'doubleAccounting']
248
+ else
249
+ attributes = [] # Default to single underline.
250
+ end
251
+
252
+ @writer.empty_tag('u', attributes)
253
+ end
254
+
255
+ #
256
+ # Write the <color> element.
257
+ #
258
+ def write_color(name, value)
259
+ attributes = [name, value]
260
+
261
+ @writer.empty_tag('color', attributes)
262
+ end
263
+
264
+ #
265
+ # Write the <fills> element.
266
+ #
267
+ def write_fills
268
+ count = @fill_count
269
+
270
+ attributes = ['count', count]
271
+
272
+ @writer.start_tag('fills', attributes)
273
+
274
+ # Write the default fill element.
275
+ write_default_fill('none')
276
+ write_default_fill('gray125')
277
+
278
+ # Write the fill elements for format objects that have them.
279
+ @xf_formats.each do |format|
280
+ write_fill(format) unless format.has_fill == 0
281
+ end
282
+
283
+ @writer.end_tag( 'fills' )
284
+ end
285
+
286
+ #
287
+ # Write the <fill> element for the default fills.
288
+ #
289
+ def write_default_fill(pattern_type)
290
+ @writer.start_tag('fill')
291
+ @writer.empty_tag('patternFill', ['patternType', pattern_type])
292
+ @writer.end_tag('fill')
293
+ end
294
+
295
+ #
296
+ # Write the <fill> element.
297
+ #
298
+ def write_fill(format, dxf_format = nil)
299
+ pattern = format.pattern
300
+ bg_color = format.bg_color
301
+ fg_color = format.fg_color
302
+
303
+ patterns = %w(
304
+ none
305
+ solid
306
+ mediumGray
307
+ darkGray
308
+ lightGray
309
+ darkHorizontal
310
+ darkVertical
311
+ darkDown
312
+ darkUp
313
+ darkGrid
314
+ darkTrellis
315
+ lightHorizontal
316
+ lightVertical
317
+ lightDown
318
+ lightUp
319
+ lightGrid
320
+ lightTrellis
321
+ gray125
322
+ gray0625
323
+ )
324
+
325
+ @writer.start_tag('fill' )
326
+
327
+ # The "none" pattern is handled differently for dxf formats.
328
+ if dxf_format && format.pattern <= 1
329
+ @writer.start_tag('patternFill')
330
+ else
331
+ @writer.start_tag('patternFill', ['patternType', patterns[format.pattern]])
332
+ end
333
+
334
+ unless fg_color == 0
335
+ fg_color = get_palette_color(fg_color)
336
+ @writer.empty_tag('fgColor', ['rgb', fg_color])
337
+ end
338
+
339
+ if bg_color != 0
340
+ bg_color = get_palette_color(bg_color)
341
+ @writer.empty_tag('bgColor', ['rgb', bg_color])
342
+ else
343
+ @writer.empty_tag('bgColor', ['indexed', 64]) if !dxf_format
344
+ end
345
+
346
+ @writer.end_tag('patternFill')
347
+ @writer.end_tag('fill')
348
+ end
349
+
350
+ #
351
+ # Write the <borders> element.
352
+ #
353
+ def write_borders
354
+ count = @border_count
355
+
356
+ attributes = ['count', count]
357
+
358
+ @writer.start_tag('borders', attributes)
359
+
360
+ # Write the border elements for format objects that have them.
361
+ @xf_formats.each do |format|
362
+ write_border(format) unless format.has_border == 0
363
+ end
364
+
365
+ @writer.end_tag('borders')
366
+ end
367
+
368
+ #
369
+ # Write the <border> element.
370
+ #
371
+ def write_border(format, dxf_format = nil)
372
+ attributes = []
373
+
374
+ # Diagonal borders add attributes to the <border> element.
375
+ if format.diag_type == 1
376
+ attributes << 'diagonalUp' << 1
377
+ elsif format.diag_type == 2
378
+ attributes << 'diagonalDown' << 1
379
+ elsif format.diag_type == 3
380
+ attributes << 'diagonalUp' << 1
381
+ attributes << 'diagonalDown' << 1
382
+ end
383
+
384
+ # Ensure that a default diag border is set if the diag type is set.
385
+ format.diag_border = 1 if format.diag_type != 0 && format.diag_border == 0
386
+
387
+ # Write the start border tag.
388
+ @writer.start_tag('border', attributes)
389
+
390
+ # Write the <border> sub elements.
391
+ write_sub_border('left', format.left, format.left_color)
392
+ write_sub_border('right', format.right, format.right_color)
393
+ write_sub_border('top', format.top, format.top_color)
394
+ write_sub_border('bottom', format.bottom, format.bottom_color)
395
+
396
+ # Condition DXF formats don't allow diagonal borders
397
+ if !dxf_format
398
+ write_sub_border('diagonal', format.diag_border, format.diag_color)
399
+ end
400
+
401
+ if dxf_format
402
+ write_sub_border('vertical')
403
+ write_sub_border('horizontal')
404
+ end
405
+
406
+ @writer.end_tag('border')
407
+ end
408
+
409
+ #
410
+ # Write the <border> sub elements such as <right>, <top>, etc.
411
+ #
412
+ def write_sub_border(type, style = 0, color = nil)
413
+ if style == 0
414
+ @writer.empty_tag(type)
415
+ return
416
+ end
417
+
418
+ border_styles = %w(
419
+ none
420
+ thin
421
+ medium
422
+ dashed
423
+ dotted
424
+ thick
425
+ double
426
+ hair
427
+ mediumDashed
428
+ dashDot
429
+ mediumDashDot
430
+ dashDotDot
431
+ mediumDashDotDot
432
+ slantDashDot
433
+ )
434
+
435
+ attributes = [:style, border_styles[style]]
436
+
437
+ @writer.start_tag(type, attributes)
438
+
439
+ if color != 0
440
+ color = get_palette_color(color)
441
+ @writer.empty_tag('color', ['rgb', color])
442
+ else
443
+ @writer.empty_tag('color', ['auto', 1])
444
+ end
445
+
446
+ @writer.end_tag(type)
447
+ end
448
+
449
+ #
450
+ # Write the <cellStyleXfs> element.
451
+ #
452
+ def write_cell_style_xfs
453
+ count = 1
454
+
455
+ attributes = ['count', count]
456
+
457
+ @writer.start_tag('cellStyleXfs', attributes)
458
+
459
+ # Write the style_xf element.
460
+ write_style_xf
461
+
462
+ @writer.end_tag('cellStyleXfs')
463
+ end
464
+
465
+ #
466
+ # Write the <cellXfs> element.
467
+ #
468
+ def write_cell_xfs
469
+ formats = @xf_formats
470
+
471
+ # Workaround for when the last format is used for the comment font
472
+ # and shouldn't be used for cellXfs.
473
+ last_format = formats[-1]
474
+
475
+ formats.pop if last_format && last_format.font_only != 0
476
+
477
+ attributes = ['count', formats.size]
478
+
479
+ @writer.start_tag('cellXfs', attributes)
480
+
481
+ # Write the xf elements.
482
+ formats.each { |format| write_xf(format) }
483
+
484
+ @writer.end_tag('cellXfs')
485
+ end
486
+
487
+ #
488
+ # Write the style <xf> element.
489
+ #
490
+ def write_style_xf
491
+ num_fmt_id = 0
492
+ font_id = 0
493
+ fill_id = 0
494
+ border_id = 0
495
+
496
+ attributes = [
497
+ 'numFmtId', num_fmt_id,
498
+ 'fontId', font_id,
499
+ 'fillId', fill_id,
500
+ 'borderId', border_id
501
+ ]
502
+
503
+ @writer.empty_tag('xf', attributes)
504
+ end
505
+
506
+ private
507
+
508
+ #
509
+ # Write the <xf> element.
510
+ #
511
+ def write_xf(format)
512
+ has_align = false
513
+ has_protect = false
514
+
515
+ attributes = [
516
+ 'numFmtId', format.num_format_index,
517
+ 'fontId' , format.font_index,
518
+ 'fillId' , format.fill_index,
519
+ 'borderId', format.border_index,
520
+ 'xfId' , 0
521
+ ]
522
+
523
+ attributes << 'applyNumberFormat' << 1 if format.num_format_index > 0
524
+
525
+ # Add applyFont attribute if XF format uses a font element.
526
+ attributes << 'applyFont' << 1 if format.has_font != 0 && format.font_index > 0
527
+
528
+ # Add applyFill attribute if XF format uses a fill element.
529
+ attributes << 'applyFill' << 1 if format.fill_index > 0
530
+
531
+ # Add applyBorder attribute if XF format uses a border element.
532
+ attributes << 'applyBorder' << 1 if format.border_index > 0
533
+
534
+ # Check if XF format has alignment properties set.
535
+ apply_align, align = format.get_align_properties
536
+
537
+ # Check if an alignment sub-element should be written.
538
+ has_align = true if apply_align && !align.empty?
539
+
540
+ # We can also have applyAlignment without a sub-element.
541
+ attributes << 'applyAlignment' << 1 if apply_align
542
+
543
+ # Check for cell protection properties.
544
+ protection = format.get_protection_properties
545
+
546
+ if protection
547
+ attributes << 'applyProtection' << 1
548
+ has_protect = true
549
+ end
550
+
551
+ # Write XF with sub-elements if required.
552
+ if has_align || has_protect
553
+ @writer.start_tag('xf', attributes)
554
+ @writer.empty_tag('alignment', align) if has_align
555
+ @writer.empty_tag('protection', protection) if has_protect
556
+ @writer.end_tag('xf')
557
+ else
558
+ @writer.empty_tag('xf', attributes)
559
+ end
560
+ end
561
+
562
+ #
563
+ # Write the <cellStyles> element.
564
+ #
565
+ def write_cell_styles
566
+ count = 1
567
+
568
+ attributes = ['count', count]
569
+
570
+ @writer.start_tag('cellStyles', attributes)
571
+
572
+ # Write the cellStyle element.
573
+ write_cell_style
574
+
575
+ @writer.end_tag('cellStyles')
576
+ end
577
+
578
+ #
579
+ # Write the <cellStyle> element.
580
+ #
581
+ def write_cell_style
582
+ name = 'Normal'
583
+ xf_id = 0
584
+ builtin_id = 0
585
+
586
+ attributes = [
587
+ 'name', name,
588
+ 'xfId', xf_id,
589
+ 'builtinId', builtin_id
590
+ ]
591
+
592
+ @writer.empty_tag('cellStyle', attributes)
593
+ end
594
+
595
+ #
596
+ # Write the <dxfs> element.
597
+ #
598
+ def write_dxfs
599
+ formats = @dxf_formats
600
+
601
+ count = formats.size
602
+
603
+ attributes = ['count', count]
604
+
605
+ if !formats.empty?
606
+ @writer.start_tag('dxfs', attributes)
607
+
608
+ # Write the font elements for format objects that have them.
609
+ @dxf_formats.each do |format|
610
+ @writer.start_tag('dxf')
611
+ write_font(format, 1) unless format.has_dxf_font == 0
612
+
613
+ if format.num_format_index != 0
614
+ write_num_fmt(format.num_format_index, format.num_format)
615
+ end
616
+
617
+ write_fill(format, 1) if format.has_dxf_fill != 0
618
+ write_border(format, 1) if format.has_dxf_border != 0
619
+ @writer.end_tag('dxf')
620
+ end
621
+
622
+ @writer.end_tag('dxfs')
623
+ else
624
+ @writer.empty_tag('dxfs', attributes)
625
+ end
626
+ end
627
+
628
+ #
629
+ # Write the <tableStyles> element.
630
+ #
631
+ def write_table_styles
632
+ count = 0
633
+ default_table_style = 'TableStyleMedium9'
634
+ default_pivot_style = 'PivotStyleLight16'
635
+
636
+ attributes = [
637
+ 'count', count,
638
+ 'defaultTableStyle', default_table_style,
639
+ 'defaultPivotStyle', default_pivot_style
640
+ ]
641
+
642
+ @writer.empty_tag('tableStyles', attributes)
643
+ end
644
+
645
+ #
646
+ # Write the <colors> element.
647
+ #
648
+ def write_colors
649
+ custom_colors = @custom_colors
650
+
651
+ return if @custom_colors.empty?
652
+
653
+ @writer.start_tag( 'colors' )
654
+ write_mru_colors(@custom_colors)
655
+ @writer.end_tag('colors')
656
+ end
657
+
658
+ #
659
+ # Write the <mruColors> element for the most recently used colours.
660
+ #
661
+ def write_mru_colors(*args)
662
+ custom_colors = args
663
+
664
+ # Limit the mruColors to the last 10.
665
+ count = custom_colors.size
666
+ custom_colors = custom_colors[-10, 10] if count > 10
667
+
668
+ @writer.start_tag('mruColors')
669
+
670
+ # Write the custom colors in reverse order.
671
+ @custom_colors.reverse.each do |color|
672
+ write_color('rgb', color)
673
+ end
674
+
675
+ @writer.end_tag('mruColors')
676
+ end
677
+
678
+ def write_xml_declaration
679
+ @writer.xml_decl
680
+ end
681
+
682
+ #
683
+ # Write the <condense> element.
684
+ #
685
+ def write_condense
686
+ val = 0
687
+
688
+ attributes = ['val', val]
689
+
690
+ @writer.empty_tag('condense', attributes)
691
+ end
692
+
693
+ #
694
+ # Write the <extend> element.
695
+ #
696
+ def write_extend
697
+ val = 0
698
+
699
+ attributes = ['val', val]
700
+
701
+ @writer.empty_tag('extend', attributes)
702
+ end
703
+ end
704
+ end
705
+ end