write_xlsx 0.59.0 → 0.60.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/README.rdoc +11 -2
  2. data/bin/extract_vba.rb +5 -0
  3. data/lib/write_xlsx/package/comments.rb +0 -2
  4. data/lib/write_xlsx/package/packager.rb +3 -2
  5. data/lib/write_xlsx/package/vml.rb +250 -31
  6. data/lib/write_xlsx/package/xml_writer_simple.rb +1 -1
  7. data/lib/write_xlsx/version.rb +1 -1
  8. data/lib/write_xlsx/workbook.rb +28 -18
  9. data/lib/write_xlsx/worksheet.rb +139 -4
  10. data/test/package/vml/test_write_div.rb +1 -1
  11. data/test/package/vml/test_write_fill.rb +10 -2
  12. data/test/package/vml/test_write_path.rb +10 -2
  13. data/test/package/vml/test_write_shapetype.rb +9 -1
  14. data/test/package/vml/test_write_textbox.rb +1 -1
  15. data/test/regression/test_button01.rb +23 -0
  16. data/test/regression/test_button02.rb +29 -0
  17. data/test/regression/test_button03.rb +24 -0
  18. data/test/regression/test_button04.rb +25 -0
  19. data/test/regression/test_button05.rb +28 -0
  20. data/test/regression/test_button06.rb +28 -0
  21. data/test/regression/test_button07.rb +32 -0
  22. data/test/regression/test_escapes07.rb +29 -0
  23. data/test/regression/test_escapes08.rb +30 -0
  24. data/test/regression/test_vml01.rb +29 -0
  25. data/test/regression/test_vml02.rb +31 -0
  26. data/test/regression/test_vml03.rb +40 -0
  27. data/test/regression/test_vml04.rb +41 -0
  28. data/test/regression/xlsx_files/button01.xlsx +0 -0
  29. data/test/regression/xlsx_files/button02.xlsx +0 -0
  30. data/test/regression/xlsx_files/button03.xlsx +0 -0
  31. data/test/regression/xlsx_files/button04.xlsx +0 -0
  32. data/test/regression/xlsx_files/button05.xlsx +0 -0
  33. data/test/regression/xlsx_files/button07.xlsm +0 -0
  34. data/test/regression/xlsx_files/escapes07.xlsx +0 -0
  35. data/test/regression/xlsx_files/escapes08.xlsx +0 -0
  36. data/test/regression/xlsx_files/vbaProject02.bin +0 -0
  37. data/test/regression/xlsx_files/vml01.xlsx +0 -0
  38. data/test/regression/xlsx_files/vml02.xlsx +0 -0
  39. data/test/regression/xlsx_files/vml03.xlsx +0 -0
  40. data/test/regression/xlsx_files/vml04.xlsx +0 -0
  41. metadata +54 -2
data/README.rdoc CHANGED
@@ -32,7 +32,7 @@ write_xlsx uses the same interface as writeexcel gem.
32
32
 
33
33
  Add this line to your application's Gemfile:
34
34
 
35
- gem 'writeexcel'
35
+ gem 'write_xlsx'
36
36
 
37
37
  And then execute:
38
38
 
@@ -40,7 +40,7 @@ And then execute:
40
40
 
41
41
  Or install it yourself as:
42
42
 
43
- $ gem install writeexcel
43
+ $ gem install write_xlsx
44
44
 
45
45
  == Synopsis
46
46
 
@@ -74,6 +74,15 @@ the first worksheet in an Excel XML spreadsheet called ruby.xlsx:
74
74
  workbook.close
75
75
 
76
76
  == Recent change
77
+ 2013-02-19 v0.60.0
78
+ Added Excel form buttons via the worksheet insert_button() method.
79
+ This allows the user to tie the button to an embedded macro imported
80
+ using add_vba_project().
81
+ The portal to the dungeon dimensions is now fully open.
82
+
83
+ bug fix in Worksheet#write_url
84
+ bug fix in bin/vba_extract.rb
85
+
77
86
  2013-02-17 v0.59.0
78
87
  Added macro support via VBA projects extracted from existing Excel
79
88
  xlsm files. User defined functions can be called from worksheets
data/bin/extract_vba.rb CHANGED
@@ -18,6 +18,11 @@ def extract_vba_project(src, dest, options = {})
18
18
  File.open(path, File::CREAT|File::WRONLY|File::BINARY) do |w|
19
19
  w.puts(is.read())
20
20
  end
21
+ # The mod data on vbaProject.bin isn't generally set correctly in the
22
+ # xlsm/zip file. This can cause issues on Windows so reset it to the
23
+ # current data.
24
+ ctime = Time.now
25
+ File::utime(ctime, ctime, path)
21
26
  break
22
27
  end
23
28
  end
@@ -167,8 +167,6 @@ def assemble_xml_file
167
167
  end
