axlsx 1.0.16 → 1.0.18

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 (91) hide show
  1. data/.yardopts +1 -0
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +105 -46
  4. data/examples/example.rb +172 -142
  5. data/lib/axlsx/content_type/content_type.rb +4 -3
  6. data/lib/axlsx/content_type/default.rb +1 -0
  7. data/lib/axlsx/content_type/override.rb +1 -0
  8. data/lib/axlsx/doc_props/app.rb +1 -1
  9. data/lib/axlsx/doc_props/core.rb +1 -0
  10. data/lib/axlsx/drawing/axis.rb +1 -0
  11. data/lib/axlsx/drawing/bar_3D_chart.rb +1 -0
  12. data/lib/axlsx/drawing/bar_series.rb +1 -0
  13. data/lib/axlsx/drawing/cat_axis.rb +1 -0
  14. data/lib/axlsx/drawing/cat_axis_data.rb +1 -0
  15. data/lib/axlsx/drawing/chart.rb +4 -4
  16. data/lib/axlsx/drawing/drawing.rb +1 -0
  17. data/lib/axlsx/drawing/graphic_frame.rb +1 -0
  18. data/lib/axlsx/drawing/hyperlink.rb +1 -1
  19. data/lib/axlsx/drawing/line_3D_chart.rb +1 -0
  20. data/lib/axlsx/drawing/line_series.rb +1 -0
  21. data/lib/axlsx/drawing/marker.rb +1 -0
  22. data/lib/axlsx/drawing/one_cell_anchor.rb +1 -0
  23. data/lib/axlsx/drawing/pic.rb +2 -1
  24. data/lib/axlsx/drawing/picture_locking.rb +1 -0
  25. data/lib/axlsx/drawing/pie_3D_chart.rb +1 -0
  26. data/lib/axlsx/drawing/pie_series.rb +1 -0
  27. data/lib/axlsx/drawing/scaling.rb +1 -0
  28. data/lib/axlsx/drawing/ser_axis.rb +1 -0
  29. data/lib/axlsx/drawing/series.rb +1 -0
  30. data/lib/axlsx/drawing/series_title.rb +1 -0
  31. data/lib/axlsx/drawing/title.rb +1 -0
  32. data/lib/axlsx/drawing/two_cell_anchor.rb +1 -0
  33. data/lib/axlsx/drawing/val_axis.rb +1 -0
  34. data/lib/axlsx/drawing/val_axis_data.rb +1 -0
  35. data/lib/axlsx/drawing/view_3D.rb +1 -0
  36. data/lib/axlsx/package.rb +86 -38
  37. data/lib/axlsx/rels/relationship.rb +4 -3
  38. data/lib/axlsx/rels/relationships.rb +1 -0
  39. data/lib/axlsx/stylesheet/border.rb +1 -0
  40. data/lib/axlsx/stylesheet/border_pr.rb +1 -0
  41. data/lib/axlsx/stylesheet/cell_alignment.rb +1 -0
  42. data/lib/axlsx/stylesheet/cell_protection.rb +1 -0
  43. data/lib/axlsx/stylesheet/cell_style.rb +1 -0
  44. data/lib/axlsx/stylesheet/color.rb +1 -0
  45. data/lib/axlsx/stylesheet/fill.rb +1 -0
  46. data/lib/axlsx/stylesheet/font.rb +8 -0
  47. data/lib/axlsx/stylesheet/gradient_fill.rb +1 -1
  48. data/lib/axlsx/stylesheet/gradient_stop.rb +1 -1
  49. data/lib/axlsx/stylesheet/num_fmt.rb +1 -0
  50. data/lib/axlsx/stylesheet/pattern_fill.rb +1 -0
  51. data/lib/axlsx/stylesheet/styles.rb +12 -9
  52. data/lib/axlsx/stylesheet/table_style.rb +1 -0
  53. data/lib/axlsx/stylesheet/table_style_element.rb +1 -0
  54. data/lib/axlsx/stylesheet/table_styles.rb +1 -0
  55. data/lib/axlsx/stylesheet/xf.rb +1 -0
  56. data/lib/axlsx/util/cbf.rb +1 -0
  57. data/lib/axlsx/util/constants.rb +23 -14
  58. data/lib/axlsx/util/ms_off_crypto.rb +1 -1
  59. data/lib/axlsx/util/parser.rb +1 -0
  60. data/lib/axlsx/util/simple_typed_list.rb +2 -1
  61. data/lib/axlsx/util/storage.rb +1 -0
  62. data/lib/axlsx/util/validators.rb +11 -2
  63. data/lib/axlsx/version.rb +2 -1
  64. data/lib/axlsx/workbook/shared_strings_table.rb +71 -0
  65. data/lib/axlsx/workbook/shared_strings_table.rb~ +69 -0
  66. data/lib/axlsx/workbook/workbook.rb +47 -25
  67. data/lib/axlsx/workbook/worksheet/cell.rb +131 -78
  68. data/lib/axlsx/workbook/worksheet/date_time_converter.rb +29 -0
  69. data/lib/axlsx/workbook/worksheet/page_margins.rb +94 -0
  70. data/lib/axlsx/workbook/worksheet/row.rb +44 -12
  71. data/lib/axlsx/workbook/worksheet/shared_strings_table.rb~ +0 -0
  72. data/lib/axlsx/workbook/worksheet/worksheet.rb +158 -44
  73. data/lib/axlsx.rb +16 -10
  74. data/test/content_type/tc_content_type.rb +2 -0
  75. data/test/content_type/tc_default.rb +2 -0
  76. data/test/content_type/tc_override.rb +1 -0
  77. data/test/drawing/tc_chart.rb +2 -2
  78. data/test/stylesheet/tc_cell_alignment.rb +8 -3
  79. data/test/stylesheet/tc_font.rb +8 -0
  80. data/test/stylesheet/tc_styles.rb +6 -2
  81. data/test/tc_package.rb +31 -0
  82. data/test/workbook/tc_shared_strings_table.rb +39 -0
  83. data/test/workbook/tc_shared_strings_table.rb~ +8 -0
  84. data/test/workbook/tc_workbook.rb +8 -0
  85. data/test/workbook/worksheet/tc_cell.rb +52 -9
  86. data/test/workbook/worksheet/tc_date_time_converter.rb +127 -0
  87. data/test/workbook/worksheet/tc_date_time_converter.rb~ +69 -0
  88. data/test/workbook/worksheet/tc_page_margins.rb +100 -0
  89. data/test/workbook/worksheet/tc_row.rb +36 -0
  90. data/test/workbook/worksheet/tc_worksheet.rb +97 -3
  91. metadata +33 -40
