ru_excel 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README +21 -0
- data/Rakefile +30 -0
- data/VERSION +1 -0
- data/examples/big-16Mb_test.rb +35 -0
- data/examples/test_multiline.rb +13 -0
- data/examples/test_utf8.rb +12 -0
- data/lib/ru_excel/biff_records.rb +1930 -0
- data/lib/ru_excel/bitmap.rb +222 -0
- data/lib/ru_excel/cell.rb +57 -0
- data/lib/ru_excel/column.rb +31 -0
- data/lib/ru_excel/compound_doc.rb +268 -0
- data/lib/ru_excel/deco.rb +156 -0
- data/lib/ru_excel/excel_magic.rb +1019 -0
- data/lib/ru_excel/formatting.rb +231 -0
- data/lib/ru_excel/row.rb +170 -0
- data/lib/ru_excel/style.rb +135 -0
- data/lib/ru_excel/unicode_utils.rb +73 -0
- data/lib/ru_excel/workbook.rb +365 -0
- data/lib/ru_excel/worksheet.rb +466 -0
- data/lib/ru_excel.rb +16 -0
- metadata +73 -0
@@ -0,0 +1,231 @@
|
|
1
|
+
=begin
|
2
|
+
The XF record is able to store explicit cell formatting attributes or the
|
3
|
+
attributes of a cell style. Explicit formatting includes the reference to
|
4
|
+
a cell style XF record. This allows to extend a defined cell style with
|
5
|
+
some explicit attributes. The formatting attributes are divided into
|
6
|
+
6 groups
|
7
|
+
|
8
|
+
Group Attributes
|
9
|
+
-------------------------------------
|
10
|
+
Number format Number format index (index to FORMAT record)
|
11
|
+
Font Font index (index to FONT record)
|
12
|
+
Alignment Horizontal and vertical alignment, text wrap, indentation,
|
13
|
+
orientation/rotation, text direction
|
14
|
+
Border Border line styles and colours
|
15
|
+
Background Background area style and colours
|
16
|
+
Protection Cell locked, formula hidden
|
17
|
+
|
18
|
+
For each group a flag in the cell XF record specifies whether to use the
|
19
|
+
attributes contained in that XF record or in the referenced style
|
20
|
+
XF record. In style XF records, these flags specify whether the attributes
|
21
|
+
will overwrite explicit cell formatting when the style is applied to
|
22
|
+
a cell. Changing a cell style (without applying this style to a cell) will
|
23
|
+
change all cells which already use that style and do not contain explicit
|
24
|
+
cell attributes for the changed style attributes. If a cell XF record does
|
25
|
+
not contain explicit attributes in a group (if the attribute group flag
|
26
|
+
is not set), it repeats the attributes of its style XF record.
|
27
|
+
=end
|
28
|
+
require File.dirname(__FILE__)+'/biff_records'
|
29
|
+
|
30
|
+
module Excel
|
31
|
+
class Font
|
32
|
+
ESCAPEMENT_NONE = 0x00
|
33
|
+
ESCAPEMENT_SUPERSCRIPT = 0x01
|
34
|
+
ESCAPEMENT_SUBSCRIPT = 0x02
|
35
|
+
|
36
|
+
UNDERLINE_NONE = 0x00
|
37
|
+
UNDERLINE_SINGLE = 0x01
|
38
|
+
UNDERLINE_SINGLE_ACC = 0x21
|
39
|
+
UNDERLINE_DOUBLE = 0x02
|
40
|
+
UNDERLINE_DOUBLE_ACC = 0x22
|
41
|
+
|
42
|
+
FAMILY_NONE = 0x00
|
43
|
+
FAMILY_ROMAN = 0x01
|
44
|
+
FAMILY_SWISS = 0x02
|
45
|
+
FAMILY_MODERN = 0x03
|
46
|
+
FAMILY_SCRIPT = 0x04
|
47
|
+
FAMILY_DECORARTIVE = 0x05
|
48
|
+
|
49
|
+
CHARSET_ANSI_LATIN = 0x00
|
50
|
+
CHARSET_SYS_DEFAULT = 0x01
|
51
|
+
CHARSET_SYMBOL = 0x02
|
52
|
+
CHARSET_APPLE_ROMAN = 0x4D
|
53
|
+
CHARSET_ANSI_JAP_SHIFT_JIS = 0x80
|
54
|
+
CHARSET_ANSI_KOR_HANGUL = 0x81
|
55
|
+
CHARSET_ANSI_KOR_JOHAB = 0x82
|
56
|
+
CHARSET_ANSI_CHINESE_GBK = 0x86
|
57
|
+
CHARSET_ANSI_CHINESE_BIG5 = 0x88
|
58
|
+
CHARSET_ANSI_GREEK = 0xA1
|
59
|
+
CHARSET_ANSI_TURKISH = 0xA2
|
60
|
+
CHARSET_ANSI_VIETNAMESE = 0xA3
|
61
|
+
CHARSET_ANSI_HEBREW = 0xB1
|
62
|
+
CHARSET_ANSI_ARABIC = 0xB2
|
63
|
+
CHARSET_ANSI_BALTIC = 0xBA
|
64
|
+
CHARSET_ANSI_CYRILLIC = 0xCC
|
65
|
+
CHARSET_ANSI_THAI = 0xDE
|
66
|
+
CHARSET_ANSI_LATIN_II = 0xEE
|
67
|
+
CHARSET_OEM_LATIN_I = 0xFF
|
68
|
+
|
69
|
+
def initialize
|
70
|
+
# twip = 1/20 of a point = 1/1440 of a inch
|
71
|
+
# usually resolution == 96 pixels per 1 inch
|
72
|
+
# (rarely 120 pixels per 1 inch or another one)
|
73
|
+
|
74
|
+
@height = 0x00C8 # 200: this is font with height 10 points
|
75
|
+
@italic = false
|
76
|
+
@struck_out = false
|
77
|
+
@outline = false
|
78
|
+
@shadow = false
|
79
|
+
@colour_index = 0x7FFF
|
80
|
+
@bold = false
|
81
|
+
@_weight = 0x0190 # 0x02BC gives bold font
|
82
|
+
@escapement = ESCAPEMENT_NONE
|
83
|
+
@underline = UNDERLINE_NONE
|
84
|
+
@family = FAMILY_NONE
|
85
|
+
@charset = CHARSET_ANSI_CYRILLIC
|
86
|
+
@name = 'Arial'
|
87
|
+
end
|
88
|
+
attr_accessor :height, :italic, :struck_out, :outline, :shadow, :colour_index, :bold, :_weight
|
89
|
+
attr_accessor :underline, :family, :charset, :name
|
90
|
+
|
91
|
+
def get_biff_record
|
92
|
+
height = @height
|
93
|
+
|
94
|
+
options = 0x00
|
95
|
+
if @bold
|
96
|
+
options |= 0x01
|
97
|
+
@_weight = 0x02BC
|
98
|
+
end
|
99
|
+
options |= 0x02 if @italic
|
100
|
+
options |= 0x04 if @underline != UNDERLINE_NONE
|
101
|
+
options |= 0x08 if @struck_out
|
102
|
+
options |= 0x010 if @outline
|
103
|
+
options |= 0x020 if @shadow
|
104
|
+
|
105
|
+
colour_index = @colour_index
|
106
|
+
weight = @_weight
|
107
|
+
escapement = @escapement
|
108
|
+
underline = @underline
|
109
|
+
family = @family
|
110
|
+
charset = @charset
|
111
|
+
name = @name
|
112
|
+
|
113
|
+
BiffRecord.fontRecord(
|
114
|
+
height, options, colour_index, weight, escapement,
|
115
|
+
underline, family, charset,
|
116
|
+
name)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class Alignment
|
121
|
+
HORZ_GENERAL = 0x00
|
122
|
+
HORZ_LEFT = 0x01
|
123
|
+
HORZ_CENTER = 0x02
|
124
|
+
HORZ_RIGHT = 0x03
|
125
|
+
HORZ_FILLED = 0x04
|
126
|
+
HORZ_JUSTIFIED = 0x05 # BIFF4-BIFF8X
|
127
|
+
HORZ_CENTER_ACROSS_SEL = 0x06 # Centred across selection (BIFF4-BIFF8X)
|
128
|
+
HORZ_DISTRIBUTED = 0x07 # Distributed (BIFF8X)
|
129
|
+
|
130
|
+
VERT_TOP = 0x00
|
131
|
+
VERT_CENTER = 0x01
|
132
|
+
VERT_BOTTOM = 0x02
|
133
|
+
VERT_JUSTIFIED = 0x03 # Justified (BIFF5-BIFF8X)
|
134
|
+
VERT_DISIRIBUTED = 0x04 # Distributed (BIFF8X)
|
135
|
+
|
136
|
+
DIRECTION_GENERAL = 0x00 # BIFF8X
|
137
|
+
DIRECTION_LR = 0x01
|
138
|
+
DIRECTION_RL = 0x02
|
139
|
+
|
140
|
+
ORIENTATION_NOT_ROTATED = 0x00
|
141
|
+
ORIENTATION_STACKED = 0x01
|
142
|
+
ORIENTATION_90_CC = 0x02
|
143
|
+
ORIENTATION_90_CW = 0x03
|
144
|
+
|
145
|
+
ROTATION_0_ANGLE = 0x00
|
146
|
+
ROTATION_STACKED = 0xFF
|
147
|
+
|
148
|
+
WRAP_AT_RIGHT = 0x01
|
149
|
+
NOT_WRAP_AT_RIGHT = 0x00
|
150
|
+
|
151
|
+
SHRINK_TO_FIT = 0x01
|
152
|
+
NOT_SHRINK_TO_FIT = 0x00
|
153
|
+
|
154
|
+
def initialize
|
155
|
+
@horz = HORZ_GENERAL
|
156
|
+
@vert = VERT_BOTTOM
|
157
|
+
@dire = DIRECTION_GENERAL
|
158
|
+
@orie = ORIENTATION_NOT_ROTATED
|
159
|
+
@rota = ROTATION_0_ANGLE
|
160
|
+
@wrap = NOT_WRAP_AT_RIGHT
|
161
|
+
# @wrap = WRAP_AT_RIGHT
|
162
|
+
@shri = NOT_SHRINK_TO_FIT
|
163
|
+
@inde = 0
|
164
|
+
@merg = 0
|
165
|
+
end
|
166
|
+
attr_accessor :horz, :vert, :dire, :orie, :rota, :wrap, :shri, :inde, :merg
|
167
|
+
end
|
168
|
+
|
169
|
+
class Borders
|
170
|
+
NO_LINE = 0x00
|
171
|
+
THIN = 0x01
|
172
|
+
MEDIUM = 0x02
|
173
|
+
DASHED = 0x03
|
174
|
+
DOTTED = 0x04
|
175
|
+
THICK = 0x05
|
176
|
+
DOUBLE = 0x06
|
177
|
+
HAIR = 0x07
|
178
|
+
#The following for BIFF8
|
179
|
+
MEDIUM_DASHED = 0x08
|
180
|
+
THIN_DASH_DOTTED = 0x09
|
181
|
+
MEDIUM_DASH_DOTTED = 0x0A
|
182
|
+
THIN_DASH_DOT_DOTTED = 0x0B
|
183
|
+
MEDIUM_DASH_DOT_DOTTED = 0x0C
|
184
|
+
SLANTED_MEDIUM_DASH_DOTTED = 0x0D
|
185
|
+
|
186
|
+
NEED_DIAG1 = 0x01
|
187
|
+
NEED_DIAG2 = 0x01
|
188
|
+
NO_NEED_DIAG1 = 0x00
|
189
|
+
NO_NEED_DIAG2 = 0x00
|
190
|
+
|
191
|
+
def initialize
|
192
|
+
@left = NO_LINE
|
193
|
+
@right = NO_LINE
|
194
|
+
@top = NO_LINE
|
195
|
+
@bottom = NO_LINE
|
196
|
+
@diag = NO_LINE
|
197
|
+
|
198
|
+
@left_colour = 0x40
|
199
|
+
@right_colour = 0x40
|
200
|
+
@top_colour = 0x40
|
201
|
+
@bottom_colour = 0x40
|
202
|
+
@diag_colour = 0x40
|
203
|
+
|
204
|
+
@need_diag1 = NO_NEED_DIAG1
|
205
|
+
@need_diag2 = NO_NEED_DIAG2
|
206
|
+
end
|
207
|
+
|
208
|
+
attr_accessor :left, :right, :top, :bottom, :diag, :left_colour, :right_colour, :top_colour, :bottom_colour
|
209
|
+
attr_accessor :diag_colour, :need_diag1, :need_diag2
|
210
|
+
end
|
211
|
+
|
212
|
+
class Pattern
|
213
|
+
# patterns 0x00 - 0x12
|
214
|
+
NO_PATTERN = 0x00
|
215
|
+
SOLID_PATTERN = 0x01
|
216
|
+
def initialize
|
217
|
+
@pattern = NO_PATTERN
|
218
|
+
@pattern_fore_colour = 0x40
|
219
|
+
@pattern_back_colour = 0x41
|
220
|
+
end
|
221
|
+
attr_accessor :pattern, :pattern_fore_colour, :pattern_back_colour
|
222
|
+
end
|
223
|
+
|
224
|
+
class Protection
|
225
|
+
def initialize
|
226
|
+
@cell_locked = 1
|
227
|
+
@formula_hidden = 0
|
228
|
+
end
|
229
|
+
attr_accessor :cell_locked, :formula_hidden
|
230
|
+
end
|
231
|
+
end
|
data/lib/ru_excel/row.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require dir+'/biff_records'
|
3
|
+
require dir+'/worksheet'
|
4
|
+
require dir+'/style'
|
5
|
+
require 'date'
|
6
|
+
|
7
|
+
module Excel
|
8
|
+
class Row
|
9
|
+
|
10
|
+
#################################################################
|
11
|
+
## Constructor
|
12
|
+
#################################################################
|
13
|
+
def initialize(index, parent_sheet)
|
14
|
+
@idx = index
|
15
|
+
@parent = parent_sheet
|
16
|
+
@parent_wb = parent_sheet.parent
|
17
|
+
@cells = []
|
18
|
+
@min_col_idx = 0
|
19
|
+
@max_col_idx = 0
|
20
|
+
@total_str = 0
|
21
|
+
@xf_index = 0x0F
|
22
|
+
@has_default_format = 0
|
23
|
+
@height_in_pixels = 0x11
|
24
|
+
|
25
|
+
@height = 0x00FF
|
26
|
+
@has_default_height = 0x00
|
27
|
+
@level = 0
|
28
|
+
@collapse = 0
|
29
|
+
@hidden = 0
|
30
|
+
@space_above = 0
|
31
|
+
@space_below = 0
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_accessor :level
|
35
|
+
|
36
|
+
def _adjust_height(style)
|
37
|
+
twips = style.font.height
|
38
|
+
points = twips.to_f/20.0
|
39
|
+
# Cell height in pixels can be calcuted by following approx. formula
|
40
|
+
# cell height in pixels = font height in points * 83/50 + 2/5
|
41
|
+
# It works when screen resolution is 96 dpi
|
42
|
+
pix = (points*83.0/50.0 + 2.0/5.0.to_i).round
|
43
|
+
if pix > @height_in_pixels
|
44
|
+
@height_in_pixels = pix
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def _adjust_bound_col_idx(*args)
|
50
|
+
for arg in args
|
51
|
+
if arg < @min_col_idx
|
52
|
+
@min_col_idx = arg
|
53
|
+
elsif arg > @max_col_idx
|
54
|
+
@max_col_idx = arg
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
EPOCH = Date.new(1899, 12, 31)
|
60
|
+
TIME_EPOCH = Time.mktime(1902, 1, 1)
|
61
|
+
TIME_EPOCH_ADD = (Date.new(1902, 1, 1) - Date.new(1899, 12, 31))
|
62
|
+
def _excel_date_dt(date)
|
63
|
+
if Date === date
|
64
|
+
xldate = (date - EPOCH).to_f + date.offset.to_f
|
65
|
+
elsif Time === date
|
66
|
+
xldate = (date - TIME_EPOCH +
|
67
|
+
date.utc_offset - TIME_EPOCH.utc_offset).to_f / (24 * 60 * 60) +
|
68
|
+
TIME_EPOCH_ADD
|
69
|
+
else
|
70
|
+
raise date.inspect+' is not a date'
|
71
|
+
end
|
72
|
+
# Add a day for Excel's missing leap day in 1900
|
73
|
+
xldate += 1 if xldate > 59
|
74
|
+
xldate
|
75
|
+
end
|
76
|
+
|
77
|
+
def get_height_in_pixels
|
78
|
+
@height_in_pixels
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def set_style(style)
|
83
|
+
_adjust_height(style)
|
84
|
+
@xf_index = @parent_wb.add_style(style)
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
def get_xf_index
|
89
|
+
@xf_index
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def get_cells_count
|
94
|
+
@cells.length
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def get_min_col
|
99
|
+
@min_col_idx
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def get_max_col
|
104
|
+
@min_col_idx
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def get_str_count
|
109
|
+
@total_str
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
def get_row_biff_data
|
114
|
+
height_options = (@height & 0x07FFF)
|
115
|
+
height_options |= (@has_default_height & 0x01) << 15
|
116
|
+
|
117
|
+
options = (@level & 0x07) << 0
|
118
|
+
options |= (@collapse & 0x01) << 4
|
119
|
+
options |= (@hidden & 0x01) << 5
|
120
|
+
options |= (0x00 & 0x01) << 6
|
121
|
+
options |= (0x01 & 0x01) << 8
|
122
|
+
if @xf_index != 0x0F
|
123
|
+
options |= (0x01 & 0x01) << 7
|
124
|
+
else
|
125
|
+
options |= (0x00 & 0x01) << 7
|
126
|
+
end
|
127
|
+
options |= (@xf_index & 0x0FFF) << 16
|
128
|
+
options |= (0x00 & @space_above) << 28
|
129
|
+
options |= (0x00 & @space_below) << 29
|
130
|
+
|
131
|
+
BiffRecord.rowRecord(@idx, @min_col_idx, @max_col_idx, height_options, options)
|
132
|
+
end
|
133
|
+
|
134
|
+
def get_cells_biff_data
|
135
|
+
#@cells.map{|c| c.get_biff_data()}.join('')
|
136
|
+
@cells.join('')
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
def get_index
|
141
|
+
@idx
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
def write(col, label, style)
|
146
|
+
_adjust_height(style)
|
147
|
+
_adjust_bound_col_idx(col)
|
148
|
+
if String === label
|
149
|
+
if label.length > 0
|
150
|
+
@cells << Cell.strCell(self, col, @parent_wb.add_style(style), @parent_wb.add_str(label))
|
151
|
+
@total_str += 1
|
152
|
+
else
|
153
|
+
@cells << Cell.blankCell(self, col, @parent_wb.add_style(style))
|
154
|
+
end
|
155
|
+
elsif Numeric === label
|
156
|
+
@cells << Cell.numberCell(self, col, @parent_wb.add_style(style), label)
|
157
|
+
elsif Date === label or Time === label
|
158
|
+
@cells << Cell.numberCell(self, col, @parent_wb.add_style(style), _excel_date_dt(label))
|
159
|
+
else
|
160
|
+
@cells << Cell.formulaCell(self, col, @parent_wb.add_style(style), label)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def write_blanks(c1, c2, style)
|
165
|
+
_adjust_height(style)
|
166
|
+
_adjust_bound_col_idx(c1, c2)
|
167
|
+
@cells << Cell.mulBlankCell(self, c1, c2, @parent_wb.add_style(style))
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require dir+'/formatting'
|
3
|
+
require dir+'/biff_records'
|
4
|
+
|
5
|
+
module Excel
|
6
|
+
|
7
|
+
class XFStyle
|
8
|
+
Default_num_format = 'general'
|
9
|
+
Default_font = Font.new()
|
10
|
+
Default_alignment = Alignment.new()
|
11
|
+
Default_borders = Borders.new()
|
12
|
+
Default_pattern = Pattern.new()
|
13
|
+
Default_protection = Protection.new()
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@num_format_str = Default_num_format
|
17
|
+
@font = Default_font
|
18
|
+
@alignment = Default_alignment
|
19
|
+
@borders = Default_borders
|
20
|
+
@pattern = Default_pattern
|
21
|
+
@protection = Default_protection
|
22
|
+
end
|
23
|
+
attr_accessor :num_format_str, :font, :alignment, :borders, :pattern, :protection
|
24
|
+
end
|
25
|
+
|
26
|
+
class StyleCollection
|
27
|
+
Std_num_fmt_list = [
|
28
|
+
'general',
|
29
|
+
'0',
|
30
|
+
'0.00',
|
31
|
+
'#,##0',
|
32
|
+
'#,##0.00',
|
33
|
+
'"$"#,##0_);("$"#,##',
|
34
|
+
'"$"#,##0_);[Red]("$"#,##',
|
35
|
+
'"$"#,##0.00_);("$"#,##',
|
36
|
+
'"$"#,##0.00_);[Red]("$"#,##',
|
37
|
+
'0%',
|
38
|
+
'0.00%',
|
39
|
+
'0.00E+00',
|
40
|
+
'# ?/?',
|
41
|
+
'# ??/??',
|
42
|
+
'M/D/YY',
|
43
|
+
'D-MMM-YY',
|
44
|
+
'D-MMM',
|
45
|
+
'MMM-YY',
|
46
|
+
'h:mm AM/PM',
|
47
|
+
'h:mm:ss AM/PM',
|
48
|
+
'h:mm',
|
49
|
+
'h:mm:ss',
|
50
|
+
'M/D/YY h:mm',
|
51
|
+
'_(#,##0_);(#,##0)',
|
52
|
+
'_(#,##0_);[Red](#,##0)',
|
53
|
+
'_(#,##0.00_);(#,##0.00)',
|
54
|
+
'_(#,##0.00_);[Red](#,##0.00)',
|
55
|
+
'_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)',
|
56
|
+
'_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',
|
57
|
+
'_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)',
|
58
|
+
'_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',
|
59
|
+
'mm:ss',
|
60
|
+
'[h]:mm:ss',
|
61
|
+
'mm:ss.0',
|
62
|
+
'##0.0E+0',
|
63
|
+
'@'
|
64
|
+
]
|
65
|
+
|
66
|
+
NumFormats = {}
|
67
|
+
for fmtidx, fmtstr in (0...23).zip(Std_num_fmt_list[(0)...(23)])
|
68
|
+
NumFormats[fmtstr] = fmtidx
|
69
|
+
end
|
70
|
+
for fmtidx, fmtstr in (37...50).zip(Std_num_fmt_list[(23)..-1])
|
71
|
+
NumFormats[fmtstr] = fmtidx
|
72
|
+
end
|
73
|
+
|
74
|
+
def initialize
|
75
|
+
@_fonts = {}
|
76
|
+
@_fonts[Font.new()] = 0
|
77
|
+
@_fonts[Font.new()] = 1
|
78
|
+
@_fonts[Font.new()] = 2
|
79
|
+
@_fonts[Font.new()] = 3
|
80
|
+
# The font with index 4 is omitted in all BIFF versions
|
81
|
+
@_fonts[Font.new()] = 5
|
82
|
+
@_num_formats = NumFormats.dup
|
83
|
+
|
84
|
+
@_xf = {}
|
85
|
+
@default_style = XFStyle.new()
|
86
|
+
@_default_xf = _add_style(@default_style)[0]
|
87
|
+
end
|
88
|
+
attr_accessor :default_style
|
89
|
+
|
90
|
+
def add(style)
|
91
|
+
style == nil ? 0x10 : _add_style(style)[1]
|
92
|
+
end
|
93
|
+
|
94
|
+
def _add_style(style)
|
95
|
+
num_format_str = style.num_format_str
|
96
|
+
num_format_idx = @_num_formats[num_format_str] ||=
|
97
|
+
163 + @_num_formats.length - Std_num_fmt_list.length
|
98
|
+
font = style.font
|
99
|
+
font_idx = @_fonts[font] ||= @_fonts.length + 1
|
100
|
+
xf = [font_idx, num_format_idx, style.alignment, style.borders, style.pattern, style.protection]
|
101
|
+
xf_index = @_xf[xf] ||= 0x10 + @_xf.length
|
102
|
+
[xf, xf_index]
|
103
|
+
end
|
104
|
+
|
105
|
+
def get_biff_data
|
106
|
+
result = ''
|
107
|
+
result << _all_fonts()
|
108
|
+
result << _all_num_formats()
|
109
|
+
result << _all_cell_styles()
|
110
|
+
result << _all_styles()
|
111
|
+
result
|
112
|
+
end
|
113
|
+
|
114
|
+
def _all_fonts
|
115
|
+
fonts = @_fonts.map{|k,v| [v,k]}.sort!
|
116
|
+
fonts.collect!{|_,font| font.get_biff_record}
|
117
|
+
fonts.join('')
|
118
|
+
end
|
119
|
+
|
120
|
+
def _all_num_formats
|
121
|
+
formats = @_num_formats.select{|k, v| v>=163}.to_a.each{|a| a.reverse!}
|
122
|
+
formats.map!{|fmtidx, fmtstr| BiffRecord.numberFormatRecord(fmtidx, fmtstr)}
|
123
|
+
formats.join('')
|
124
|
+
end
|
125
|
+
|
126
|
+
def _all_cell_styles
|
127
|
+
result = BiffRecord.xfRecord(@_default_xf, 'style') * 16
|
128
|
+
result << @_xf.map{|k,v| [v,k]}.sort!.collect!{|_,xf| BiffRecord.xfRecord(xf)}.join('')
|
129
|
+
end
|
130
|
+
|
131
|
+
def _all_styles
|
132
|
+
BiffRecord.styleRecord()
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
=begin
|
2
|
+
From BIFF8 on, strings are always stored using UTF-16LE text encoding. The
|
3
|
+
character array is a sequence of 16-bit values4. Additionally it is
|
4
|
+
possible to use a compressed format, which omits the high bytes of all
|
5
|
+
characters, if they are all zero.
|
6
|
+
|
7
|
+
The following tables describe the standard format of the entire string, but
|
8
|
+
in many records the strings differ from this format. This will be mentioned
|
9
|
+
separately. It is possible (but not required) to store Rich-Text formatting
|
10
|
+
information and Asian phonetic information inside a Unicode string. This
|
11
|
+
results in four different ways to store a string. The character array
|
12
|
+
is not zero-terminated.
|
13
|
+
|
14
|
+
The string consists of the character count (as usual an 8-bit value or
|
15
|
+
a 16-bit value), option flags, the character array and optional formatting
|
16
|
+
information. If the string is empty, sometimes the option flags field will
|
17
|
+
not occur. This is mentioned at the respective place.
|
18
|
+
|
19
|
+
Offset Size Contents
|
20
|
+
0 1 or 2 Length of the string (character count, ln)
|
21
|
+
1 or 2 1 Option flags:
|
22
|
+
Bit Mask Contents
|
23
|
+
0 01H Character compression (ccompr):
|
24
|
+
0 = Compressed (8-bit characters)
|
25
|
+
1 = Uncompressed (16-bit characters)
|
26
|
+
2 04H Asian phonetic settings (phonetic):
|
27
|
+
0 = Does not contain Asian phonetic settings
|
28
|
+
1 = Contains Asian phonetic settings
|
29
|
+
3 08H Rich-Text settings (richtext):
|
30
|
+
0 = Does not contain Rich-Text settings
|
31
|
+
1 = Contains Rich-Text settings
|
32
|
+
[2 or 3] 2 (optional, only if richtext=1) Number of Rich-Text formatting runs (rt)
|
33
|
+
[var.] 4 (optional, only if phonetic=1) Size of Asian phonetic settings block (in bytes, sz)
|
34
|
+
var. ln or
|
35
|
+
2�ln Character array (8-bit characters or 16-bit characters, dependent on ccompr)
|
36
|
+
[var.] 4�rt (optional, only if richtext=1) List of rt formatting runs
|
37
|
+
[var.] sz (optional, only if phonetic=1) Asian Phonetic Settings Block
|
38
|
+
=end
|
39
|
+
|
40
|
+
|
41
|
+
require 'iconv'
|
42
|
+
module Excel
|
43
|
+
module UnicodeUtils
|
44
|
+
|
45
|
+
def u2ints(str)
|
46
|
+
Excel::ICONV[:to_unicode].iconv(str).unpack('S*')
|
47
|
+
end
|
48
|
+
|
49
|
+
def u2bytes(str)
|
50
|
+
Excel::ICONV[:to_unicode].iconv(str)
|
51
|
+
end
|
52
|
+
|
53
|
+
def upack2(str)
|
54
|
+
begin
|
55
|
+
ustr = Excel::ICONV[:check_ascii].iconv(str)
|
56
|
+
[str.length, 0].pack('SC')+str
|
57
|
+
rescue Iconv::IllegalSequence
|
58
|
+
ustr = u2bytes(str)
|
59
|
+
[ustr.length / 2, 1].pack('SC')+ustr
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def upack1(str)
|
64
|
+
begin
|
65
|
+
ustr = Excel::ICONV[:check_ascii].iconv(str)
|
66
|
+
[str.length, 0].pack('CC')+str
|
67
|
+
rescue Iconv::IllegalSequence
|
68
|
+
ustr = u2bytes(str)
|
69
|
+
[ustr.length / 2, 1].pack('CC')+ustr
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|