write_xlsx 0.59.0 → 0.60.0
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.
- 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
|