@@ -1,53 +1,61 @@
1
- # -*- coding: utf-8 -*-
1
+ # encoding: UTF-8
2
2
  module Axlsx
3
- # A cell in a worksheet.
3
+ # A cell in a worksheet.
4
4
  # Cell stores inforamation requried to serialize a single worksheet cell to xml. You must provde the Row that the cell belongs to and the cells value. The data type will automatically be determed if you do not specify the :type option. The default style will be applied if you do not supply the :style option. Changing the cell's type will recast the value to the type specified. Altering the cell's value via the property accessor will also automatically cast the provided value to the cell's type.
5
5
  # @example Manually creating and manipulating Cell objects
6
- # ws = Workbook.new.add_worksheet
6
+ # ws = Workbook.new.add_worksheet
7
7
  # # This is the simple, and recommended way to create cells. Data types will automatically be determined for you.
8
8
  # ws.add_row :values => [1,"fish",Time.now]
9
9
  #
10
10
  # # but you can also do this
11
11
  # r = ws.add_row
12
12
  # r.add_cell 1
13
- #
13
+ #
14
14
  # # or even this
15
15
  # r = ws.add_row
16
16
  # c = Cell.new row, 1, :value=>integer
17
17
  #
18
18
  # # cells can also be accessed via Row#cells. The example here changes the cells type, which will automatically updated the value from 1 to 1.0
19
19
  # r.cells.last.type = :float
20
- #
20
+ #
21
21
  # @note The recommended way to generate cells is via Worksheet#add_row
22
- #
22
+ #
23
23
  # @see Worksheet#add_row
24
24
  class Cell
25
25
 
