ricardoo27-writeexcel 0.6.12.1

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 (245) hide show
  1. data/.document +5 -0
  2. data/.gitattributes +1 -0
  3. data/README.rdoc +136 -0
  4. data/Rakefile +52 -0
  5. data/VERSION +1 -0
  6. data/charts/chartex.rb +316 -0
  7. data/charts/demo1.rb +46 -0
  8. data/charts/demo101.bin +0 -0
  9. data/charts/demo2.rb +65 -0
  10. data/charts/demo201.bin +0 -0
  11. data/charts/demo3.rb +117 -0
  12. data/charts/demo301.bin +0 -0
  13. data/charts/demo4.rb +119 -0
  14. data/charts/demo401.bin +0 -0
  15. data/charts/demo5.rb +48 -0
  16. data/charts/demo501.bin +0 -0
  17. data/examples/a_simple.rb +43 -0
  18. data/examples/autofilter.rb +265 -0
  19. data/examples/bigfile.rb +30 -0
  20. data/examples/chart_area.rb +121 -0
  21. data/examples/chart_bar.rb +120 -0
  22. data/examples/chart_column.rb +120 -0
  23. data/examples/chart_line.rb +120 -0
  24. data/examples/chart_pie.rb +108 -0
  25. data/examples/chart_scatter.rb +121 -0
  26. data/examples/chart_stock.rb +148 -0
  27. data/examples/chess.rb +142 -0
  28. data/examples/colors.rb +129 -0
  29. data/examples/comments1.rb +27 -0
  30. data/examples/comments2.rb +352 -0
  31. data/examples/copyformat.rb +52 -0
  32. data/examples/data_validate.rb +279 -0
  33. data/examples/date_time.rb +87 -0
  34. data/examples/defined_name.rb +32 -0
  35. data/examples/demo.rb +124 -0
  36. data/examples/diag_border.rb +36 -0
  37. data/examples/formats.rb +490 -0
  38. data/examples/formula_result.rb +30 -0
  39. data/examples/header.rb +137 -0
  40. data/examples/hide_sheet.rb +29 -0
  41. data/examples/hyperlink.rb +43 -0
  42. data/examples/images.rb +63 -0
  43. data/examples/indent.rb +31 -0
  44. data/examples/merge1.rb +40 -0
  45. data/examples/merge2.rb +45 -0
  46. data/examples/merge3.rb +66 -0
  47. data/examples/merge4.rb +83 -0
  48. data/examples/merge5.rb +80 -0
  49. data/examples/merge6.rb +67 -0
  50. data/examples/outline.rb +255 -0
  51. data/examples/outline_collapsed.rb +209 -0
  52. data/examples/panes.rb +113 -0
  53. data/examples/password_protection.rb +33 -0
  54. data/examples/properties.rb +34 -0
  55. data/examples/properties_jp.rb +33 -0
  56. data/examples/protection.rb +47 -0
  57. data/examples/regions.rb +53 -0
  58. data/examples/repeat.rb +43 -0
  59. data/examples/republic.png +0 -0
  60. data/examples/right_to_left.rb +27 -0
  61. data/examples/row_wrap.rb +53 -0
  62. data/examples/set_first_sheet.rb +14 -0
  63. data/examples/stats.rb +74 -0
  64. data/examples/stocks.rb +81 -0
  65. data/examples/store_formula.rb +15 -0
  66. data/examples/tab_colors.rb +31 -0
  67. data/examples/utf8.rb +15 -0
  68. data/examples/write_arrays.rb +83 -0
  69. data/html/en/doc_en.html +5946 -0
  70. data/html/images/a_simple.jpg +0 -0
  71. data/html/images/area1.jpg +0 -0
  72. data/html/images/bar1.jpg +0 -0
  73. data/html/images/chart_area.xls +0 -0
  74. data/html/images/column1.jpg +0 -0
  75. data/html/images/data_validation.jpg +0 -0
  76. data/html/images/line1.jpg +0 -0
  77. data/html/images/pie1.jpg +0 -0
  78. data/html/images/regions.jpg +0 -0
  79. data/html/images/scatter1.jpg +0 -0
  80. data/html/images/stats.jpg +0 -0
  81. data/html/images/stock1.jpg +0 -0
  82. data/html/images/stocks.jpg +0 -0
  83. data/html/index.html +16 -0
  84. data/html/style.css +433 -0
  85. data/lib/writeexcel.rb +1159 -0
  86. data/lib/writeexcel/biffwriter.rb +223 -0
  87. data/lib/writeexcel/caller_info.rb +12 -0
  88. data/lib/writeexcel/cell_range.rb +332 -0
  89. data/lib/writeexcel/chart.rb +1968 -0
  90. data/lib/writeexcel/charts/area.rb +154 -0
  91. data/lib/writeexcel/charts/bar.rb +177 -0
  92. data/lib/writeexcel/charts/column.rb +156 -0
  93. data/lib/writeexcel/charts/external.rb +66 -0
  94. data/lib/writeexcel/charts/line.rb +154 -0
  95. data/lib/writeexcel/charts/pie.rb +169 -0
  96. data/lib/writeexcel/charts/scatter.rb +192 -0
  97. data/lib/writeexcel/charts/stock.rb +213 -0
  98. data/lib/writeexcel/col_info.rb +87 -0
  99. data/lib/writeexcel/colors.rb +68 -0
  100. data/lib/writeexcel/comments.rb +460 -0
  101. data/lib/writeexcel/compatibility.rb +65 -0
  102. data/lib/writeexcel/convert_date_time.rb +117 -0
  103. data/lib/writeexcel/data_validations.rb +370 -0
  104. data/lib/writeexcel/debug_info.rb +41 -0
  105. data/lib/writeexcel/embedded_chart.rb +35 -0
  106. data/lib/writeexcel/excelformula.y +139 -0
  107. data/lib/writeexcel/excelformulaparser.rb +587 -0
  108. data/lib/writeexcel/format.rb +1575 -0
  109. data/lib/writeexcel/formula.rb +987 -0
  110. data/lib/writeexcel/helper.rb +78 -0
  111. data/lib/writeexcel/image.rb +218 -0
  112. data/lib/writeexcel/olewriter.rb +305 -0
  113. data/lib/writeexcel/outline.rb +24 -0
  114. data/lib/writeexcel/properties.rb +242 -0
  115. data/lib/writeexcel/shared_string_table.rb +153 -0
  116. data/lib/writeexcel/storage_lite.rb +984 -0
  117. data/lib/writeexcel/workbook.rb +2478 -0
  118. data/lib/writeexcel/worksheet.rb +6925 -0
  119. data/lib/writeexcel/worksheets.rb +25 -0
  120. data/lib/writeexcel/write_file.rb +63 -0
  121. data/test/excelfile/Chart1.xls +0 -0
  122. data/test/excelfile/Chart2.xls +0 -0
  123. data/test/excelfile/Chart3.xls +0 -0
  124. data/test/excelfile/Chart4.xls +0 -0
  125. data/test/excelfile/Chart5.xls +0 -0
  126. data/test/helper.rb +31 -0
  127. data/test/perl_output/Chart1.xls.data +0 -0
  128. data/test/perl_output/Chart2.xls.data +0 -0
  129. data/test/perl_output/Chart3.xls.data +0 -0
  130. data/test/perl_output/Chart4.xls.data +0 -0
  131. data/test/perl_output/Chart5.xls.data +0 -0
  132. data/test/perl_output/README +31 -0
  133. data/test/perl_output/a_simple.xls +0 -0
  134. data/test/perl_output/autofilter.xls +0 -0
  135. data/test/perl_output/biff_add_continue_testdata +0 -0
  136. data/test/perl_output/chart_area.xls +0 -0
  137. data/test/perl_output/chart_bar.xls +0 -0
  138. data/test/perl_output/chart_column.xls +0 -0
  139. data/test/perl_output/chart_line.xls +0 -0
  140. data/test/perl_output/chess.xls +0 -0
  141. data/test/perl_output/colors.xls +0 -0
  142. data/test/perl_output/comments0.xls +0 -0
  143. data/test/perl_output/comments1.xls +0 -0
  144. data/test/perl_output/comments2.xls +0 -0
  145. data/test/perl_output/data_validate.xls +0 -0
  146. data/test/perl_output/date_time.xls +0 -0
  147. data/test/perl_output/defined_name.xls +0 -0
  148. data/test/perl_output/demo.xls +0 -0
  149. data/test/perl_output/demo101.bin +0 -0
  150. data/test/perl_output/demo201.bin +0 -0
  151. data/test/perl_output/demo301.bin +0 -0
  152. data/test/perl_output/demo401.bin +0 -0
  153. data/test/perl_output/demo501.bin +0 -0
  154. data/test/perl_output/diag_border.xls +0 -0
  155. data/test/perl_output/f_font_biff +0 -0
  156. data/test/perl_output/f_font_key +1 -0
  157. data/test/perl_output/f_xf_biff +0 -0
  158. data/test/perl_output/file_font_biff +0 -0
  159. data/test/perl_output/file_font_key +1 -0
  160. data/test/perl_output/file_xf_biff +0 -0
  161. data/test/perl_output/formula_result.xls +0 -0
  162. data/test/perl_output/headers.xls +0 -0
  163. data/test/perl_output/hidden.xls +0 -0
  164. data/test/perl_output/hide_zero.xls +0 -0
  165. data/test/perl_output/hyperlink.xls +0 -0
  166. data/test/perl_output/images.xls +0 -0
  167. data/test/perl_output/indent.xls +0 -0
  168. data/test/perl_output/merge1.xls +0 -0
  169. data/test/perl_output/merge2.xls +0 -0
  170. data/test/perl_output/merge3.xls +0 -0
  171. data/test/perl_output/merge4.xls +0 -0
  172. data/test/perl_output/merge5.xls +0 -0
  173. data/test/perl_output/merge6.xls +0 -0
  174. data/test/perl_output/ole_write_header +0 -0
  175. data/test/perl_output/outline.xls +0 -0
  176. data/test/perl_output/outline_collapsed.xls +0 -0
  177. data/test/perl_output/panes.xls +0 -0
  178. data/test/perl_output/password_protection.xls +0 -0
  179. data/test/perl_output/protection.xls +0 -0
  180. data/test/perl_output/regions.xls +0 -0
  181. data/test/perl_output/right_to_left.xls +0 -0
  182. data/test/perl_output/set_first_sheet.xls +0 -0
  183. data/test/perl_output/stats.xls +0 -0
  184. data/test/perl_output/stocks.xls +0 -0
  185. data/test/perl_output/store_formula.xls +0 -0
  186. data/test/perl_output/tab_colors.xls +0 -0
  187. data/test/perl_output/unicode_cyrillic.xls +0 -0
  188. data/test/perl_output/utf8.xls +0 -0
  189. data/test/perl_output/workbook1.xls +0 -0
  190. data/test/perl_output/workbook2.xls +0 -0
  191. data/test/perl_output/ws_colinfo +1 -0
  192. data/test/perl_output/ws_store_colinfo +0 -0
  193. data/test/perl_output/ws_store_dimensions +0 -0
  194. data/test/perl_output/ws_store_filtermode +0 -0
  195. data/test/perl_output/ws_store_filtermode_off +0 -0
  196. data/test/perl_output/ws_store_filtermode_on +0 -0
  197. data/test/perl_output/ws_store_selection +0 -0
  198. data/test/perl_output/ws_store_window2 +1 -0
  199. data/test/republic.png +0 -0
  200. data/test/test_00_IEEE_double.rb +13 -0
  201. data/test/test_01_add_worksheet.rb +10 -0
  202. data/test/test_02_merge_formats.rb +49 -0
  203. data/test/test_04_dimensions.rb +388 -0
  204. data/test/test_05_rows.rb +175 -0
  205. data/test/test_06_extsst.rb +74 -0
  206. data/test/test_11_date_time.rb +475 -0
  207. data/test/test_12_date_only.rb +525 -0
  208. data/test/test_13_date_seconds.rb +477 -0
  209. data/test/test_21_escher.rb +624 -0
  210. data/test/test_22_mso_drawing_group.rb +741 -0
  211. data/test/test_23_note.rb +57 -0
  212. data/test/test_24_txo.rb +74 -0
  213. data/test/test_25_position_object.rb +80 -0
  214. data/test/test_26_autofilter.rb +309 -0
  215. data/test/test_27_autofilter.rb +126 -0
  216. data/test/test_28_autofilter.rb +156 -0
  217. data/test/test_29_process_jpg.rb +670 -0
  218. data/test/test_30_validation_dval.rb +74 -0
  219. data/test/test_31_validation_dv_strings.rb +123 -0
  220. data/test/test_32_validation_dv_formula.rb +203 -0
  221. data/test/test_40_property_types.rb +188 -0
  222. data/test/test_41_properties.rb +235 -0
  223. data/test/test_42_set_properties.rb +434 -0
  224. data/test/test_50_name_stored.rb +295 -0
  225. data/test/test_51_name_print_area.rb +353 -0
  226. data/test/test_52_name_print_titles.rb +450 -0
  227. data/test/test_53_autofilter.rb +199 -0
  228. data/test/test_60_chart_generic.rb +574 -0
  229. data/test/test_61_chart_subclasses.rb +84 -0
  230. data/test/test_62_chart_formats.rb +268 -0
  231. data/test/test_63_chart_area_formats.rb +645 -0
  232. data/test/test_biff.rb +71 -0
  233. data/test/test_big_workbook.rb +17 -0
  234. data/test/test_compatibility.rb +12 -0
  235. data/test/test_example_match.rb +3246 -0
  236. data/test/test_format.rb +1189 -0
  237. data/test/test_formula.rb +61 -0
  238. data/test/test_ole.rb +102 -0
  239. data/test/test_storage_lite.rb +116 -0
  240. data/test/test_workbook.rb +146 -0
  241. data/test/test_worksheet.rb +106 -0
  242. data/utils/add_magic_comment.rb +80 -0
  243. data/writeexcel.gemspec +278 -0
  244. data/writeexcel.rdoc +1425 -0
  245. metadata +292 -0