168
168
 
169
169
  def sorted_comments
170
- @sorted_comments if @sorted_comments
171
-
172
170
  @sorted_comments = []
173
171
  # We sort the comments by row and column but that isn't strictly required.
174
172
  @comments.keys.sort.each do |row|
@@ -42,6 +42,7 @@ def add_workbook(workbook)
42
42
  @sheet_names = workbook.sheetnames
43
43
  @chart_count = workbook.charts.size
44
44
  @drawing_count = workbook.drawings.size
45
+ @num_vml_files = workbook.num_vml_files
45
46
  @num_comment_files = workbook.num_comment_files
46
47
  @named_ranges = workbook.named_ranges
47
48
 
@@ -153,7 +154,7 @@ def write_chart_or_drawing_files(objects, filename)
153
154
  def write_vml_files
154
155
  index = 1
155
156
  @workbook.worksheets.each do |worksheet|
156
- next unless worksheet.has_comments?
157
+ next unless worksheet.has_vml?
157
158
  FileUtils.mkdir_p("#{@package_dir}/xl/drawings")
158
159
 
159
160
  vml = Package::Vml.new
@@ -273,7 +274,7 @@ def write_content_types_file
273
274
  (1 .. @chart_count).each { |i| content.add_chart_name("chart#{i}") }
274
275
  (1 .. @drawing_count).each { |i| content.add_drawing_name("drawing#{i}") }
275
276
 
276
- content.add_vml_name if @num_comment_files > 0
277
+ content.add_vml_name if @num_vml_files > 0
277
278
 
278
279
  (1 .. @table_count).each { |i| content.add_table_name("table#{i}") }
279
280
 
@@ -23,16 +23,27 @@ def assemble_xml_file(worksheet)
23
23
  # Write the o:shapelayout element.
24
24
  write_shapelayout(worksheet.vml_data_id)
25
25
 
26
- # Write the v:shapetype element.
27
- write_shapetype
28
-
29
26
  z_index = 1
30
27
  vml_shape_id = worksheet.vml_shape_id
31
- worksheet.comments_array.each do |comment|
32
- # Write the v:shape element.
33
- vml_shape_id += 1
34
- write_shape(vml_shape_id, z_index, comment)
35
- z_index += 1
28
+ unless worksheet.buttons_data.empty?
29
+ # Write the v:shapetype element.
30
+ write_button_shapetype
31
+ worksheet.buttons_data.each do |button|
32
+ # Write the v:shape element.
33
+ vml_shape_id += 1
34
+ write_button_shape(vml_shape_id, z_index, button)
35
+ z_index += 1
36
+ end
37
+ end
38
+ unless worksheet.comments_array.empty?
39
+ # Write the v:shapetype element.
40
+ write_comment_shapetype
41
+ worksheet.comments_array.each do |comment|
42
+ # Write the v:shape element.
43
+ vml_shape_id += 1
44
+ write_comment_shape(vml_shape_id, z_index, comment)
45
+ z_index += 1
46
+ end
36
47
  end
37
48
  end
38
49
  @writer.crlf
@@ -107,24 +118,40 @@ def write_idmap(data_id)
107
118
  #
108
119
  # Write the <v:shapetype> element.
109
120
  #
110
- def write_shapetype
111
- id = '_x0000_t202'
112
- coordsize = '21600,21600'
113
- spt = 202
114
- path = 'm,l,21600r21600,l21600,xe'
121
+ def write_comment_shapetype
122
+ attributes = [
123
+ 'id', '_x0000_t202',
124
+ 'coordsize', '21600,21600',
125
+ 'o:spt', 202,
126
+ 'path', 'm,l,21600r21600,l21600,xe'
127
+ ]
128
+
129
+ @writer.tag_elements('v:shapetype', attributes) do
130
+ # Write the v:stroke element.
131
+ write_stroke
132
+ # Write the v:path element.
133
+ write_comment_path('t', 'rect')
134
+ end
135
+ end
115
136
 
137
+ #
138
+ # Write the <v:shapetype> element.
139
+ #
140
+ def write_button_shapetype
116
141
  attributes = [
117
- 'id', id,
118
- 'coordsize', coordsize,
119
- 'o:spt', spt,
120
- 'path', path
142
+ 'id', '_x0000_t201',
143
+ 'coordsize', '21600,21600',
144
+ 'o:spt', 201,
145
+ 'path', 'm,l,21600r21600,l21600,xe'
121
146
  ]
122
147
 
123
148
  @writer.tag_elements('v:shapetype', attributes) do
124
149
  # Write the v:stroke element.
125
150
  write_stroke
126
151
  # Write the v:path element.
127
- write_path('t', 'rect')
152
+ write_button_path
153
+ # Write the o:lock element.
154
+ write_shapetype_lock
128
155
  end
129
156
  end
130
157
 