26
+
27
+ # An array of available inline styes.
28
+ INLINE_STYLES = ['value', 'type', 'font_name', 'charset',
29
+ 'family', 'b', 'i', 'strike','outline',
30
+ 'shadow', 'condense', 'extend', 'u',
31
+ 'vertAlign', 'sz', 'color', 'scheme']
32
+
33
+
26
34
  # The index of the cellXfs item to be applied to this cell.
27
- # @return [Integer]
35
+ # @return [Integer]
28
36
  # @see Axlsx::Styles
29
37
  attr_reader :style
30
38
 
31
39
  # The row this cell belongs to.
32
40
  # @return [Row]
33
41
  attr_reader :row
34
-
35
- # The cell's data type. Currently only four types are supported, :time, :float, :integer and :string.
36
- # Changing the type for a cell will recast the value into that type. If no type option is specified in the constructor, the type is
42
+
43
+ # The cell's data type. Currently only six types are supported, :date, :time, :float, :integer, :string and :boolean.
44
+ # Changing the type for a cell will recast the value into that type. If no type option is specified in the constructor, the type is
37
45
  # automatically determed.
38
46
  # @see Cell#cell_type_from_value
39
- # @return [Symbol] The type of data this cell's value is cast to.
40
- # @raise [ArgumentExeption] Cell.type must be one of [:time, :float, :integer, :string]
41
- # @note
47
+ # @return [Symbol] The type of data this cell's value is cast to.
48
+ # @raise [ArgumentExeption] Cell.type must be one of [:date, time, :float, :integer, :string, :boolean]
49
+ # @note
42
50
  # If the value provided cannot be cast into the type specified, type is changed to :string and the following logic is applied.
43
- # :string to :integer or :float, type coversions always return 0 or 0.0
51
+ # :string to :integer or :float, type conversions always return 0 or 0.0
44
52
  # :string, :integer, or :float to :time conversions always return the original value as a string and set the cells type to :string.
45
53
  # No support is currently implemented for parsing time strings.
46
54
  attr_reader :type
47
55
  # @see type
48
- def type=(v)
49
- RestrictionValidator.validate "Cell.type", [:time, :float, :integer, :string], v
50
- @type=v
56
+ def type=(v)
57
+ RestrictionValidator.validate "Cell.type", [:date, :time, :float, :integer, :string, :boolean], v
58
+ @type=v
51
59
  self.value = @value unless @value.nil?
52
60
  end
53
61
 
@@ -60,7 +68,7 @@ module Axlsx
60
68
  #TODO: consider doing value based type determination first?
61
69
  @value = cast_value(v)
62
70
  end
63
-
71
+
64
72
  # The inline font_name property for the cell
65
73
  # @return [String]
66
74
  attr_reader :font_name
@@ -131,7 +139,7 @@ module Axlsx
131
139
  # @return [Color]
132
140
  attr_reader :color
133
141
  # @param [String] The 8 character representation for an rgb color #FFFFFFFF"
134
- def color=(v)
142
+ def color=(v)
135
143
  @color = v.is_a?(Color) ? v : Color.new(:rgb=>v)
136
144
  end
137
145
 
@@ -156,7 +164,7 @@ module Axlsx
156
164
  def scheme=(v) RestrictionValidator.validate "Cell.schema", [:none, :major, :minor], v; @scheme = v; end
157
165
 
158
166
  # @param [Row] row The row this cell belongs to.
159
- # @param [Any] value The value associated with this cell.
167
+ # @param [Any] value The value associated with this cell.
160
168
  # @option options [Symbol] type The intended data type for this cell. If not specified the data type will be determined internally based on the vlue provided.
161
169
  # @option options [Integer] style The index of the cellXfs item to be applied to this cell. If not specified, the default style (0) will be applied.
162
170
  # @option options [String] font_name
@@ -175,9 +183,11 @@ module Axlsx
175
183
  # @option options [String] color an 8 letter rgb specification
176
184
  # @option options [Symbol] scheme must be one of :none, major, :minor
177
185
  def initialize(row, value="", options={})
178
- self.row=row
186
+ self.row=row
187
+ @font_name = @charset = @family = @b = @i = @strike = @outline = @shadow = nil
188
+ @condense = @u = @vertAlign = @sz = @color = @scheme = @extend = @ssti = nil
179
189
  @styles = row.worksheet.workbook.styles