@@ -0,0 +1,213 @@
1
+ # -*- coding: utf-8 -*-
2
+ ###############################################################################
3
+ #
4
+ # Stock - A writer class for Excel Stock charts.
5
+ #
6
+ # Used in conjunction with WriteExcel::Chart.
7
+ #
8
+ # See formatting note in WriteExcel::Chart.
9
+ #
10
+ # Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
11
+ #
12
+ # original written in Perl by John McNamara
13
+ # converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp
14
+ #
15
+
16
+ require 'writeexcel'
17
+
18
+ module Writeexcel
19
+
20
+ class Chart
21
+
22
+ # ==SYNOPSIS
23
+ #
24
+ # To create a simple Excel file with a Stock chart using WriteExcel:
25
+ #
26
+ # #!/usr/bin/ruby -w
27
+ #
28
+ # require 'writeexcel'
29
+ #
30
+ # workbook = WriteExcel.new('chart.xls')
31
+ # worksheet = workbook.add_worksheet
32
+ #
33
+ # chart = workbook.add_chart(:type => 'Chart::Stock')
34
+ #
35
+ # # Add a series for each Open-High-Low-Close.
36
+ # chart.add_series(:categories => '=Sheet1!$A$2:$A$6', :values => '=Sheet1!$B$2:$B$6')
37
+ # chart.add_series(:categories => '=Sheet1!$A$2:$A$6', :values => '=Sheet1!$C$2:$C$6')
38
+ # chart.add_series(:categories => '=Sheet1!$A$2:$A$6', :values => '=Sheet1!$D$2:$D$6')
39
+ # chart.add_series(:categories => '=Sheet1!$A$2:$A$6', :values => '=Sheet1!$E$2:$E$6')
40
+ #
41
+ # # Add the worksheet data the chart refers to.
42
+ # # ... See the full example below.
43
+ #
44
+ # workbook.close
45
+ #
46
+ # ==DESCRIPTION
47
+ #
48
+ # This module implements Stock charts for WriteExcel. The chart object
49
+ # is created via the Workbook add_chart() method:
50
+ #
51
+ # chart = workbook.add_chart(:type => 'Chart::Stock')
52
+ #
53
+ # Once the object is created it can be configured via the following methods
54
+ # that are common to all chart classes:
55
+ #
56
+ # chart.add_series
57
+ # chart.set_x_axis
58
+ # chart.set_y_axis
59
+ # chart.set_title
60
+ #
61
+ # These methods are explained in detail in Chart section of WriteExcel.
62
+ # Class specific methods or settings, if any, are explained below.
63
+ #
64
+ # ==Stock Chart Methods
65
+ #
66
+ # There aren't currently any stock chart specific methods.
67
+ # See the TODO section of Chart section in WriteExcel.
68
+ #
69
+ # The default Stock chart is an Open-High-Low-Close chart.
70
+ # A series must be added for each of these data sources.
71
+ #
72
+ # The default Stock chart is in black and white. User defined colours
73
+ # will be added at a later stage.
74
+ #
75
+ # ==EXAMPLE
76
+ #
77
+ # Here is a complete example that demonstrates most of the available features
78
+ # when creating a Stock chart.
79
+ #
80
+ # #!/usr/bin/ruby -w
81
+ #
82
+ # require 'writeexcel'
83
+ #
84
+ # workbook = WriteExcel.new('chart_stock_ex.xls')
85
+ # worksheet = workbook.add_worksheet
86
+ # bold = workbook.add_format(:bold => 1)
87
+ # date_format = workbook.add_format(:num_format => 'dd/mm/yyyy')
88
+ #
89
+ # # Add the worksheet data that the charts will refer to.
90
+ # headings = [ 'Date', 'Open', 'High', 'Low', 'Close' ]
91
+ # data = [
92
+ # [ '2009-08-23', 110.75, 113.48, 109.05, 109.40 ],
93
+ # [ '2009-08-24', 111.24, 111.60, 103.57, 104.87 ],
94
+ # [ '2009-08-25', 104.96, 108.00, 103.88, 106.00 ],
95
+ # [ '2009-08-26', 104.95, 107.95, 104.66, 107.91 ],
96
+ # [ '2009-08-27', 108.10, 108.62, 105.69, 106.15 ]
97
+ # ]
98
+ #
99
+ # worksheet.write('A1', headings, bold)
100
+ #
101
+ # row = 1
102
+ # data.each do |d|
103
+ # worksheet.write(row, 0, d[0], date_format)
104
+ # worksheet.write(row, 1, d[1])
105
+ # worksheet.write(row, 2, d[2])
106
+ # worksheet.write(row, 3, d[3])
107
+ # worksheet.write(row, 4, d[4])
108
+ # row += 1
109
+ # end
110
+ #
111
+ # # Create a new chart object. In this case an embedded chart.
112
+ # chart = workbook.add_chart(:type => 'Chart::Stock', ::embedded => 1)
113
+ #
114
+ # # Add a series for each of the Open-High-Low-Close columns.
115
+ # chart.add_series(
116
+ # :categories => '=Sheet1!$A$2:$A$6',
117
+ # :values => '=Sheet1!$B$2:$B$6',
118
+ # :name => 'Open'
119
+ # )
120
+ #
121
+ # chart.add_series(
122
+ # :categories => '=Sheet1!$A$2:$A$6',
123
+ # :values => '=Sheet1!$C$2:$C$6',
124
+ # :name => 'High'
125
+ # )
126
+ #
127
+ # chart.add_series(
128
+ # :categories => '=Sheet1!$A$2:$A$6',
129
+ # :values => '=Sheet1!$D$2:$D$6',
130
+ # :name => 'Low'
131
+ # )
132
+ #
133
+ # chart.add_series(
134
+ # :categories => '=Sheet1!$A$2:$A$6',
135
+ # :values => '=Sheet1!$E$2:$E$6',
136
+ # :name => 'Close'
137
+ # )
138
+ #
139
+ # # Add a chart title and some axis labels.
140
+ # chart.set_title(:name => 'Open-High-Low-Close')
141
+ # chart.set_x_axis(:name => 'Date')
142
+ # chart.set_y_axis(:name => 'Share price')
143
+ #
144
+ # # Insert the chart into the worksheet (with an offset).
145
+ # worksheet.insert_chart('F2', chart, 25, 10)
146
+ #
147
+ # workbook.close
148
+ #
149
+ class Stock < Chart
150
+ ###############################################################################
151
+ #
152
+ # new()
153
+ #
154
+ #
155
+ def initialize(*args) # :nodoc:
156
+ super
157
+ end
158
+
159
+ ###############################################################################
160
+ #
161
+ # _store_chart_type()
162
+ #
163
+ # Implementation of the abstract method from the specific chart class.
164
+ #
165
+ # Write the LINE chart BIFF record. A stock chart uses the same LINE record
166
+ # as a line chart but with additional DROPBAR and CHARTLINE records to define
167
+ # the stock style.
168
+ #
169
+ def store_chart_type # :nodoc:
170
+ record = 0x1018 # Record identifier.
171
+ length = 0x0002 # Number of bytes to follow.
172
+ grbit = 0x0000 # Option flags.
173
+
174
+ store_simple(record, length, grbit)
175
+ end
176
+
177
+ ###############################################################################
178
+ #
179
+ # _store_marker_dataformat_stream(). Overridden.
180
+ #
181
+ # This is an implementation of the parent abstract method to define
182
+ # properties of markers, linetypes, pie formats and other.
183
+ #
184
+ def store_marker_dataformat_stream # :nodoc:
185
+ store_dropbar
186
+ store_begin
187
+ store_lineformat(0x00000000, 0x0000, 0xFFFF, 0x0001, 0x004F)
188
+ store_areaformat(0x00FFFFFF, 0x0000, 0x01, 0x01, 0x09, 0x08)
189
+ store_end
190
+
191
+ store_dropbar
192
+ store_begin
193
+ store_lineformat(0x00000000, 0x0000, 0xFFFF, 0x0001, 0x004F)
194
+ store_areaformat(0x0000, 0x00FFFFFF, 0x01, 0x01, 0x08, 0x09)
195
+ store_end
196
+
197
+ store_chartline
198
+ store_lineformat(0x00000000, 0x0000, 0xFFFF, 0x0000, 0x004F)
199
+
200
+
201
+ store_dataformat(0x0000, 0xFFFD, 0x0000)
202
+ store_begin
203
+ store_3dbarshape
204
+ store_lineformat(0x00000000, 0x0005, 0xFFFF, 0x0000, 0x004F)
205
+ store_areaformat(0x00000000, 0x0000, 0x00, 0x01, 0x4D, 0x4D)
206
+ store_pieformat
207
+ store_markerformat(0x00, 0x00, 0x00, 0x00, 0x4D, 0x4D, 0x3C)
208
+ store_end
209
+ end
210
+ end
211
+ end # class Chart
212
+
213
+ end # module Writeexcel
@@ -0,0 +1,87 @@
1
+ module Writeexcel
2
+
3
+ class Worksheet < BIFFWriter
4
+ require 'writeexcel/helper'
5
+
6
+ class ColInfo
7
+ attr_reader :level
8
+
9
+ #
10
+ # new(firstcol, lastcol, width, [format, hidden, level, collapsed])
11
+ #
12
+ # firstcol : First formatted column
13
+ # lastcol : Last formatted column
14
+ # width : Col width in user units, 8.43 is default
15
+ # format : format object
16
+ # hidden : hidden flag
17
+ # level : outline level
18
+ # collapsed : ?
19
+ #
20
+ def initialize(*args)
21
+ @firstcol, @lastcol, @width, @format, @hidden, @level, @collapsed = args
22
+ @width ||= 8.43 # default width
23
+ @level ||= 0 # default level
24
+ end
25
+
26
+ # Write BIFF record COLINFO to define column widths
27
+ #
28
+ # Note: The SDK says the record length is 0x0B but Excel writes a 0x0C
29
+ # length record.
30
+ #
31
+ def biff_record
32
+ record = 0x007D # Record identifier
33
+ length = 0x000B # Number of bytes to follow
34
+
35
+ coldx = (pixels * 256 / 7).to_i # Col width in internal units
36
+ reserved = 0x00 # Reserved
37
+
38
+ header = [record, length].pack("vv")
39
+ data = [@firstcol, @lastcol, coldx,
40
+ ixfe, grbit, reserved].pack("vvvvvC")
41
+ [header, data]
42
+ end
43
+
44
+ # Excel rounds the column width to the nearest pixel. Therefore we first
45
+ # convert to pixels and then to the internal units. The pixel to users-units
46
+ # relationship is different for values less than 1.
47
+ #
48
+ def pixels
49
+ if @width < 1
50
+ result = @width * 12
51
+ else
52
+ result = @width * 7 + 5
53
+ end
54
+ result.to_i
55
+ end
56
+
57
+ def ixfe
58
+ if @format && @format.respond_to?(:xf_index)
59
+ ixfe = @format.xf_index
60
+ else
61
+ ixfe = 0x0F
62
+ end
63
+ end
64
+
65
+ # Set the limits for the outline levels (0 <= x <= 7).
66
+ def level
67
+ if @level < 0
68
+ 0
69
+ elsif 7 < @level
70
+ 7
71
+ else
72
+ @level
73
+ end
74
+ end
75
+
76
+ # Set the options flags. (See set_row() for more details).
77
+ def grbit
78
+ grbit = 0x0000 # Option flags
79
+ grbit |= 0x0001 if @hidden && @hidden != 0
80
+ grbit |= level << 8
81
+ grbit |= 0x1000 if @collapsed && @collapsed != 0
82
+ grbit
83
+ end
84
+ end
85
+ end
86
+
87
+ end
@@ -0,0 +1,68 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Writeexcel
4
+
5
+ class Colors
6
+ COLORS = {
7
+ :aqua => 0x0F,
8
+ :cyan => 0x0F,
9
+ :black => 0x08,
10
+ :blue => 0x0C,
11
+ :brown => 0x10,
12
+ :magenta => 0x0E,
13
+ :fuchsia => 0x0E,
14
+ :gray => 0x17,
15
+ :grey => 0x17,
16
+ :green => 0x11,
17
+ :lime => 0x0B,
18
+ :navy => 0x12,
19
+ :orange => 0x35,
20
+ :pink => 0x21,
21
+ :purple => 0x14,
22
+ :red => 0x0A,
23
+ :silver => 0x16,
24
+ :white => 0x09,
25
+ :yellow => 0x0D,
26
+ } # :nodoc:
27
+
28
+ ###############################################################################
29
+ #
30
+ # get_color(colour)
31
+ #
32
+ # Used in conjunction with the set_xxx_color methods to convert a color
33
+ # string into a number. Color range is 0..63 but we will restrict it
34
+ # to 8..63 to comply with Gnumeric. Colors 0..7 are repeated in 8..15.
35
+ #
36
+ def get_color(color = nil) # :nodoc:
37
+ if color.respond_to?(:to_int) && color.respond_to?(:+)
38
+ # the default color if arg is outside range,
39
+ if color < 0 || 63 < color
40
+ 0x7FFF
41
+ # or an index < 8 mapped into the correct range,
42
+ elsif color < 8
43
+ (color + 8).to_i
44
+ # or an integer in the valid range
45
+ else
46
+ color.to_i
47
+ end
48
+ elsif color.respond_to?(:to_sym)
49
+ color = color.downcase.to_sym if color.respond_to?(:to_str)
50
+ # or the color string converted to an integer,
51
+ if COLORS.has_key?(color)
52
+ COLORS[color]
53
+ # or the default color if string is unrecognised,
54
+ else
55
+ 0x7FFF
56
+ end
57
+ else
58
+ 0x7FFF
59
+ end
60
+ end
61
+
62
+ def inspect
63
+ to_s
64
+ end
65
+ end # class Colors
66
+
67
+ end # module Writeexcel
68
+
@@ -0,0 +1,460 @@
1
+ module Writeexcel
2
+
3
+ class Worksheet < BIFFWriter
4
+ require 'writeexcel/helper'
5
+
6
+ class Collection
7
+ def initialize
8
+ @items = {}
9
+ end
10
+
11
+ def <<(item)
12
+ if @items[item.row]
13
+ @items[item.row][item.col] = item
14
+ else
15
+ @items[item.row] = { item.col => item }
16
+ end
17
+ end
18
+
19
+ def array
20
+ return @array if @array
21
+
22
+ @array = []
23
+ @items.keys.sort.each do |row|
24
+ @items[row].keys.sort.each do |col|
25
+ @array << @items[row][col]
26
+ end
27
+ end
28
+ @array
29
+ end
30
+
31
+ end
32
+
33
+ class Comments < Collection
34
+ attr_writer :visible
35
+
36
+ def initialize
37
+ super
38
+ @visible = false
39
+ end
40
+
41
+ def visible?
42
+ @visible
43
+ end
44
+ end
45
+
46
+ class Comment
47
+ attr_reader :row, :col, :string, :encoding, :author, :author_encoding, :visible, :color, :vertices
48
+
49
+ def initialize(worksheet, row, col, string, options = {})
50
+ @worksheet = worksheet
51
+ @row, @col = row, col
52
+ @params = params_with(options)
53
+ @string, @params[:encoding] = string_and_encoding(string, @params[:encoding], 'comment')
54
+
55
+ # Limit the string to the max number of chars (not bytes).
56
+ max_len = 32767
57
+ max_len = max_len * 2 if @params[:encoding] != 0
58
+
59
+ if @string.bytesize > max_len
60
+ @string = @string[0 .. max_len]
61
+ end
62
+ @encoding = @params[:encoding]
63
+ @author = @params[:author]
64
+ @author_encoding = @params[:author_encoding]
65
+ @visible = @params[:visible]
66
+ @color = @params[:color]
67
+ @vertices = calc_vertices
68
+ end
69
+
70
+ def store_comment_record(i, num_objects, num_comments, spid)
71
+ str_len = string.bytesize
72
+ str_len = str_len / 2 if encoding != 0 # Num of chars not bytes.
73
+
74
+ spid = store_comment_mso_drawing_record(i, num_objects, num_comments, spid, visible, color, vertices)
75
+ store_obj_comment(num_objects + i + 1)
76
+ store_mso_drawing_text_box
77
+ store_txo(str_len)
78
+ store_txo_continue_1(string, encoding)
79
+ formats = [[0, 9], [str_len, 0]]
80
+ store_txo_continue_2(formats)
81
+ spid
82
+ end
83
+
84
+ #
85
+ # Write the worksheet NOTE record that is part of cell comments.
86
+ #
87
+ def store_note_record(obj_id) #:nodoc:
88
+ comment_author = author
89
+ comment_author_enc = author_encoding
90
+ ruby_19 { comment_author = [comment_author].pack('a*') if comment_author.ascii_only? }
91
+ record = 0x001C # Record identifier
92
+ length = 0x000C # Bytes to follow
93
+
94
+ comment_author = '' unless comment_author
95
+ comment_author_enc = 0 unless author_encoding
96
+
97
+ # Use the visible flag if set by the user or else use the worksheet value.
98
+ # The flag is also set in store_mso_opt_comment() but with the opposite
99
+ # value.
100
+ if visible
101
+ comment_visible = visible != 0 ? 0x0002 : 0x0000
102
+ else
103
+ comment_visible = @worksheet.comments_visible? ? 0x0002 : 0x0000
104
+ end
105
+
106
+ # Get the number of chars in the author string (not bytes).
107
+ num_chars = comment_author.bytesize
108
+ num_chars = num_chars / 2 if comment_author_enc != 0 && comment_author_enc
109
+
110
+ # Null terminate the author string.
111
+ comment_author =
112
+ ruby_18 { comment_author + "\0" } ||
113
+ ruby_19 { comment_author.force_encoding('BINARY') + "\0".force_encoding('BINARY') }
114
+
115
+ # Pack the record.
116
+ data = [@row, @col, comment_visible, obj_id, num_chars, comment_author_enc].pack("vvvvvC")
117
+
118
+ length = data.bytesize + comment_author.bytesize
119
+ header = [record, length].pack("vv")
120
+
121
+ append(header, data, comment_author)
122
+ end
123
+
124
+ #
125
+ # Write the Escher Opt record that is part of MSODRAWING.
126
+ #
127
+ def store_mso_opt_comment(spid, visible = nil, colour = 0x50) #:nodoc:
128
+ type = 0xF00B
129
+ version = 3
130
+ instance = 9
131
+ data = ''
132
+ length = 54
133
+
134
+ # Use the visible flag if set by the user or else use the worksheet value.
135
+ # Note that the value used is the opposite of Comment#note_record.
136
+ #
137
+ if visible
138
+ visible = visible != 0 ? 0x0000 : 0x0002
139
+ else
140
+ visible = @worksheet.comments_visible? ? 0x0000 : 0x0002
141
+ end
142
+
143
+ data = [spid].pack('V') +
144
+ ['0000BF00080008005801000000008101'].pack("H*") +
145
+ [colour].pack("C") +
146
+ ['000008830150000008BF011000110001'+'02000000003F0203000300BF03'].pack("H*") +
147
+ [visible].pack('v') +
148
+ ['0A00'].pack('H*')
149
+
150
+ @worksheet.add_mso_generic(type, version, instance, data, length)
151
+ end
152
+
153
+ #
154
+ # OBJ record that is part of cell comments.
155
+ # obj_id # Object ID number.
156
+ #
157
+ def obj_comment_record(obj_id) #:nodoc:
158
+ record = 0x005D # Record identifier
159
+ length = 0x0034 # Bytes to follow
160
+
161
+ obj_type = 0x0019 # Object type (comment).
162
+ data = '' # Record data.
163
+
164
+ sub_record = 0x0000 # Sub-record identifier.
165
+ sub_length = 0x0000 # Length of sub-record.
166
+ sub_data = '' # Data of sub-record.
167
+ options = 0x4011
168
+ reserved = 0x0000
169
+
170
+ # Add ftCmo (common object data) subobject
171
+ sub_record = 0x0015 # ftCmo
172
+ sub_length = 0x0012
173
+ sub_data = [obj_type, obj_id, options, reserved, reserved, reserved].pack( "vvvVVV")
174
+ data = [sub_record, sub_length].pack("vv") + sub_data
175
+
176
+ # Add ftNts (note structure) subobject
177
+ sub_record = 0x000D # ftNts
178
+ sub_length = 0x0016
179
+ sub_data = [reserved,reserved,reserved,reserved,reserved,reserved].pack( "VVVVVv")
180
+ data += [sub_record, sub_length].pack("vv") + sub_data
181
+
182
+ # Add ftEnd (end of object) subobject
183
+ sub_record = 0x0000 # ftNts
184
+ sub_length = 0x0000
185
+ data += [sub_record, sub_length].pack("vv")
186
+
187
+ # Pack the record.
188
+ header = [record, length].pack("vv")
189
+
190
+ header + data
191
+ end
192
+
193
+ private
194
+
195
+ def params_with(options)
196
+ params = default_params.update(options)
197
+
198
+ # Ensure that a width and height have been set.
199
+ params[:width] = default_width unless params[:width] && params[:width] != 0
200
+ params[:width] = params[:width] * params[:x_scale] if params[:x_scale] != 0
201
+ params[:height] = default_height unless params[:height] && params[:height] != 0
202
+ params[:height] = params[:height] * params[:y_scale] if params[:y_scale] != 0
203
+
204
+ params[:author], params[:author_encoding] =
205
+ string_and_encoding(params[:author], params[:author_encoding], 'author')
206
+
207
+ # Set the comment background colour.
208
+ params[:color] = background_color(params[:color])
209
+
210
+ # Set the default start cell and offsets for the comment. These are
211
+ # generally fixed in relation to the parent cell. However there are
212
+ # some edge cases for cells at the, er, edges.
213
+ #
214
+ params[:start_row] = default_start_row unless params[:start_row]
215
+ params[:y_offset] = default_y_offset unless params[:y_offset]
216
+ params[:start_col] = default_start_col unless params[:start_col]
217
+ params[:x_offset] = default_x_offset unless params[:x_offset]
218
+
219
+ params
220
+ end
221
+
222
+ def default_params
223
+ {
224
+ :author => '',
225
+ :author_encoding => 0,
226
+ :encoding => 0,
227
+ :color => nil,
228
+ :start_cell => nil,
229
+ :start_col => nil,
230
+ :start_row => nil,
231
+ :visible => nil,
232
+ :width => default_width,
233
+ :height => default_height,
234
+ :x_offset => nil,
235
+ :x_scale => 1,
236
+ :y_offset => nil,
237
+ :y_scale => 1
238
+ }
239
+ end
240
+
241
+ def default_width
242
+ 128
243
+ end
244
+
245
+ def default_height
246
+ 74
247
+ end
248
+
249
+ def default_start_row
250
+ case @row
251
+ when 0 then 0
252
+ when 65533 then 65529
253
+ when 65534 then 65530
254
+ when 65535 then 65531
255
+ else @row -1
256
+ end
257
+ end
258
+
259
+ def default_y_offset
260
+ case @row
261
+ when 0 then 2
262
+ when 65533 then 4
263
+ when 65534 then 4
264
+ when 65535 then 2
265
+ else 7
266
+ end
267
+ end
268
+
269
+ def default_start_col
270
+ case @col
271
+ when 253 then 250
272
+ when 254 then 251
273
+ when 255 then 252
274
+ else @col + 1
275
+ end
276
+ end
277
+
278
+ def default_x_offset
279
+ case @col
280
+ when 253 then 49
281
+ when 254 then 49
282
+ when 255 then 49
283
+ else 15
284
+ end
285
+ end
286
+
287
+ def string_and_encoding(string, encoding, type)
288
+ string = convert_to_ascii_if_ascii(string)
289
+ if encoding != 0
290
+ raise "Uneven number of bytes in #{type} string" if string.bytesize % 2 != 0
291
+ # Change from UTF-16BE to UTF-16LE
292
+ string = utf16be_to_16le(string)
293
+ # Handle utf8 strings
294
+ else
295
+ if is_utf8?(string)
296
+ string = NKF.nkf('-w16L0 -m0 -W', string)
297
+ ruby_19 { string.force_encoding('UTF-16LE') }
298
+ encoding = 1
299
+ end
300
+ end
301
+ [string, encoding]
302
+ end
303
+
304
+ def background_color(color)
305
+ color = Colors.new.get_color(color)
306
+ color = 0x50 if color == 0x7FFF # Default color.
307
+ color
308
+ end
309
+
310
+ # Calculate the positions of comment object.
311
+ def calc_vertices
312
+ @worksheet.position_object( @params[:start_col],
313
+ @params[:start_row],
314
+ @params[:x_offset],
315
+ @params[:y_offset],
316
+ @params[:width],
317
+ @params[:height]
318
+ )
319
+ end
320
+
321
+ def store_comment_mso_drawing_record(i, num_objects, num_comments, spid, visible, color, vertices)
322
+ if i == 0 && num_objects == 0
323
+ # Write the parent MSODRAWIING record.
324
+ dg_length = 200 + 128 * (num_comments - 1)
325
+ spgr_length = 176 + 128 * (num_comments - 1)
326
+
327
+ data = @worksheet.store_parent_mso_record(dg_length, spgr_length, spid)
328
+ spid += 1
329
+ else
330
+ data = ''
331
+ end
332
+ data += @worksheet.store_mso_sp_container(120) + @worksheet.store_mso_sp(202, spid, 0x0A00)
333
+ spid += 1
334
+ data +=
335
+ store_mso_opt_comment(0x80, visible, color) +
336
+ @worksheet.store_mso_client_anchor(3, *vertices) +
337
+ @worksheet.store_mso_client_data
338
+ record = 0x00EC # Record identifier
339
+ length = data.bytesize
340
+ header = [record, length].pack("vv")
341
+ append(header, data)
342
+
343
+ spid
344
+ end
345
+
346
+ def store_obj_comment(obj_id)
347
+ append(obj_comment_record(obj_id))
348
+ end
349
+
350
+ #
351
+ # Write the MSODRAWING ClientTextbox record that is part of comments.
352
+ #
353
+ def store_mso_drawing_text_box #:nodoc:
354
+ record = 0x00EC # Record identifier
355
+ length = 0x0008 # Bytes to follow
356
+
357
+ data = store_mso_client_text_box
358
+ header = [record, length].pack('vv')
359
+
360
+ append(header, data)
361
+ end
362
+
363
+ #
364
+ # Write the Escher ClientTextbox record that is part of MSODRAWING.
365
+ #
366
+ def store_mso_client_text_box #:nodoc:
367
+ type = 0xF00D
368
+ version = 0
369
+ instance = 0
370
+ data = ''
371
+ length = 0
372
+
373
+ @worksheet.add_mso_generic(type, version, instance, data, length)
374
+ end
375
+
376
+ #
377
+ # Write the worksheet TXO record that is part of cell comments.
378
+ # string_len # Length of the note text.
379
+ # format_len # Length of the format runs.
380
+ # rotation # Options
381
+ #
382
+ def store_txo(string_len, format_len = 16, rotation = 0) #:nodoc:
383
+ record = 0x01B6 # Record identifier
384
+ length = 0x0012 # Bytes to follow
385
+
386
+ grbit = 0x0212 # Options
387
+ reserved = 0x0000 # Options
388
+
389
+ # Pack the record.
390
+ header = [record, length].pack('vv')
391
+ data = [grbit, rotation, reserved, reserved, string_len, format_len, reserved].pack("vvVvvvV")
392
+ append(header, data)
393
+ end
394
+
395
+ #
396
+ # Write the first CONTINUE record to follow the TXO record. It contains the
397
+ # text data.
398
+ # string # Comment string.
399
+ # encoding # Encoding of the string.
400
+ #
401
+ def store_txo_continue_1(string, encoding = 0) #:nodoc:
402
+ # Split long comment strings into smaller continue blocks if necessary.
403
+ # We can't let BIFFwriter::_add_continue() handled this since an extra
404
+ # encoding byte has to be added similar to the SST block.
405
+ #
406
+ # We make the limit size smaller than the add_continue() size and even
407
+ # so that UTF16 chars occur in the same block.
408
+ #
409
+ limit = 8218
410
+ while string.bytesize > limit
411
+ string[0 .. limit] = ""
412
+ tmp_str = string
413
+ data = [encoding].pack("C") +
414
+ ruby_18 { tmp_str } ||
415
+ ruby_19 { tmp_str.force_encoding('ASCII-8BIT') }
416
+ length = data.bytesize
417
+ header = [record, length].pack('vv')
418
+
419
+ append(header, data)
420
+ end
421
+
422
+ # Pack the record.
423
+ data =
424
+ ruby_18 { [encoding].pack("C") + string } ||
425
+ ruby_19 { [encoding].pack("C") + string.force_encoding('ASCII-8BIT') }
426
+
427
+ record = 0x003C # Record identifier
428
+ length = data.bytesize
429
+ header = [record, length].pack('vv')
430
+
431
+ append(header, data)
432
+ end
433
+
434
+ #
435
+ # Write the second CONTINUE record to follow the TXO record. It contains the
436
+ # formatting information for the string.
437
+ # formats # Formatting information
438
+ #
439
+ def store_txo_continue_2(formats) #:nodoc:
440
+ # Pack the record.
441
+ data = ''
442
+
443
+ formats.each do |a_ref|
444
+ data += [a_ref[0], a_ref[1], 0x0].pack('vvV')
445
+ end
446
+
447
+ record = 0x003C # Record identifier
448
+ length = data.bytesize
449
+ header = [record, length].pack("vv")
450
+
451
+ append(header, data)
452
+ end
453
+
454
+ def append(*args)
455
+ @worksheet.append(*args)
456
+ end
457
+ end
458
+ end
459
+
460
+ end