@@ -142,7 +169,7 @@ def write_stroke
142
169
  #
143
170
  # Write the <v:path> element.
144
171
  #
145
- def write_path(gradientshapeok, connecttype)
172
+ def write_comment_path(gradientshapeok, connecttype)
146
173
  attributes = []
147
174
 
148
175
  attributes << 'gradientshapeok' << 't' if gradientshapeok
@@ -151,10 +178,46 @@ def write_path(gradientshapeok, connecttype)
151
178
  @writer.empty_tag('v:path', attributes)
152
179
  end
153
180
 
181
+ #
182
+ # Write the <v:path> element.
183
+ #
184
+ def write_button_path
185
+ attributes = [
186
+ 'shadowok', 'f',
187
+ 'o:extrusionok', 'f',
188
+ 'strokeok', 'f',
189
+ 'fillok', 'f',
190
+ 'o:connecttype', 'rect'
191
+ ]
192
+ @writer.empty_tag('v:path', attributes)
193
+ end
194
+
195
+ #
196
+ # Write the <o:lock> element.
197
+ #
198
+ def write_shapetype_lock
199
+ attributes = [
200
+ 'v:ext', 'edit',
201
+ 'shapetype', 't'
202
+ ]
203
+ @writer.empty_tag('o:lock', attributes)
204
+ end
205
+
206
+ #
207
+ # Write the <o:lock> element.
208
+ #
209
+ def write_rotation_lock
210
+ attributes = [
211
+ 'v:ext', 'edit',
212
+ 'rotation', 't'
213
+ ]
214
+ @writer.empty_tag('o:lock', attributes)
215
+ end
216
+
154
217
  #
155
218
  # Write the <v:shape> element.
156
219
  #
157
- def write_shape(id, z_index, comment)
220
+ def write_comment_shape(id, z_index, comment)
158
221
  type = '#_x0000_t202'
159
222
  insetmode = 'auto'
160
223
  visibility = 'hidden'
@@ -199,15 +262,68 @@ def write_shape(id, z_index, comment)
199
262
 
200
263
  @writer.tag_elements('v:shape', attributes) do
201
264
  # Write the v:fill element.
202
- write_fill
265
+ write_comment_fill
203
266
  # Write the v:shadow element.
204
267
  write_shadow
205
268
  # Write the v:path element.
206
- write_path(nil, 'none')
269
+ write_comment_path(nil, 'none')
270
+ # Write the v:textbox element.
271
+ write_comment_textbox
272
+ # Write the x:ClientData element.
273
+ write_comment_client_data(comment)
274
+ end
275
+ end
276
+
277
+ #
278
+ # Write the <v:shape> element.
279
+ #
280
+ def write_button_shape(id, z_index, button)
281
+ type = '#_x0000_t201'
282
+
283
+ # Set the shape index.
284
+ id = "_x0000_s#{id}"
285
+
286
+ left, top, width, height = pixels_to_points(button[:_vertices])
287
+
288
+ left_str = float_to_str(left)
289
+ top_str = float_to_str(top)
290
+ width_str = float_to_str(width)
291
+ height_str = float_to_str(height)
292
+ z_index_str = float_to_str(z_index)
293
+
294
+ style =
295
+ 'position:absolute;' +
296
+ 'margin-left:' +
297
+ left_str + 'pt;' +
298
+ 'margin-top:' +
299
+ top_str + 'pt;' +
300
+ 'width:' +
301
+ width_str + 'pt;' +
302
+ 'height:' +
303
+ height_str + 'pt;' +
304
+ 'z-index:' +
305
+ z_index_str + ';' +
306
+ 'mso-wrap-style:tight'
307
+
308
+ attributes = [
309
+ 'id', id,
310
+ 'type', type,
311
+ 'style', style,
312
+ 'o:button', 't',
313
+ 'fillcolor', 'buttonFace [67]',
314
+ 'strokecolor', 'windowText [64]',
315
+ 'o:insetmode', 'auto'
316
+ ]
317
+
318
+ @writer.tag_elements('v:shape', attributes) do
319
+ # Write the v:fill element.
320
+ write_button_fill
321
+ # Write the o:lock element.
322
+ write_rotation_lock
207
323
  # Write the v:textbox element.
208
- write_textbox
324
+ write_button_textbox(button[:_font])
209
325
  # Write the x:ClientData element.
210
- write_client_data(comment)
326
+ write_button_client_data(button)
211
327
  end
212
328
  end
213
329
 
@@ -223,13 +339,28 @@ def float_to_str(float)
223
339
  #
224
340
  # Write the <v:fill> element.
225
341
  #
226
- def write_fill
342
+ def write_comment_fill
227
343
  color_2 = '#ffffe1'
228
344
  attributes = ['color2', color_2]
229
345
 
230
346
  @writer.empty_tag('v:fill', attributes)