180
- @row.cells << self
190
+ @row.cells << self
181
191
  options.each do |o|
182
192
  self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
183
193
  end
@@ -186,6 +196,24 @@ module Axlsx
186
196
  @value = cast_value(value)
187
197
  end
188
198
 
199
+ # The Shared Strings Table index for this cell
200
+ # @return [Integer]
201
+ attr_reader :ssti
202
+
203
+ # equality comparison to test value, type and inline style attributes
204
+ # this is how we work out if the cell needs to be added or already exists in the shared strings table
205
+ def shareable(v)
206
+
207
+ #using reject becase 1.8.7 select returns an array...
208
+ v_hash = v.instance_values.reject { |key, val| !INLINE_STYLES.include?(key) }
209
+ self_hash = self.instance_values.reject { |key, val| !INLINE_STYLES.include?(key) }
210
+ # required as color is an object, and the comparison will fail even though both use the same color.
211
+ v_hash['color'] = v_hash['color'].instance_values if v_hash['color']
212
+ self_hash['color'] = self_hash['color'].instance_values if self_hash['color']
213
+
214
+ v_hash == self_hash
215
+ end
216
+
189
217
  # @return [Integer] The index of the cell in the containing row.
190
218
  def index
191
219
  @row.cells.index(self)
@@ -193,14 +221,14 @@ module Axlsx
193
221
 
194
222
  # @return [String] The alpha(column)numeric(row) reference for this sell.
195
223
  # @example Relative Cell Reference
196
- # ws.rows.first.cells.first.r #=> "A1"
224
+ # ws.rows.first.cells.first.r #=> "A1"
197
225
  def r
198
- "#{col_ref}#{@row.index+1}"
226
+ "#{col_ref}#{@row.index+1}"
199
227
  end
200
228
 
201
229
  # @return [String] The absolute alpha(column)numeric(row) reference for this sell.
202
230
  # @example Absolute Cell Reference
203
- # ws.rows.first.cells.first.r #=> "$A$1"
231
+ # ws.rows.first.cells.first.r #=> "$A$1"
204
232
  def r_abs
205
233
  "$#{r.split('').join('$')}"
206
234
  end
@@ -229,74 +257,88 @@ module Axlsx
229
257
  target.r
230
258
  end
231
259
  self.row.worksheet.merge_cells "#{self.r}:#{range_end}" unless range_end.nil?
232
- end
260
+ end
261
+
262
+ # builds an xml text run based on this cells attributes. This is extracted from to_xml so that shared strings can use it.
263
+ # @param [Nokogiri::XML::Builder] xml The document builder instance this output will be added to.
264
+ # @return [String] the xml for this cell's text run
265
+ def run_xml(xml)
266
+ if (self.instance_values.keys & INLINE_STYLES).size > 0
267
+ xml.r {
268
+ xml.rPr {
269
+ xml.rFont(:val=>@font_name) if @font_name
270
+ xml.charset(:val=>@charset) if @charset
271
+ xml.family(:val=>@family) if @family
272
+ xml.b(:val=>@b) if @b
273
+ xml.i(:val=>@i) if @i
274
+ xml.strike(:val=>@strike) if @strike
275
+ xml.outline(:val=>@outline) if @outline
276
+ xml.shadow(:val=>@shadow) if @shadow
277
+ xml.condense(:val=>@condense) if @condense
278
+ xml.extend(:val=>@extend) if @extend
279
+ @color.to_xml(xml) if @color
280
+ xml.sz(:val=>@sz) if @sz
281
+ xml.u(:val=>@u) if @u
282
+ # :baseline, :subscript, :superscript
283
+ xml.vertAlign(:val=>@vertAlign) if @vertAlign
284
+ # :none, major, :minor
285
+ xml.scheme(:val=>@scheme) if @scheme
286
+ }
287
+ xml.t @value.to_s
288
+ }
289
+ else
290
+ xml.t @value.to_s
291
+ end
292
+ end
233
293
 
234
294
  # Serializes the cell
235
295
  # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
236
296
  # @return [String] xml text for the cell
237
- # @note
238
- # Shared Strings are not used in this library. All values are set directly in the each sheet.
239
297
  def to_xml(xml)
240
-
241
- # however nokogiri does a nice 'force_encoding' which we shall remove!
242
- if @type == :string
298
+ if @type == :string
243
299
  #parse formula
