ru_excel 0.0.6
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 +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
|