231
347
  end
232
348
 
349
+ #
350
+ # Write the <v:fill> element.
351
+ #
352
+ def write_button_fill
353
+ color_2 = 'buttonFace [67]'
354
+ detectmouseclick = 't'
355
+
356
+ attributes = [
357
+ 'color2', color_2,
358
+ 'o:detectmouseclick', detectmouseclick
359
+ ]
360
+
361
+ @writer.empty_tag('v:fill', attributes)
362
+ end
363
+
233
364
  #
234
365
  # Write the <v:shadow> element.
235
366
  #
@@ -250,31 +381,67 @@ def write_shadow
250
381
  #
251
382
  # Write the <v:textbox> element.
252
383
  #
253
- def write_textbox
384
+ def write_comment_textbox
254
385
  style = 'mso-direction-alt:auto'
255
386
 
256
387
  attributes = ['style', style]
257
388
 
258
389
  @writer.tag_elements('v:textbox', attributes) do
259
390
  # Write the div element.
260
- write_div
391
+ write_div('left')
392
+ end
393
+ end
394
+
395
+ #
396
+ # Write the <v:textbox> element.
397
+ #
398
+ def write_button_textbox(font)
399
+ style = 'mso-direction-alt:auto'
400
+
401
+ attributes = ['style', style, 'o:singleclick', 'f']
402
+
403
+ @writer.tag_elements('v:textbox', attributes) do
404
+ # Write the div element.
405
+ write_div('center', font)
261
406
  end
262
407
  end
263
408
 
264
409
  #
265
410
  # Write the <div> element.
266
411
  #
267
- def write_div
268
- style = 'text-align:left'
412
+ def write_div(align, font = nil)
413
+ style = "text-align:#{align}"
269
414
  attributes = ['style', style]
270
415
 
271
- @writer.tag_elements('div', attributes) { }
416
+ @writer.tag_elements('div', attributes) do
417
+ if font
418
+ # Write the font element.
419
+ write_font(font)
420
+ end
421
+ end
422
+ end
423
+
424
+ #
425
+ # Write the <font> element.
426
+ #
427
+ def write_font(font)
428
+ caption = font[:_caption]
429
+ face = 'Calibri'
430
+ size = 220
431
+ color = '#000000'
432
+
433
+ attributes = [
434
+ 'face', face,
435
+ 'size', size,
436
+ 'color', color
437
+ ]
438
+ @writer.data_element('font', caption, attributes)
272
439
  end
273
440
 
274
441
  #
275
442
  # Write the <x:ClientData> element.
276
443
  #
277
- def write_client_data(comment)
444
+ def write_comment_client_data(comment)
278
445
  object_type = 'Note'
279
446
 
280
447
  attributes = ['ObjectType', object_type]
@@ -297,6 +464,30 @@ def write_client_data(comment)
297
464
  end
298
465
  end
299
466
 
467
+ #
468
+ # Write the <x:ClientData> element.
469
+ #
470
+ def write_button_client_data(button)
471
+ object_type = 'Button'
472
+
473
+ attributes = ['ObjectType', object_type]
474
+
475
+ @writer.tag_elements('x:ClientData', attributes) do
476
+ # Write the x:Anchor element.
477
+ write_anchor(button[:_vertices])
478
+ # Write the x:PrintObject element.
479
+ write_print_object
480
+ # Write the x:AutoFill element.
481
+ write_auto_fill
482
+ # Write the x:FmlaMacro element.
483
+ write_fmla_macro(button[:_macro])
484
+ # Write the x:TextHAlign element.
485
+ write_text_halign
486
+ # Write the x:TextVAlign element.
487
+ write_text_valign
488
+ end
489
+ end
490
+
300
491
  #
301
492
  # Write the <x:MoveWithCells> element.
302
493
  #
@@ -350,6 +541,34 @@ def write_row(data)
350
541
  def write_column(data)
351
542
  @writer.data_element('x:Column', data)
352
543
  end
544
+
545
+ #
546
+ # Write the <x:PrintObject> element.
547
+ #
548
+ def write_print_object
549
+ @writer.data_element('x:PrintObject', 'False')
550
+ end
551
+
552
+ #
553
+ # Write the <x:TextHAlign> element.
554
+ #
555
+ def write_text_halign
556
+ @writer.data_element('x:TextHAlign', 'Center')
557
+ end
558
+
559
+ #
560
+ # Write the <x:TextVAlign> element.
561
+ #
562
+ def write_text_valign
563
+ @writer.data_element('x:TextVAlign', 'Center')
564
+ end
565
+
566
+ #
567
+ # Write the <x:FmlaMacro> element.
568
+ #
569
+ def write_fmla_macro(data)
570
+ @writer.data_element('x:FmlaMacro', data)
571
+ end
353
572
  end
354
573
  end
355
574
  end