244
300
  if @value.start_with?('=')
245
301
  xml.c(:r => r, :t=>:str, :s=>style) {
246
302
  xml.f @value.to_s.gsub('=', '')
247
303
  }
248
304
  else
249
- #parse standard string
250
- #xml.c(:r => r, :t=>:inlineStr, :s=>style) {
251
- # xml.is { xml.t @value.to_s }
252
- #}
253
- #parse styled string
254
- xml.c(:r => r, :s=>style, :t => :inlineStr) {
255
- xml.is {
256
- xml.r {
257
- xml.rPr {
258
- xml.rFont(:val=>@font_name) if @font_name
259
- xml.charset(:val=>@charset) if @charset
260
- xml.family(:val=>@family) if @family
261
- xml.b(:val=>@b) if @b
262
- xml.i(:val=>@i) if @i
263
- xml.strike(:val=>@strike) if @strike
264
- xml.outline(:val=>@outline) if @outline
265
- xml.shadow(:val=>@shadow) if @shadow
266
- xml.condense(:val=>@condense) if @condense
267
- xml.extend(:val=>@extend) if @extend
268
- @color.to_xml(xml) if @color
269
- xml.sz(:val=>@sz) if @sz
270
- xml.u(:val=>@u) if @u
271
- # :baseline, :subscript, :superscript
272
- xml.vertAlign(:val=>@vertAlign) if @verAlign
273
- # :none, major, :minor
274
- xml.scheme(:val=>@scheme) if @scheme
275
- }
276
- xml.t @value.to_s
305
+ #parse shared
306
+ if @ssti
307
+ xml.c(:r => r, :s=>style, :t => :s) { xml.v ssti }
308
+ else
309
+ #parse inline string
310
+ xml.c(:r => r, :s=>style, :t => :inlineStr) {
311
+ xml.is {
312
+ run_xml(xml)
277
313
  }
278
314
  }
279
- }
315
+ end
280
316
  end
317
+ elsif @type == :date
318
+ # TODO: See if this is subject to the same restriction as Time below
319
+ v = DateTimeConverter::date_to_serial @value
320
+ xml.c(:r => r, :s => style) { xml.v v }
281
321
  elsif @type == :time
282
- # Using hardcoded offsets here as some operating systems will not except a 'negative' offset from the ruby epoc.
283
- # (1970)
284
- epoc1900 = -2209021200 #Time.local(1900, 1, 1)
285
- epoc1904 = -2082877200 #Time.local(1904, 1, 1)
286
- epoc = Workbook.date1904 ? epoc1904 : epoc1900
287
- v = ((@value.localtime.to_f - epoc) /60.0/60.0/24.0).to_f
322
+ v = DateTimeConverter::time_to_serial @value
288
323
  xml.c(:r => r, :s => style) { xml.v v }
324
+ elsif @type == :boolean
325
+ xml.c(:r => r, :s => style, :t => :b) { xml.v value }
289
326
  else
290
327
  xml.c(:r => r, :s => style) { xml.v value }
291
328
  end
292
329
  end
293
330
 
331
+ private
294
332
 
295
- private
333
+ # @see ssti
334
+ def ssti=(v)
335
+ Axlsx::validate_unsigned_int(v)
336
+ @ssti = v
337
+ end
296
338
 
297
339
  # assigns the owning row for this cell.
298
340
  def row=(v) DataTypeValidator.validate "Cell.row", Row, v; @row=v end
299
-
341
+
300
342
  # converts the column index into alphabetical values.
301
343
  # @note This follows the standard spreadsheet convention of naming columns A to Z, followed by AA to AZ etc.
302
344
  # @return [String]
@@ -311,15 +353,21 @@ module Axlsx
311
353
  chars.reverse.join
312
354
  end
313
355
 
314
- # Determines the cell type based on the cell value.
356
+ # Determines the cell type based on the cell value.
315
357
  # @note This is only used when a cell is created but no :type option is specified, the following rules apply:
316
- # 1. If the value is an instance of Time, the type is set to :time
317
- # 2. :float and :integer types are determined by regular expression matching.
318
- # 3. Anything that does not meet either of the above is determined to be :string.
358
+ # 1. If the value is an instance of Date, the type is set to :date
359
+ # 2. If the value is an instance of Time, the type is set to :time
360
+ # 3. If the value is an instance of TrueClass or FalseClass, the type is set to :boolean
361
+ # 4. :float and :integer types are determined by regular expression matching.
362
+ # 5. Anything that does not meet either of the above is determined to be :string.
319
363
  # @return [Symbol] The determined type
320
- def cell_type_from_value(v)
321
- if v.is_a? Time
364
+ def cell_type_from_value(v)
365
+ if v.is_a?(Date)
366
+ :date
367
+ elsif v.is_a?(Time)
322
368
  :time
369
+ elsif v.is_a?(TrueClass) || v.is_a?(FalseClass)
370
+ :boolean
323
371
  elsif v.to_s.match(/\A[+-]?\d+?\Z/) #numeric
324
372
  :integer
325
373
  elsif v.to_s.match(/\A[+-]?\d+\.\d+?\Z/) #float
@@ -329,22 +377,27 @@ module Axlsx
329
377
  end
330
378
  end
331
379
 
332
- # Cast the value into this cells data type.
333
- # @note
380
+ # Cast the value into this cells data type.
381
+ # @note
334
382
  # About Time - Time in OOXML is *different* from what you might expect. The history as to why is interesting, but you can safely assume that if you are generating docs on a mac, you will want to specify Workbook.1904 as true when using time typed values.
335
383
  # @see Axlsx#date1904
336
384
  def cast_value(v)
337
- if (@type == :time && v.is_a?(Time)) || (@type == :time && v.respond_to?(:to_time))
385
+ if @type == :date
386
+ self.style = STYLE_DATE if self.style == 0
387
+ v
388
+ elsif (@type == :time && v.is_a?(Time)) || (@type == :time && v.respond_to?(:to_time))
338
389
  self.style = STYLE_DATE if self.style == 0
339
390
  v.respond_to?(:to_time) ? v.to_time : v
340
391
  elsif @type == :float
341
392
  v.to_f
342
393
  elsif @type == :integer
343
394
  v.to_i
395
+ elsif @type == :boolean
396
+ v ? 1 : 0
344
397
  else
345
398
  @type = :string
346
399
  v.to_s
347
400
  end
348
- end
401
+ end
349
402
  end
350
403
  end
@@ -0,0 +1,29 @@
1
+ # encoding: UTF-8
2
+ require "date"
3
+
4
+ module Axlsx
5
+ # The DateTimeConverter class converts both data and time types to their apprpriate excel serializations
6
+ class DateTimeConverter
7
+
8
+ # The date_to_serial method converts Date objects to the equivelant excel serialized forms
9
+ # @param [Date] date the date to be serialized
10
+ # @return [Numeric]
11
+ def self.date_to_serial(date)
12
+ epoch = Axlsx::Workbook::date1904 ? Date.new(1904) : Date.new(1899, 12, 30)
13
+ (date-epoch).to_f
14
+ end
15
+
16
+ # The time_to_serial methond converts a Time object its excel serialized form.
17
+ # @param [Time] time the time to be serialized
18
+ # @return [Numeric]
19
+ def self.time_to_serial(time)
20
+ # Using hardcoded offsets here as some operating systems will not except
21
+ # a 'negative' offset from the ruby epoch.
22
+ epoch1900 = -2209161600 # Time.utc(1899, 12, 30).to_i
23
+ epoch1904 = -2082844800 # Time.utc(1904, 1, 1).to_i
24
+ seconds_per_day = 86400 # 60*60*24
25
+ epoch = Axlsx::Workbook::date1904 ? epoch1904 : epoch1900
26
+ (time.to_f - epoch)/seconds_per_day
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,94 @@
1
+ module Axlsx
2
+ # PageMargins specify the margins when printing a worksheet.
3
+ #
4
+ # For compatibility, PageMargins serialize to an empty string, unless at least one custom margin value
5
+ # has been specified. Otherwise, it serializes to a PageMargin element specifying all 6 margin values
6
+ # (using default values for margins that have not been specified explicitly).
7
+ #
8
+ # @note The recommended way to manage page margins is via Worksheet#page_margins
9
+ # @see Worksheet#page_margins
10
+ # @see Worksheet#initialize
11
+ class PageMargins
12
+
13
+ # Default left and right margin (in inches)
14
+ DEFAULT_LEFT_RIGHT = 0.75
15
+
16
+ # Default top and bottom margins (in inches)
17
+ DEFAULT_TOP_BOTTOM = 1.00
18
+
19
+ # Default header and footer margins (in inches)
20
+ DEFAULT_HEADER_FOOTER = 0.50
21
+
22
+ # Left margin (in inches)
23
+ # @return [Float]
24
+ attr_reader :left
25
+
26
+ # Right margin (in inches)
27
+ # @return [Float]
28
+ attr_reader :right
29
+
30
+ # Top margin (in inches)
31
+ # @return [Float]
32
+ attr_reader :top
33
+
34
+ # Bottom margin (in inches)
35
+ # @return [Float]
36
+ attr_reader :bottom
37
+
38
+ # Header margin (in inches)
39
+ # @return [Float]
40
+ attr_reader :header
41
+
42
+ # Footer margin (in inches)
43
+ # @return [Float]
44
+ attr_reader :footer
45
+
46
+ # Creates a new PageMargins object
47
+ # @option options [Numeric] left The left margin in inches
48
+ # @option options [Numeric] right The right margin in inches
49
+ # @option options [Numeric] bottom The bottom margin in inches
50
+ # @option options [Numeric] top The top margin in inches
51
+ # @option options [Numeric] header The header margin in inches
52
+ # @option options [Numeric] footer The footer margin in inches
53
+ def initialize(options={})
54
+ # Default values taken from MS Excel for Mac 2011
55
+ @left = @right = DEFAULT_LEFT_RIGHT
56
+ @top = @bottom = DEFAULT_TOP_BOTTOM
57
+ @header = @footer = DEFAULT_HEADER_FOOTER
58
+
59
+ options.each do |o|
60
+ self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
61
+ end
62
+ end
63
+
64
+ # Set some or all margins at once.
65
+ # @param [Hash] margins the margins to set (possible keys are :left, :right, :top, :bottom, :header and :footer).
66
+ def set(margins)
67
+ margins.select do |k, v|
68
+ next unless [:left, :right, :top, :bottom, :header, :footer].include? k
69
+ send("#{k}=", v)
70
+ end
71
+ end
72
+
73
+ # @see left
74
+ def left=(v); Axlsx::validate_unsigned_numeric(v); @left = v end
75
+ # @see right
76
+ def right=(v); Axlsx::validate_unsigned_numeric(v); @right = v end
77
+ # @see top
78
+ def top=(v); Axlsx::validate_unsigned_numeric(v); @top = v end
79
+ # @see bottom
80
+ def bottom=(v); Axlsx::validate_unsigned_numeric(v); @bottom = v end
81
+ # @see header
82
+ def header=(v); Axlsx::validate_unsigned_numeric(v); @header = v end
83
+ # @see footer
84
+ def footer=(v); Axlsx::validate_unsigned_numeric(v); @footer = v end
85
+
86
+ # Serializes the page margins element
87
+ # @note For compatibility, this is a noop unless custom margins have been specified.
88
+ # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
89
+ # @see #custom_margins_specified?
90
+ def to_xml(xml)
91
+ xml.pageMargins :left => left, :right => right, :top => top, :bottom => bottom, :header => header, :footer => footer
92
+ end
93
+ end
94
+ end
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  module Axlsx
2
3
  # A Row is a single row in a worksheet.
3
4
  # @note The recommended way to manage rows and cells is to use Worksheet#add_row
@@ -12,6 +13,22 @@ module Axlsx
12
13
  # @return [SimpleTypedList]
13
14
  attr_reader :cells
14
15
 
16
+ # The height of this row in points, if set explicitly.
17
+ # @return [Float]
18
+ attr_reader :height
19
+
20
+ # TODO 18.3.1.73
21
+ # collapsed
22
+ # customFormat
23
+ # hidden
24
+ # outlineLevel
25
+ # ph
26
+ # s (style)
27
+ # spans
28
+ # thickTop
29
+ # thickBottom
30
+
31
+
15
32
  # Creates a new row. New Cell objects are created based on the values, types and style options.
16
33
  # A new cell is created for each item in the values array. style and types options are applied as follows:
17
34
  # If the types option is defined and is a symbol it is applied to all the cells created.
@@ -22,28 +39,33 @@ module Axlsx
22
39
  # If the style option is not defined, the default style (0) is applied to each cell.
23
40
  # @param [Worksheet] worksheet
24
41
  # @option options [Array] values
25
- # @option options [Array, Symbol] types
26
- # @option options [Array, Integer] style
42
+ # @option options [Array, Symbol] types
43
+ # @option options [Array, Integer] style
44
+ # @option options [Float] height the row's height (in points)
27
45
  # @see Row#array_to_cells
28
46
  # @see Cell
29
47
  def initialize(worksheet, values=[], options={})
48
+ @height = nil
30
49
  self.worksheet = worksheet
31
50
  @cells = SimpleTypedList.new Cell
32
51
  @worksheet.rows << self
52
+ self.height = options.delete(:height) if options[:height]
33
53
  array_to_cells(values, options)
34
54
  end
35
55
 
36
56
  # The index of this row in the worksheet
37
57
  # @return [Integer]
38
- def index
58
+ def index
39
59
  worksheet.rows.index(self)
40
60
  end
41
-
61
+
42
62
  # Serializes the row
43
63
  # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
44
64
  # @return [String]
45
65
  def to_xml(xml)
46
- xml.row(:r => index+1) { @cells.each { |cell| cell.to_xml(xml) } }
66
+ attrs = {:r => index+1}
67
+ attrs.merge!(:customHeight => 1, :ht => height) if custom_height?
68
+ xml.row(attrs) { |ixml| @cells.each { |cell| cell.to_xml(ixml) } }
47
69
  end
48
70
 
49
71
  # Adds a singel sell to the row based on the data provided and updates the worksheet's autofit data.
@@ -53,7 +75,7 @@ module Axlsx
53
75
  update_auto_fit_data
54
76
  c
55
77
  end
56
-
78
+
57
79
  # sets the style for every cell in this row
58
80
  def style=(style)
59
81
  cells.each_with_index do | cell, index |
@@ -63,17 +85,27 @@ module Axlsx
63
85
  end
64
86
 
65
87
  # returns the cells in this row as an array
66
- # This lets us transpose the rows into columns
88
+ # This lets us transpose the rows into columns
67
89
  # @return [Array]
68
90
  def to_ary
69
91
  @cells.to_ary
70
92
  end
71
93
 
94
+ # @see height
95
+ def height=(v); Axlsx::validate_unsigned_numeric(v) unless v.nil?; @height = v end
96
+
97
+ # true if the row height has been manually set
98
+ # @return [Boolean]
99
+ # @see #height
100
+ def custom_height?
101
+ @height != nil
102
+ end
103
+
72
104
  private
73
105
 
74
106
  # assigns the owning worksheet for this row
75
107
  def worksheet=(v) DataTypeValidator.validate "Row.worksheet", Worksheet, v; @worksheet=v; end
76
-
108
+
77
109
  # Tell the worksheet to update autofit data for the columns based on this row's cells.
78
110
  # @return [SimpleTypedList]
79
111
  def update_auto_fit_data
@@ -88,13 +120,13 @@ module Axlsx
88
120
  # If the style option is defined and is an Integer, it is applied to all cells created.
89
121
  # If the style option is an array, style is applied by index for each cell.
90
122
  # @option options [Array] values
91
- # @option options [Array, Symbol] types
92
- # @option options [Array, Integer] style
123
+ # @option options [Array, Symbol] types
124
+ # @option options [Array, Integer] style
93
125
  def array_to_cells(values, options={})
94
126
  values = values
95
127
  DataTypeValidator.validate 'Row.array_to_cells', Array, values
96
128
  types, style = options.delete(:types), options.delete(:style)
97
- values.each_with_index do |value, index|
129
+ values.each_with_index do |value, index|
98
130
  cell_style = style.is_a?(Array) ? style[index] : style
99
131
  options[:style] = cell_style if cell_style
100
132
  cell_type = types.is_a?(Array)? types[index] : types
@@ -103,5 +135,5 @@ module Axlsx
103
135
  end
104
136
  end
105
137
  end
106
-
138
+
107
139
  end