writeexcel 0.6.9 → 0.6.10
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 +2 -0
- data/VERSION +1 -1
- data/lib/writeexcel/biffwriter.rb +29 -43
- data/lib/writeexcel/cell_range.rb +332 -0
- data/lib/writeexcel/chart.rb +50 -51
- data/lib/writeexcel/col_info.rb +87 -0
- data/lib/writeexcel/comments.rb +456 -0
- data/lib/writeexcel/convert_date_time.rb +117 -0
- data/lib/writeexcel/data_validations.rb +370 -0
- data/lib/writeexcel/debug_info.rb +5 -1
- data/lib/writeexcel/embedded_chart.rb +35 -0
- data/lib/writeexcel/format.rb +1 -1
- data/lib/writeexcel/formula.rb +3 -3
- data/lib/writeexcel/helper.rb +3 -0
- data/lib/writeexcel/image.rb +61 -1
- data/lib/writeexcel/olewriter.rb +2 -8
- data/lib/writeexcel/outline.rb +24 -0
- data/lib/writeexcel/shared_string_table.rb +153 -0
- data/lib/writeexcel/workbook.rb +86 -444
- data/lib/writeexcel/worksheet.rb +693 -2029
- data/lib/writeexcel/worksheets.rb +25 -0
- data/lib/writeexcel/write_file.rb +34 -15
- data/test/test_02_merge_formats.rb +0 -4
- data/test/test_04_dimensions.rb +0 -4
- data/test/test_05_rows.rb +0 -4
- data/test/test_06_extsst.rb +3 -6
- data/test/test_11_date_time.rb +0 -4
- data/test/test_12_date_only.rb +262 -231
- data/test/test_13_date_seconds.rb +0 -4
- data/test/test_21_escher.rb +71 -84
- data/test/test_22_mso_drawing_group.rb +0 -4
- data/test/test_23_note.rb +5 -21
- data/test/test_24_txo.rb +6 -7
- data/test/test_25_position_object.rb +0 -4
- data/test/test_26_autofilter.rb +0 -5
- data/test/test_27_autofilter.rb +0 -5
- data/test/test_28_autofilter.rb +0 -5
- data/test/test_29_process_jpg.rb +1 -1
- data/test/test_30_validation_dval.rb +4 -7
- data/test/test_31_validation_dv_strings.rb +9 -12
- data/test/test_32_validation_dv_formula.rb +11 -14
- data/test/test_42_set_properties.rb +0 -3
- data/test/test_50_name_stored.rb +0 -4
- data/test/test_51_name_print_area.rb +0 -4
- data/test/test_52_name_print_titles.rb +0 -4
- data/test/test_53_autofilter.rb +0 -4
- data/test/test_60_chart_generic.rb +42 -46
- data/test/test_61_chart_subclasses.rb +7 -11
- data/test/test_62_chart_formats.rb +12 -16
- data/test/test_63_chart_area_formats.rb +3 -7
- data/test/test_biff.rb +0 -4
- data/test/test_big_workbook.rb +17 -0
- data/test/test_format.rb +0 -3
- data/test/test_ole.rb +0 -4
- data/test/test_storage_lite.rb +0 -9
- data/test/test_workbook.rb +0 -4
- data/test/test_worksheet.rb +3 -7
- data/writeexcel.gemspec +12 -2
- metadata +12 -2
data/lib/writeexcel/chart.rb
CHANGED
@@ -477,15 +477,42 @@ def embedded=(val) # :nodoc:
|
|
477
477
|
end
|
478
478
|
|
479
479
|
#
|
480
|
-
#
|
481
|
-
# temporary files for efficiency. The Chart* classes don't need to do this
|
482
|
-
# since they are dealing with smaller amounts of data so we override
|
483
|
-
# _prepend() to turn it into an _append() method. This allows for a more
|
484
|
-
# natural method calling order.
|
480
|
+
# Setup the default configuration data for an embedded chart.
|
485
481
|
#
|
486
|
-
def
|
487
|
-
@
|
488
|
-
|
482
|
+
def set_embedded_config_data # :nodoc:
|
483
|
+
@embedded = true
|
484
|
+
|
485
|
+
@chartarea = {
|
486
|
+
:visible => 1,
|
487
|
+
:fg_color_index => 0x4E,
|
488
|
+
:fg_color_rgb => 0xFFFFFF,
|
489
|
+
:bg_color_index => 0x4D,
|
490
|
+
:bg_color_rgb => 0x000000,
|
491
|
+
:area_pattern => 0x0001,
|
492
|
+
:area_options => 0x0001,
|
493
|
+
:line_pattern => 0x0000,
|
494
|
+
:line_weight => 0x0000,
|
495
|
+
:line_color_index => 0x4D,
|
496
|
+
:line_color_rgb => 0x000000,
|
497
|
+
:line_options => 0x0009,
|
498
|
+
}
|
499
|
+
|
500
|
+
@config = default_config_data.merge({
|
501
|
+
:axisparent => [ 0, 0x01D8, 0x031D, 0x0D79, 0x07E9 ],
|
502
|
+
:axisparent_pos => [ 2, 2, 0x010C, 0x0292, 0x0E46, 0x09FD ],
|
503
|
+
:chart => [ 0x0000, 0x0000, 0x01847FE8, 0x00F47FE8 ],
|
504
|
+
:font_numbers => [ 5, 10, 0x1DC4, 0x1284, 0x0000 ],
|
505
|
+
:font_series => [ 6, 10, 0x1DC4, 0x1284, 0x0001 ],
|
506
|
+
:font_title => [ 7, 12, 0x1DC4, 0x1284, 0x0000 ],
|
507
|
+
:font_axes => [ 8, 10, 0x1DC4, 0x1284, 0x0001 ],
|
508
|
+
:legend => [ 0x044E, 0x0E4A, 0x088D, 0x0123, 0x0, 0x1, 0xF ],
|
509
|
+
:legend_pos => [ 5, 2, 0x044E, 0x0E4A, 0, 0 ],
|
510
|
+
:legend_text => [ 0xFFFFFFD9, 0xFFFFFFC1, 0, 0, 0x00B1, 0x0000 ],
|
511
|
+
:series_text => [ 0xFFFFFFD9, 0xFFFFFFC1, 0, 0, 0x00B1, 0x1020 ],
|
512
|
+
:title_text => [ 0x060F, 0x004C, 0x038A, 0x016F, 0x0081, 0x1030 ],
|
513
|
+
:x_axis_text => [ 0x07EF, 0x0C8F, 0x153, 0x123, 0x81, 0x00 ],
|
514
|
+
:y_axis_text => [ 0x0057, 0x0564, 0xB5, 0x035D, 0x0281, 0x00, 90 ],
|
515
|
+
})
|
489
516
|
end
|
490
517
|
|
491
518
|
#
|
@@ -554,6 +581,21 @@ def close # :nodoc:
|
|
554
581
|
store_eof
|
555
582
|
end
|
556
583
|
|
584
|
+
private
|
585
|
+
|
586
|
+
#
|
587
|
+
# The parent Worksheet class needs to store some data in memory and some in
|
588
|
+
# temporary files for efficiency. The Chart* classes don't need to do this
|
589
|
+
# since they are dealing with smaller amounts of data so we override
|
590
|
+
# _prepend() to turn it into an _append() method. This allows for a more
|
591
|
+
# natural method calling order.
|
592
|
+
#
|
593
|
+
def prepend(*args) # :nodoc:
|
594
|
+
@using_tmpfile = false
|
595
|
+
append(*args)
|
596
|
+
end
|
597
|
+
|
598
|
+
|
557
599
|
#
|
558
600
|
# Write BIFF record Window2. Note, this overrides the parent Worksheet
|
559
601
|
# record because the Chart version of the record is smaller and is used
|
@@ -704,7 +746,6 @@ def get_color_rbg(index) # :nodoc:
|
|
704
746
|
def palette
|
705
747
|
@workbook.palette
|
706
748
|
end
|
707
|
-
private :palette
|
708
749
|
|
709
750
|
#
|
710
751
|
# Get the Excel chart index for line pattern that corresponds to the user
|
@@ -822,7 +863,6 @@ def _formula_type_from_param(t, f, params, key) # :nodoc:
|
|
822
863
|
(v.nil? || v == [""] || v == '' || v == 0) ? f : t
|
823
864
|
end
|
824
865
|
end
|
825
|
-
private :_formula_type_from_param
|
826
866
|
|
827
867
|
#
|
828
868
|
# Write the SERIES chart substream.
|
@@ -878,7 +918,6 @@ def store_series_text_stream(font_index) # :nodoc:
|
|
878
918
|
def _formula_type(t, f, formula) # :nodoc:
|
879
919
|
(formula.nil? || formula == [""] || formula == '' || formula == 0) ? f : t
|
880
920
|
end
|
881
|
-
private :_formula_type
|
882
921
|
|
883
922
|
#
|
884
923
|
# Write the X-axis TEXT substream.
|
@@ -1053,7 +1092,6 @@ def store_area_frame_stream_common(type)
|
|
1053
1092
|
|
1054
1093
|
store_end
|
1055
1094
|
end
|
1056
|
-
private :store_area_frame_stream_common
|
1057
1095
|
|
1058
1096
|
#
|
1059
1097
|
# Write the CHARTFORMAT chart substream.
|
@@ -1924,46 +1962,7 @@ def default_config_data # :nodoc:
|
|
1924
1962
|
:y_axis_text_pos => [ 2, 2, 0, 0, 0x17, 0x44 ],
|
1925
1963
|
}
|
1926
1964
|
end
|
1927
|
-
private :default_config_data
|
1928
|
-
|
1929
|
-
#
|
1930
|
-
# Setup the default configuration data for an embedded chart.
|
1931
|
-
#
|
1932
|
-
def set_embedded_config_data # :nodoc:
|
1933
|
-
@embedded = true
|
1934
1965
|
|
1935
|
-
@chartarea = {
|
1936
|
-
:visible => 1,
|
1937
|
-
:fg_color_index => 0x4E,
|
1938
|
-
:fg_color_rgb => 0xFFFFFF,
|
1939
|
-
:bg_color_index => 0x4D,
|
1940
|
-
:bg_color_rgb => 0x000000,
|
1941
|
-
:area_pattern => 0x0001,
|
1942
|
-
:area_options => 0x0001,
|
1943
|
-
:line_pattern => 0x0000,
|
1944
|
-
:line_weight => 0x0000,
|
1945
|
-
:line_color_index => 0x4D,
|
1946
|
-
:line_color_rgb => 0x000000,
|
1947
|
-
:line_options => 0x0009,
|
1948
|
-
}
|
1949
|
-
|
1950
|
-
@config = default_config_data.merge({
|
1951
|
-
:axisparent => [ 0, 0x01D8, 0x031D, 0x0D79, 0x07E9 ],
|
1952
|
-
:axisparent_pos => [ 2, 2, 0x010C, 0x0292, 0x0E46, 0x09FD ],
|
1953
|
-
:chart => [ 0x0000, 0x0000, 0x01847FE8, 0x00F47FE8 ],
|
1954
|
-
:font_numbers => [ 5, 10, 0x1DC4, 0x1284, 0x0000 ],
|
1955
|
-
:font_series => [ 6, 10, 0x1DC4, 0x1284, 0x0001 ],
|
1956
|
-
:font_title => [ 7, 12, 0x1DC4, 0x1284, 0x0000 ],
|
1957
|
-
:font_axes => [ 8, 10, 0x1DC4, 0x1284, 0x0001 ],
|
1958
|
-
:legend => [ 0x044E, 0x0E4A, 0x088D, 0x0123, 0x0, 0x1, 0xF ],
|
1959
|
-
:legend_pos => [ 5, 2, 0x044E, 0x0E4A, 0, 0 ],
|
1960
|
-
:legend_text => [ 0xFFFFFFD9, 0xFFFFFFC1, 0, 0, 0x00B1, 0x0000 ],
|
1961
|
-
:series_text => [ 0xFFFFFFD9, 0xFFFFFFC1, 0, 0, 0x00B1, 0x1020 ],
|
1962
|
-
:title_text => [ 0x060F, 0x004C, 0x038A, 0x016F, 0x0081, 0x1030 ],
|
1963
|
-
:x_axis_text => [ 0x07EF, 0x0C8F, 0x153, 0x123, 0x81, 0x00 ],
|
1964
|
-
:y_axis_text => [ 0x0057, 0x0564, 0xB5, 0x035D, 0x0281, 0x00, 90 ],
|
1965
|
-
})
|
1966
|
-
end
|
1967
1966
|
end # class Chart
|
1968
1967
|
|
1969
1968
|
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,456 @@
|
|
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
|
+
@items[item.row] = { item.col => item }
|
13
|
+
end
|
14
|
+
|
15
|
+
def array
|
16
|
+
return @array if @array
|
17
|
+
|
18
|
+
@array = []
|
19
|
+
@items.keys.sort.each do |row|
|
20
|
+
@items[row].keys.sort.each do |col|
|
21
|
+
@array << @items[row][col]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
@array
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class Comments < Collection
|
30
|
+
attr_writer :visible
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
super
|
34
|
+
@visible = false
|
35
|
+
end
|
36
|
+
|
37
|
+
def visible?
|
38
|
+
@visible
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Comment
|
43
|
+
attr_reader :row, :col, :string, :encoding, :author, :author_encoding, :visible, :color, :vertices
|
44
|
+
|
45
|
+
def initialize(worksheet, row, col, string, options = {})
|
46
|
+
@worksheet = worksheet
|
47
|
+
@row, @col = row, col
|
48
|
+
@params = params_with(options)
|
49
|
+
@string, @params[:encoding] = string_and_encoding(string, @params[:encoding], 'comment')
|
50
|
+
|
51
|
+
# Limit the string to the max number of chars (not bytes).
|
52
|
+
max_len = 32767
|
53
|
+
max_len = max_len * 2 if @params[:encoding] != 0
|
54
|
+
|
55
|
+
if @string.bytesize > max_len
|
56
|
+
@string = @string[0 .. max_len]
|
57
|
+
end
|
58
|
+
@encoding = @params[:encoding]
|
59
|
+
@author = @params[:author]
|
60
|
+
@author_encoding = @params[:author_encoding]
|
61
|
+
@visible = @params[:visible]
|
62
|
+
@color = @params[:color]
|
63
|
+
@vertices = calc_vertices
|
64
|
+
end
|
65
|
+
|
66
|
+
def store_comment_record(i, num_objects, num_comments, spid)
|
67
|
+
str_len = string.bytesize
|
68
|
+
str_len = str_len / 2 if encoding != 0 # Num of chars not bytes.
|
69
|
+
|
70
|
+
spid = store_comment_mso_drawing_record(i, num_objects, num_comments, spid, visible, color, vertices)
|
71
|
+
store_obj_comment(num_objects + i + 1)
|
72
|
+
store_mso_drawing_text_box
|
73
|
+
store_txo(str_len)
|
74
|
+
store_txo_continue_1(string, encoding)
|
75
|
+
formats = [[0, 9], [str_len, 0]]
|
76
|
+
store_txo_continue_2(formats)
|
77
|
+
spid
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Write the worksheet NOTE record that is part of cell comments.
|
82
|
+
#
|
83
|
+
def store_note_record(obj_id) #:nodoc:
|
84
|
+
comment_author = author
|
85
|
+
comment_author_enc = author_encoding
|
86
|
+
ruby_19 { comment_author = [comment_author].pack('a*') if comment_author.ascii_only? }
|
87
|
+
record = 0x001C # Record identifier
|
88
|
+
length = 0x000C # Bytes to follow
|
89
|
+
|
90
|
+
comment_author = '' unless comment_author
|
91
|
+
comment_author_enc = 0 unless author_encoding
|
92
|
+
|
93
|
+
# Use the visible flag if set by the user or else use the worksheet value.
|
94
|
+
# The flag is also set in store_mso_opt_comment() but with the opposite
|
95
|
+
# value.
|
96
|
+
if visible
|
97
|
+
comment_visible = visible != 0 ? 0x0002 : 0x0000
|
98
|
+
else
|
99
|
+
comment_visible = @worksheet.comments_visible? ? 0x0002 : 0x0000
|
100
|
+
end
|
101
|
+
|
102
|
+
# Get the number of chars in the author string (not bytes).
|
103
|
+
num_chars = comment_author.bytesize
|
104
|
+
num_chars = num_chars / 2 if comment_author_enc != 0 && comment_author_enc
|
105
|
+
|
106
|
+
# Null terminate the author string.
|
107
|
+
comment_author =
|
108
|
+
ruby_18 { comment_author + "\0" } ||
|
109
|
+
ruby_19 { comment_author.force_encoding('BINARY') + "\0".force_encoding('BINARY') }
|
110
|
+
|
111
|
+
# Pack the record.
|
112
|
+
data = [row, col, comment_visible, obj_id, num_chars, comment_author_enc].pack("vvvvvC")
|
113
|
+
|
114
|
+
length = data.bytesize + comment_author.bytesize
|
115
|
+
header = [record, length].pack("vv")
|
116
|
+
|
117
|
+
append(header, data, comment_author)
|
118
|
+
end
|
119
|
+
|
120
|
+
#
|
121
|
+
# Write the Escher Opt record that is part of MSODRAWING.
|
122
|
+
#
|
123
|
+
def store_mso_opt_comment(spid, visible = nil, colour = 0x50) #:nodoc:
|
124
|
+
type = 0xF00B
|
125
|
+
version = 3
|
126
|
+
instance = 9
|
127
|
+
data = ''
|
128
|
+
length = 54
|
129
|
+
|
130
|
+
# Use the visible flag if set by the user or else use the worksheet value.
|
131
|
+
# Note that the value used is the opposite of Comment#note_record.
|
132
|
+
#
|
133
|
+
if visible
|
134
|
+
visible = visible != 0 ? 0x0000 : 0x0002
|
135
|
+
else
|
136
|
+
visible = @worksheet.comments_visible? ? 0x0000 : 0x0002
|
137
|
+
end
|
138
|
+
|
139
|
+
data = [spid].pack('V') +
|
140
|
+
['0000BF00080008005801000000008101'].pack("H*") +
|
141
|
+
[colour].pack("C") +
|
142
|
+
['000008830150000008BF011000110001'+'02000000003F0203000300BF03'].pack("H*") +
|
143
|
+
[visible].pack('v') +
|
144
|
+
['0A00'].pack('H*')
|
145
|
+
|
146
|
+
@worksheet.add_mso_generic(type, version, instance, data, length)
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# OBJ record that is part of cell comments.
|
151
|
+
# obj_id # Object ID number.
|
152
|
+
#
|
153
|
+
def obj_comment_record(obj_id) #:nodoc:
|
154
|
+
record = 0x005D # Record identifier
|
155
|
+
length = 0x0034 # Bytes to follow
|
156
|
+
|
157
|
+
obj_type = 0x0019 # Object type (comment).
|
158
|
+
data = '' # Record data.
|
159
|
+
|
160
|
+
sub_record = 0x0000 # Sub-record identifier.
|
161
|
+
sub_length = 0x0000 # Length of sub-record.
|
162
|
+
sub_data = '' # Data of sub-record.
|
163
|
+
options = 0x4011
|
164
|
+
reserved = 0x0000
|
165
|
+
|
166
|
+
# Add ftCmo (common object data) subobject
|
167
|
+
sub_record = 0x0015 # ftCmo
|
168
|
+
sub_length = 0x0012
|
169
|
+
sub_data = [obj_type, obj_id, options, reserved, reserved, reserved].pack( "vvvVVV")
|
170
|
+
data = [sub_record, sub_length].pack("vv") + sub_data
|
171
|
+
|
172
|
+
# Add ftNts (note structure) subobject
|
173
|
+
sub_record = 0x000D # ftNts
|
174
|
+
sub_length = 0x0016
|
175
|
+
sub_data = [reserved,reserved,reserved,reserved,reserved,reserved].pack( "VVVVVv")
|
176
|
+
data += [sub_record, sub_length].pack("vv") + sub_data
|
177
|
+
|
178
|
+
# Add ftEnd (end of object) subobject
|
179
|
+
sub_record = 0x0000 # ftNts
|
180
|
+
sub_length = 0x0000
|
181
|
+
data += [sub_record, sub_length].pack("vv")
|
182
|
+
|
183
|
+
# Pack the record.
|
184
|
+
header = [record, length].pack("vv")
|
185
|
+
|
186
|
+
header + data
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
|
191
|
+
def params_with(options)
|
192
|
+
params = default_params.update(options)
|
193
|
+
|
194
|
+
# Ensure that a width and height have been set.
|
195
|
+
params[:width] = default_width unless params[:width] && params[:width] != 0
|
196
|
+
params[:width] = params[:width] * params[:x_scale] if params[:x_scale] != 0
|
197
|
+
params[:height] = default_height unless params[:height] && params[:height] != 0
|
198
|
+
params[:height] = params[:height] * params[:y_scale] if params[:y_scale] != 0
|
199
|
+
|
200
|
+
params[:author], params[:author_encoding] =
|
201
|
+
string_and_encoding(params[:author], params[:author_encoding], 'author')
|
202
|
+
|
203
|
+
# Set the comment background colour.
|
204
|
+
params[:color] = background_color(params[:color])
|
205
|
+
|
206
|
+
# Set the default start cell and offsets for the comment. These are
|
207
|
+
# generally fixed in relation to the parent cell. However there are
|
208
|
+
# some edge cases for cells at the, er, edges.
|
209
|
+
#
|
210
|
+
params[:start_row] = default_start_row unless params[:start_row]
|
211
|
+
params[:y_offset] = default_y_offset unless params[:y_offset]
|
212
|
+
params[:start_col] = default_start_col unless params[:start_col]
|
213
|
+
params[:x_offset] = default_x_offset unless params[:x_offset]
|
214
|
+
|
215
|
+
params
|
216
|
+
end
|
217
|
+
|
218
|
+
def default_params
|
219
|
+
{
|
220
|
+
:author => '',
|
221
|
+
:author_encoding => 0,
|
222
|
+
:encoding => 0,
|
223
|
+
:color => nil,
|
224
|
+
:start_cell => nil,
|
225
|
+
:start_col => nil,
|
226
|
+
:start_row => nil,
|
227
|
+
:visible => nil,
|
228
|
+
:width => default_width,
|
229
|
+
:height => default_height,
|
230
|
+
:x_offset => nil,
|
231
|
+
:x_scale => 1,
|
232
|
+
:y_offset => nil,
|
233
|
+
:y_scale => 1
|
234
|
+
}
|
235
|
+
end
|
236
|
+
|
237
|
+
def default_width
|
238
|
+
128
|
239
|
+
end
|
240
|
+
|
241
|
+
def default_height
|
242
|
+
74
|
243
|
+
end
|
244
|
+
|
245
|
+
def default_start_row
|
246
|
+
case @row
|
247
|
+
when 0 then 0
|
248
|
+
when 65533 then 65529
|
249
|
+
when 65534 then 65530
|
250
|
+
when 65535 then 65531
|
251
|
+
else @row -1
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def default_y_offset
|
256
|
+
case @row
|
257
|
+
when 0 then 2
|
258
|
+
when 65533 then 4
|
259
|
+
when 65534 then 4
|
260
|
+
when 65535 then 2
|
261
|
+
else 7
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def default_start_col
|
266
|
+
case @col
|
267
|
+
when 253 then 250
|
268
|
+
when 254 then 251
|
269
|
+
when 255 then 252
|
270
|
+
else @col + 1
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def default_x_offset
|
275
|
+
case @col
|
276
|
+
when 253 then 49
|
277
|
+
when 254 then 49
|
278
|
+
when 255 then 49
|
279
|
+
else 15
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def string_and_encoding(string, encoding, type)
|
284
|
+
string = convert_to_ascii_if_ascii(string)
|
285
|
+
if encoding != 0
|
286
|
+
raise "Uneven number of bytes in #{type} string" if string.bytesize % 2 != 0
|
287
|
+
# Change from UTF-16BE to UTF-16LE
|
288
|
+
string = utf16be_to_16le(string)
|
289
|
+
# Handle utf8 strings
|
290
|
+
else
|
291
|
+
if is_utf8?(string)
|
292
|
+
string = NKF.nkf('-w16L0 -m0 -W', string)
|
293
|
+
ruby_19 { string.force_encoding('UTF-16LE') }
|
294
|
+
encoding = 1
|
295
|
+
end
|
296
|
+
end
|
297
|
+
[string, encoding]
|
298
|
+
end
|
299
|
+
|
300
|
+
def background_color(color)
|
301
|
+
color = Colors.new.get_color(color)
|
302
|
+
color = 0x50 if color == 0x7FFF # Default color.
|
303
|
+
color
|
304
|
+
end
|
305
|
+
|
306
|
+
# Calculate the positions of comment object.
|
307
|
+
def calc_vertices
|
308
|
+
@worksheet.position_object( @params[:start_col],
|
309
|
+
@params[:start_row],
|
310
|
+
@params[:x_offset],
|
311
|
+
@params[:y_offset],
|
312
|
+
@params[:width],
|
313
|
+
@params[:height]
|
314
|
+
)
|
315
|
+
end
|
316
|
+
|
317
|
+
def store_comment_mso_drawing_record(i, num_objects, num_comments, spid, visible, color, vertices)
|
318
|
+
if i == 0 && num_objects == 0
|
319
|
+
# Write the parent MSODRAWIING record.
|
320
|
+
dg_length = 200 + 128 * (num_comments - 1)
|
321
|
+
spgr_length = 176 + 128 * (num_comments - 1)
|
322
|
+
|
323
|
+
data = @worksheet.store_parent_mso_record(dg_length, spgr_length, spid)
|
324
|
+
spid += 1
|
325
|
+
else
|
326
|
+
data = ''
|
327
|
+
end
|
328
|
+
data += @worksheet.store_mso_sp_container(120) + @worksheet.store_mso_sp(202, spid, 0x0A00)
|
329
|
+
spid += 1
|
330
|
+
data +=
|
331
|
+
store_mso_opt_comment(0x80, visible, color) +
|
332
|
+
@worksheet.store_mso_client_anchor(3, *vertices) +
|
333
|
+
@worksheet.store_mso_client_data
|
334
|
+
record = 0x00EC # Record identifier
|
335
|
+
length = data.bytesize
|
336
|
+
header = [record, length].pack("vv")
|
337
|
+
append(header, data)
|
338
|
+
|
339
|
+
spid
|
340
|
+
end
|
341
|
+
|
342
|
+
def store_obj_comment(obj_id)
|
343
|
+
append(obj_comment_record(obj_id))
|
344
|
+
end
|
345
|
+
|
346
|
+
#
|
347
|
+
# Write the MSODRAWING ClientTextbox record that is part of comments.
|
348
|
+
#
|
349
|
+
def store_mso_drawing_text_box #:nodoc:
|
350
|
+
record = 0x00EC # Record identifier
|
351
|
+
length = 0x0008 # Bytes to follow
|
352
|
+
|
353
|
+
data = store_mso_client_text_box
|
354
|
+
header = [record, length].pack('vv')
|
355
|
+
|
356
|
+
append(header, data)
|
357
|
+
end
|
358
|
+
|
359
|
+
#
|
360
|
+
# Write the Escher ClientTextbox record that is part of MSODRAWING.
|
361
|
+
#
|
362
|
+
def store_mso_client_text_box #:nodoc:
|
363
|
+
type = 0xF00D
|
364
|
+
version = 0
|
365
|
+
instance = 0
|
366
|
+
data = ''
|
367
|
+
length = 0
|
368
|
+
|
369
|
+
@worksheet.add_mso_generic(type, version, instance, data, length)
|
370
|
+
end
|
371
|
+
|
372
|
+
#
|
373
|
+
# Write the worksheet TXO record that is part of cell comments.
|
374
|
+
# string_len # Length of the note text.
|
375
|
+
# format_len # Length of the format runs.
|
376
|
+
# rotation # Options
|
377
|
+
#
|
378
|
+
def store_txo(string_len, format_len = 16, rotation = 0) #:nodoc:
|
379
|
+
record = 0x01B6 # Record identifier
|
380
|
+
length = 0x0012 # Bytes to follow
|
381
|
+
|
382
|
+
grbit = 0x0212 # Options
|
383
|
+
reserved = 0x0000 # Options
|
384
|
+
|
385
|
+
# Pack the record.
|
386
|
+
header = [record, length].pack('vv')
|
387
|
+
data = [grbit, rotation, reserved, reserved, string_len, format_len, reserved].pack("vvVvvvV")
|
388
|
+
append(header, data)
|
389
|
+
end
|
390
|
+
|
391
|
+
#
|
392
|
+
# Write the first CONTINUE record to follow the TXO record. It contains the
|
393
|
+
# text data.
|
394
|
+
# string # Comment string.
|
395
|
+
# encoding # Encoding of the string.
|
396
|
+
#
|
397
|
+
def store_txo_continue_1(string, encoding = 0) #:nodoc:
|
398
|
+
# Split long comment strings into smaller continue blocks if necessary.
|
399
|
+
# We can't let BIFFwriter::_add_continue() handled this since an extra
|
400
|
+
# encoding byte has to be added similar to the SST block.
|
401
|
+
#
|
402
|
+
# We make the limit size smaller than the add_continue() size and even
|
403
|
+
# so that UTF16 chars occur in the same block.
|
404
|
+
#
|
405
|
+
limit = 8218
|
406
|
+
while string.bytesize > limit
|
407
|
+
string[0 .. limit] = ""
|
408
|
+
tmp_str = string
|
409
|
+
data = [encoding].pack("C") +
|
410
|
+
ruby_18 { tmp_str } ||
|
411
|
+
ruby_19 { tmp_str.force_encoding('ASCII-8BIT') }
|
412
|
+
length = data.bytesize
|
413
|
+
header = [record, length].pack('vv')
|
414
|
+
|
415
|
+
append(header, data)
|
416
|
+
end
|
417
|
+
|
418
|
+
# Pack the record.
|
419
|
+
data =
|
420
|
+
ruby_18 { [encoding].pack("C") + string } ||
|
421
|
+
ruby_19 { [encoding].pack("C") + string.force_encoding('ASCII-8BIT') }
|
422
|
+
|
423
|
+
record = 0x003C # Record identifier
|
424
|
+
length = data.bytesize
|
425
|
+
header = [record, length].pack('vv')
|
426
|
+
|
427
|
+
append(header, data)
|
428
|
+
end
|
429
|
+
|
430
|
+
#
|
431
|
+
# Write the second CONTINUE record to follow the TXO record. It contains the
|
432
|
+
# formatting information for the string.
|
433
|
+
# formats # Formatting information
|
434
|
+
#
|
435
|
+
def store_txo_continue_2(formats) #:nodoc:
|
436
|
+
# Pack the record.
|
437
|
+
data = ''
|
438
|
+
|
439
|
+
formats.each do |a_ref|
|
440
|
+
data += [a_ref[0], a_ref[1], 0x0].pack('vvV')
|
441
|
+
end
|
442
|
+
|
443
|
+
record = 0x003C # Record identifier
|
444
|
+
length = data.bytesize
|
445
|
+
header = [record, length].pack("vv")
|
446
|
+
|
447
|
+
append(header, data)
|
448
|
+
end
|
449
|
+
|
450
|
+
def append(*args)
|
451
|
+
@worksheet.append(*args)
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
end
|