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.
- data/README.rdoc +11 -2
- data/bin/extract_vba.rb +5 -0
- data/lib/write_xlsx/package/comments.rb +0 -2
- data/lib/write_xlsx/package/packager.rb +3 -2
- data/lib/write_xlsx/package/vml.rb +250 -31
- data/lib/write_xlsx/package/xml_writer_simple.rb +1 -1
- data/lib/write_xlsx/version.rb +1 -1
- data/lib/write_xlsx/workbook.rb +28 -18
- data/lib/write_xlsx/worksheet.rb +139 -4
- data/test/package/vml/test_write_div.rb +1 -1
- data/test/package/vml/test_write_fill.rb +10 -2
- data/test/package/vml/test_write_path.rb +10 -2
- data/test/package/vml/test_write_shapetype.rb +9 -1
- data/test/package/vml/test_write_textbox.rb +1 -1
- data/test/regression/test_button01.rb +23 -0
- data/test/regression/test_button02.rb +29 -0
- data/test/regression/test_button03.rb +24 -0
- data/test/regression/test_button04.rb +25 -0
- data/test/regression/test_button05.rb +28 -0
- data/test/regression/test_button06.rb +28 -0
- data/test/regression/test_button07.rb +32 -0
- data/test/regression/test_escapes07.rb +29 -0
- data/test/regression/test_escapes08.rb +30 -0
- data/test/regression/test_vml01.rb +29 -0
- data/test/regression/test_vml02.rb +31 -0
- data/test/regression/test_vml03.rb +40 -0
- data/test/regression/test_vml04.rb +41 -0
- data/test/regression/xlsx_files/button01.xlsx +0 -0
- data/test/regression/xlsx_files/button02.xlsx +0 -0
- data/test/regression/xlsx_files/button03.xlsx +0 -0
- data/test/regression/xlsx_files/button04.xlsx +0 -0
- data/test/regression/xlsx_files/button05.xlsx +0 -0
- data/test/regression/xlsx_files/button07.xlsm +0 -0
- data/test/regression/xlsx_files/escapes07.xlsx +0 -0
- data/test/regression/xlsx_files/escapes08.xlsx +0 -0
- data/test/regression/xlsx_files/vbaProject02.bin +0 -0
- data/test/regression/xlsx_files/vml01.xlsx +0 -0
- data/test/regression/xlsx_files/vml02.xlsx +0 -0
- data/test/regression/xlsx_files/vml03.xlsx +0 -0
- data/test/regression/xlsx_files/vml04.xlsx +0 -0
- 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 '
|
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
|
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
|
@@ -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.
|
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 @
|
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.
|
32
|
-
# Write the v:
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
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',
|
118
|
-
'coordsize',
|
119
|
-
'o:spt',
|
120
|
-
'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
|
-
|
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
|
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
|
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
|
-
|
265
|
+
write_comment_fill
|
203
266
|
# Write the v:shadow element.
|
204
267
|
write_shadow
|
205
268
|
# Write the v:path element.
|
206
|
-
|
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
|
-
|
324
|
+
write_button_textbox(button[:_font])
|
209
325
|
# Write the x:ClientData element.
|
210
|
-
|
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
|
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
|
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 =
|
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
|
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
|