fast_excel 0.2.1 → 0.3.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.
- checksums.yaml +5 -5
- data/.dockerignore +2 -0
- data/.gitignore +7 -0
- data/.travis.yml +44 -0
- data/CHANGELOG.md +41 -1
- data/Dockerfile.test +16 -0
- data/Gemfile +5 -2
- data/Gemfile.lock +30 -23
- data/LICENSE +21 -0
- data/Makefile +13 -0
- data/README.md +177 -40
- data/Rakefile +16 -0
- data/appveyor.yml +25 -0
- data/benchmarks/1k_rows.rb +17 -4
- data/benchmarks/20k_rows.rb +4 -0
- data/benchmarks/auto_width.rb +37 -0
- data/benchmarks/init.rb +14 -2
- data/benchmarks/memory.rb +8 -0
- data/benchmarks/profiler.rb +27 -0
- data/benchmarks/write_value.rb +62 -0
- data/examples/example.rb +3 -4
- data/examples/example_align.rb +23 -0
- data/examples/example_auto_width.rb +26 -0
- data/examples/example_colors.rb +37 -0
- data/examples/example_filters.rb +36 -0
- data/examples/example_formula.rb +1 -5
- data/examples/example_hyperlink.rb +20 -0
- data/examples/example_image.rb +1 -1
- data/examples/example_styles.rb +27 -0
- data/examples/logo.png +0 -0
- data/ext/fast_excel/extconf.rb +3 -0
- data/ext/fast_excel/text_width_ext.c +460 -0
- data/fast_excel.gemspec +2 -3
- data/letters.html +114 -0
- data/lib/fast_excel.rb +455 -78
- data/lib/fast_excel/binding.rb +31 -21
- data/lib/fast_excel/binding/chart.rb +20 -1
- data/lib/fast_excel/binding/format.rb +11 -4
- data/lib/fast_excel/binding/workbook.rb +10 -2
- data/lib/fast_excel/binding/worksheet.rb +44 -27
- data/libxlsxwriter/.gitignore +1 -0
- data/libxlsxwriter/.indent.pro +8 -0
- data/libxlsxwriter/.travis.yml +12 -0
- data/libxlsxwriter/CMakeLists.txt +338 -0
- data/libxlsxwriter/CONTRIBUTING.md +1 -1
- data/libxlsxwriter/Changes.txt +162 -0
- data/libxlsxwriter/LICENSE.txt +65 -4
- data/libxlsxwriter/Makefile +33 -11
- data/libxlsxwriter/Readme.md +3 -1
- data/libxlsxwriter/cocoapods/libxlsxwriter-umbrella.h +2 -1
- data/libxlsxwriter/cocoapods/libxlsxwriter.modulemap +2 -2
- data/libxlsxwriter/include/xlsxwriter.h +2 -2
- data/libxlsxwriter/include/xlsxwriter/app.h +2 -2
- data/libxlsxwriter/include/xlsxwriter/chart.h +164 -13
- data/libxlsxwriter/include/xlsxwriter/chartsheet.h +544 -0
- data/libxlsxwriter/include/xlsxwriter/common.h +35 -6
- data/libxlsxwriter/include/xlsxwriter/content_types.h +5 -2
- data/libxlsxwriter/include/xlsxwriter/core.h +2 -2
- data/libxlsxwriter/include/xlsxwriter/custom.h +2 -2
- data/libxlsxwriter/include/xlsxwriter/drawing.h +3 -2
- data/libxlsxwriter/include/xlsxwriter/format.h +8 -8
- data/libxlsxwriter/include/xlsxwriter/hash_table.h +1 -1
- data/libxlsxwriter/include/xlsxwriter/packager.h +18 -8
- data/libxlsxwriter/include/xlsxwriter/relationships.h +2 -2
- data/libxlsxwriter/include/xlsxwriter/shared_strings.h +5 -3
- data/libxlsxwriter/include/xlsxwriter/styles.h +10 -5
- data/libxlsxwriter/include/xlsxwriter/theme.h +2 -2
- data/libxlsxwriter/include/xlsxwriter/utility.h +35 -5
- data/libxlsxwriter/include/xlsxwriter/workbook.h +234 -57
- data/libxlsxwriter/include/xlsxwriter/worksheet.h +780 -91
- data/libxlsxwriter/include/xlsxwriter/xmlwriter.h +4 -2
- data/libxlsxwriter/libxlsxwriter.podspec +4 -2
- data/libxlsxwriter/src/Makefile +31 -6
- data/libxlsxwriter/src/app.c +2 -2
- data/libxlsxwriter/src/chart.c +116 -23
- data/libxlsxwriter/src/chartsheet.c +508 -0
- data/libxlsxwriter/src/content_types.c +12 -4
- data/libxlsxwriter/src/core.c +11 -11
- data/libxlsxwriter/src/custom.c +3 -3
- data/libxlsxwriter/src/drawing.c +114 -17
- data/libxlsxwriter/src/format.c +5 -5
- data/libxlsxwriter/src/hash_table.c +1 -1
- data/libxlsxwriter/src/packager.c +378 -61
- data/libxlsxwriter/src/relationships.c +2 -2
- data/libxlsxwriter/src/shared_strings.c +18 -4
- data/libxlsxwriter/src/styles.c +59 -12
- data/libxlsxwriter/src/theme.c +2 -2
- data/libxlsxwriter/src/utility.c +93 -6
- data/libxlsxwriter/src/workbook.c +379 -61
- data/libxlsxwriter/src/worksheet.c +1240 -174
- data/libxlsxwriter/src/xmlwriter.c +18 -9
- data/libxlsxwriter/third_party/minizip/Makefile +6 -1
- data/libxlsxwriter/third_party/minizip/ioapi.c +10 -0
- data/libxlsxwriter/third_party/minizip/zip.c +2 -0
- data/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.c +2 -2
- data/libxlsxwriter/version.txt +1 -1
- data/test/auto_width_test.rb +19 -0
- data/test/date_test.rb +34 -0
- data/test/format_test.rb +179 -0
- data/test/reopen_test.rb +22 -0
- data/test/test_helper.rb +23 -4
- data/test/text_width_test.rb +80 -0
- data/test/tmpfile_test.rb +1 -0
- data/test/validations_test.rb +47 -0
- data/test/worksheet_test.rb +129 -0
- metadata +34 -5
data/fast_excel.gemspec
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
Gem::Specification.new do |s|
|
|
2
2
|
s.name = "fast_excel"
|
|
3
|
-
s.version = "0.
|
|
3
|
+
s.version = "0.3.0"
|
|
4
4
|
s.author = ["Pavel Evstigneev"]
|
|
5
5
|
s.email = ["pavel.evst@gmail.com"]
|
|
6
6
|
s.homepage = "https://github.com/paxa/fast_excel"
|
|
7
|
-
s.summary = %q{Ultra Fast Excel
|
|
7
|
+
s.summary = %q{Ultra Fast Excel Writer}
|
|
8
8
|
s.description = "Wrapper for libxlsxwriter using ffi"
|
|
9
9
|
s.license = 'MIT'
|
|
10
|
-
s.has_rdoc = false
|
|
11
10
|
s.required_ruby_version = '~> 2.0'
|
|
12
11
|
|
|
13
12
|
s.files = `git ls-files`.split("\n")
|
data/letters.html
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<style>
|
|
2
|
+
html, body {
|
|
3
|
+
font-family: Arial;
|
|
4
|
+
}
|
|
5
|
+
#result {
|
|
6
|
+
font-size: 14px;
|
|
7
|
+
font-family: Arial;
|
|
8
|
+
white-space: pre;
|
|
9
|
+
}
|
|
10
|
+
</style>
|
|
11
|
+
|
|
12
|
+
<select onchange="generateFor(this.value)">
|
|
13
|
+
<option></option>
|
|
14
|
+
<option>Arial</option>
|
|
15
|
+
<option>Calibri</option>
|
|
16
|
+
<option>Times New Roman</option>
|
|
17
|
+
</select>
|
|
18
|
+
|
|
19
|
+
<pre id="result">
|
|
20
|
+
|
|
21
|
+
</pre>
|
|
22
|
+
|
|
23
|
+
<script>
|
|
24
|
+
function ready(fn){var d=document;(d.readyState=='loading')?d.addEventListener('DOMContentLoaded',fn):fn();}
|
|
25
|
+
|
|
26
|
+
let average = (array) => array.reduce((a, b) => a + b) / array.length;
|
|
27
|
+
|
|
28
|
+
window.widths = {};
|
|
29
|
+
window.combos = {};
|
|
30
|
+
window.characters = [];
|
|
31
|
+
|
|
32
|
+
for (var n = 32; n <= 126; n++) {
|
|
33
|
+
characters.push(String.fromCharCode(n));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function generateFor(fontFamily) {
|
|
37
|
+
window.widths = {};
|
|
38
|
+
window.combos = {};
|
|
39
|
+
|
|
40
|
+
var startTime = (new Date()).getTime();
|
|
41
|
+
|
|
42
|
+
var canvas = document.createElement('canvas').getContext('2d');
|
|
43
|
+
canvas.font = `normal 100px ${fontFamily}`;
|
|
44
|
+
canvas.textAlign = 'start';
|
|
45
|
+
canvas.textBaseline = 'alphabetic';
|
|
46
|
+
|
|
47
|
+
window.textWidth = function getWidth(value) {
|
|
48
|
+
return canvas.measureText(value).width;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
characters.forEach((char) => {
|
|
52
|
+
widths[char] = textWidth(char);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
characters.forEach((char1) => {
|
|
56
|
+
characters.forEach((char2) => {
|
|
57
|
+
var combined = textWidth(char1 + char2);
|
|
58
|
+
var expected = widths[char1] + widths[char2];
|
|
59
|
+
|
|
60
|
+
if (Math.abs(combined - expected) > 0.001) {
|
|
61
|
+
combos[ char1 + char2 ] = [combined, expected];
|
|
62
|
+
//console.log('delta', char1, char2, Math.abs(el.clientWidth - expected));
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
printOutput(startTime, fontFamily);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function printOutput(startTime, fontFamily) {
|
|
71
|
+
//document.querySelector('#result').innerText = JSON.stringify(widths, null, 4) + "\n" + JSON.stringify(combos, null, 4);
|
|
72
|
+
var fontPrefix = fontFamily.replace(/\s+/g, '_').toUpperCase();
|
|
73
|
+
var output = "";
|
|
74
|
+
|
|
75
|
+
var averageSize = average(Object.values(widths));
|
|
76
|
+
|
|
77
|
+
output += `static float ${fontPrefix}_DEFAULT = ${averageSize};\n`;
|
|
78
|
+
output += `static float ${fontPrefix}_CHAR_WIDTH[127] = {0`;
|
|
79
|
+
for (var n = 1; n <= 126; n++) {
|
|
80
|
+
output += `, ${widths[String.fromCharCode(n)] || '0' }`;
|
|
81
|
+
}
|
|
82
|
+
output += "};\n";
|
|
83
|
+
|
|
84
|
+
output += `static float ${fontPrefix}_KERNING[127][127] = {\n`;
|
|
85
|
+
for (var n1 = 0; n1 <= 126; n1++) {
|
|
86
|
+
var char1 = String.fromCharCode(n1);
|
|
87
|
+
var charDisplay = characters.includes(char1) ? char1 : '';
|
|
88
|
+
|
|
89
|
+
var prefix = `/* ${n1} : ${charDisplay} */`;
|
|
90
|
+
if (Object.keys(combos).some((pair) => { return pair[0] == char1 })) {
|
|
91
|
+
output += ` ${prefix} { 0`;
|
|
92
|
+
for (var n2 = 1; n2 <= 126; n2++) {
|
|
93
|
+
var char2 = String.fromCharCode(n2);
|
|
94
|
+
output += `, ${combos[char1 + char2] ? combos[char1 + char2][0] : '0' }`;
|
|
95
|
+
}
|
|
96
|
+
output += ` }${n1 == 126 ? '' : ','}\n`;
|
|
97
|
+
} else {
|
|
98
|
+
output += ` ${prefix} { 0 },\n`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
output += "};\n";
|
|
102
|
+
|
|
103
|
+
output += "\nKerning:\n";
|
|
104
|
+
Object.keys(combos).forEach((pair) => {
|
|
105
|
+
output += `${pair} = ${combos[pair][0]} (not ${combos[pair][1]})\n`
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
document.querySelector('#result').innerText = output;
|
|
109
|
+
|
|
110
|
+
var endTime = (new Date()).getTime();
|
|
111
|
+
//alert(`done ${endTime - startTime} ms`);
|
|
112
|
+
console.log(`done ${endTime - startTime} ms, ${Object.keys(combos).length} combos`);
|
|
113
|
+
}
|
|
114
|
+
</script>
|
data/lib/fast_excel.rb
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
require_relative './fast_excel/binding'
|
|
2
|
+
require 'set'
|
|
3
|
+
|
|
4
|
+
# not used for now
|
|
5
|
+
#require_relative '../ext/fast_excel/text_width_ext'
|
|
2
6
|
|
|
3
7
|
module FastExcel
|
|
4
8
|
|
|
@@ -9,20 +13,27 @@ module FastExcel
|
|
|
9
13
|
end
|
|
10
14
|
end
|
|
11
15
|
|
|
16
|
+
class URL
|
|
17
|
+
attr_accessor :url
|
|
18
|
+
def initialize(url)
|
|
19
|
+
@url = url
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
12
23
|
DEF_COL_WIDTH = 8.43
|
|
13
24
|
|
|
14
25
|
def self.open(filename = nil, constant_memory: false, default_format: nil)
|
|
15
26
|
tmp_file = false
|
|
16
|
-
|
|
27
|
+
if filename
|
|
28
|
+
if File.exist?(filename) && File.size(filename) > 0
|
|
29
|
+
raise ArgumentError, "File '#{filename}' already exists. FastExcel can not open existing files, only create new files"
|
|
30
|
+
end
|
|
31
|
+
else
|
|
17
32
|
require 'tmpdir'
|
|
18
33
|
filename = "#{Dir.mktmpdir}/fast_excel.xlsx"
|
|
19
34
|
tmp_file = true
|
|
20
35
|
end
|
|
21
36
|
|
|
22
|
-
unless filename
|
|
23
|
-
raise ArgumentError, "filename is required"
|
|
24
|
-
end
|
|
25
|
-
|
|
26
37
|
filename = filename.to_s if defined?(Pathname) && filename.is_a?(Pathname)
|
|
27
38
|
|
|
28
39
|
workbook = if constant_memory
|
|
@@ -110,15 +121,197 @@ module FastExcel
|
|
|
110
121
|
nil
|
|
111
122
|
end
|
|
112
123
|
|
|
124
|
+
|
|
125
|
+
COLOR_ENUM = Libxlsxwriter.enum_type(:defined_colors)
|
|
126
|
+
EXTRA_COLORS = {
|
|
127
|
+
alice_blue: 0xF0F8FF,
|
|
128
|
+
antique_white: 0xFAEBD7,
|
|
129
|
+
aqua: 0x00FFFF,
|
|
130
|
+
aquamarine: 0x7FFFD4,
|
|
131
|
+
azure: 0xF0FFFF,
|
|
132
|
+
beige: 0xF5F5DC,
|
|
133
|
+
bisque: 0xFFE4C4,
|
|
134
|
+
black: 0x000000,
|
|
135
|
+
blanched_almond: 0xFFEBCD,
|
|
136
|
+
blue: 0x0000FF,
|
|
137
|
+
blue_violet: 0x8A2BE2,
|
|
138
|
+
brown: 0xA52A2A,
|
|
139
|
+
burly_wood: 0xDEB887,
|
|
140
|
+
cadet_blue: 0x5F9EA0,
|
|
141
|
+
chartreuse: 0x7FFF00,
|
|
142
|
+
chocolate: 0xD2691E,
|
|
143
|
+
coral: 0xFF7F50,
|
|
144
|
+
cornflower_blue: 0x6495ED,
|
|
145
|
+
cornsilk: 0xFFF8DC,
|
|
146
|
+
crimson: 0xDC143C,
|
|
147
|
+
cyan: 0x00FFFF,
|
|
148
|
+
dark_blue: 0x00008B,
|
|
149
|
+
dark_cyan: 0x008B8B,
|
|
150
|
+
dark_golden_rod: 0xB8860B,
|
|
151
|
+
dark_gray: 0xA9A9A9,
|
|
152
|
+
dark_grey: 0xA9A9A9,
|
|
153
|
+
dark_green: 0x006400,
|
|
154
|
+
dark_khaki: 0xBDB76B,
|
|
155
|
+
dark_magenta: 0x8B008B,
|
|
156
|
+
dark_olive_green: 0x556B2F,
|
|
157
|
+
dark_orange: 0xFF8C00,
|
|
158
|
+
dark_orchid: 0x9932CC,
|
|
159
|
+
dark_red: 0x8B0000,
|
|
160
|
+
dark_salmon: 0xE9967A,
|
|
161
|
+
dark_sea_green: 0x8FBC8F,
|
|
162
|
+
dark_slate_blue: 0x483D8B,
|
|
163
|
+
dark_slate_gray: 0x2F4F4F,
|
|
164
|
+
dark_slate_grey: 0x2F4F4F,
|
|
165
|
+
dark_turquoise: 0x00CED1,
|
|
166
|
+
dark_violet: 0x9400D3,
|
|
167
|
+
deep_pink: 0xFF1493,
|
|
168
|
+
deep_sky_blue: 0x00BFFF,
|
|
169
|
+
dim_gray: 0x696969,
|
|
170
|
+
dim_grey: 0x696969,
|
|
171
|
+
dodger_blue: 0x1E90FF,
|
|
172
|
+
fire_brick: 0xB22222,
|
|
173
|
+
floral_white: 0xFFFAF0,
|
|
174
|
+
forest_green: 0x228B22,
|
|
175
|
+
fuchsia: 0xFF00FF,
|
|
176
|
+
gainsboro: 0xDCDCDC,
|
|
177
|
+
ghost_white: 0xF8F8FF,
|
|
178
|
+
gold: 0xFFD700,
|
|
179
|
+
golden_rod: 0xDAA520,
|
|
180
|
+
gray: 0x808080,
|
|
181
|
+
grey: 0x808080,
|
|
182
|
+
green: 0x008000,
|
|
183
|
+
green_yellow: 0xADFF2F,
|
|
184
|
+
honey_dew: 0xF0FFF0,
|
|
185
|
+
hot_pink: 0xFF69B4,
|
|
186
|
+
indian_red: 0xCD5C5C,
|
|
187
|
+
indigo: 0x4B0082,
|
|
188
|
+
ivory: 0xFFFFF0,
|
|
189
|
+
khaki: 0xF0E68C,
|
|
190
|
+
lavender: 0xE6E6FA,
|
|
191
|
+
lavender_blush: 0xFFF0F5,
|
|
192
|
+
lawn_green: 0x7CFC00,
|
|
193
|
+
lemon_chiffon: 0xFFFACD,
|
|
194
|
+
light_blue: 0xADD8E6,
|
|
195
|
+
light_coral: 0xF08080,
|
|
196
|
+
light_cyan: 0xE0FFFF,
|
|
197
|
+
light_golden_rod_yellow: 0xFAFAD2,
|
|
198
|
+
light_gray: 0xD3D3D3,
|
|
199
|
+
light_grey: 0xD3D3D3,
|
|
200
|
+
light_green: 0x90EE90,
|
|
201
|
+
light_pink: 0xFFB6C1,
|
|
202
|
+
light_salmon: 0xFFA07A,
|
|
203
|
+
light_sea_green: 0x20B2AA,
|
|
204
|
+
light_sky_blue: 0x87CEFA,
|
|
205
|
+
light_slate_gray: 0x778899,
|
|
206
|
+
light_slate_grey: 0x778899,
|
|
207
|
+
light_steel_blue: 0xB0C4DE,
|
|
208
|
+
light_yellow: 0xFFFFE0,
|
|
209
|
+
lime: 0x00FF00,
|
|
210
|
+
lime_green: 0x32CD32,
|
|
211
|
+
linen: 0xFAF0E6,
|
|
212
|
+
magenta: 0xFF00FF,
|
|
213
|
+
maroon: 0x800000,
|
|
214
|
+
medium_aqua_marine: 0x66CDAA,
|
|
215
|
+
medium_blue: 0x0000CD,
|
|
216
|
+
medium_orchid: 0xBA55D3,
|
|
217
|
+
medium_purple: 0x9370DB,
|
|
218
|
+
medium_sea_green: 0x3CB371,
|
|
219
|
+
medium_slate_blue: 0x7B68EE,
|
|
220
|
+
medium_spring_green: 0x00FA9A,
|
|
221
|
+
medium_turquoise: 0x48D1CC,
|
|
222
|
+
medium_violet_red: 0xC71585,
|
|
223
|
+
midnight_blue: 0x191970,
|
|
224
|
+
mint_cream: 0xF5FFFA,
|
|
225
|
+
misty_rose: 0xFFE4E1,
|
|
226
|
+
moccasin: 0xFFE4B5,
|
|
227
|
+
navajo_white: 0xFFDEAD,
|
|
228
|
+
navy: 0x000080,
|
|
229
|
+
old_lace: 0xFDF5E6,
|
|
230
|
+
olive: 0x808000,
|
|
231
|
+
olive_drab: 0x6B8E23,
|
|
232
|
+
orange: 0xFFA500,
|
|
233
|
+
orange_red: 0xFF4500,
|
|
234
|
+
orchid: 0xDA70D6,
|
|
235
|
+
pale_golden_rod: 0xEEE8AA,
|
|
236
|
+
pale_green: 0x98FB98,
|
|
237
|
+
pale_turquoise: 0xAFEEEE,
|
|
238
|
+
pale_violet_red: 0xDB7093,
|
|
239
|
+
papaya_whip: 0xFFEFD5,
|
|
240
|
+
peach_puff: 0xFFDAB9,
|
|
241
|
+
peru: 0xCD853F,
|
|
242
|
+
pink: 0xFFC0CB,
|
|
243
|
+
plum: 0xDDA0DD,
|
|
244
|
+
powder_blue: 0xB0E0E6,
|
|
245
|
+
purple: 0x800080,
|
|
246
|
+
rebecca_purple: 0x663399,
|
|
247
|
+
red: 0xFF0000,
|
|
248
|
+
rosy_brown: 0xBC8F8F,
|
|
249
|
+
royal_blue: 0x4169E1,
|
|
250
|
+
saddle_brown: 0x8B4513,
|
|
251
|
+
salmon: 0xFA8072,
|
|
252
|
+
sandy_brown: 0xF4A460,
|
|
253
|
+
sea_green: 0x2E8B57,
|
|
254
|
+
sea_shell: 0xFFF5EE,
|
|
255
|
+
sienna: 0xA0522D,
|
|
256
|
+
silver: 0xC0C0C0,
|
|
257
|
+
sky_blue: 0x87CEEB,
|
|
258
|
+
slate_blue: 0x6A5ACD,
|
|
259
|
+
slate_gray: 0x708090,
|
|
260
|
+
slate_grey: 0x708090,
|
|
261
|
+
snow: 0xFFFAFA,
|
|
262
|
+
spring_green: 0x00FF7F,
|
|
263
|
+
steel_blue: 0x4682B4,
|
|
264
|
+
tan: 0xD2B48C,
|
|
265
|
+
teal: 0x008080,
|
|
266
|
+
thistle: 0xD8BFD8,
|
|
267
|
+
tomato: 0xFF6347,
|
|
268
|
+
turquoise: 0x40E0D0,
|
|
269
|
+
violet: 0xEE82EE,
|
|
270
|
+
wheat: 0xF5DEB3,
|
|
271
|
+
white: 0xFFFFFF,
|
|
272
|
+
white_smoke: 0xF5F5F5,
|
|
273
|
+
yellow: 0xFFFF00,
|
|
274
|
+
yellow_green: 0x9ACD32
|
|
275
|
+
}.freeze
|
|
276
|
+
|
|
277
|
+
# Convert hex string, color name or hex number to color hex number
|
|
278
|
+
def self.color_to_hex(value)
|
|
279
|
+
orig_value = value
|
|
280
|
+
value = value.to_s if value.is_a?(Symbol)
|
|
281
|
+
|
|
282
|
+
if value.is_a?(String)
|
|
283
|
+
if EXTRA_COLORS[value.to_sym]
|
|
284
|
+
return EXTRA_COLORS[value.to_sym]
|
|
285
|
+
elsif COLOR_ENUM.find(value.to_sym)
|
|
286
|
+
return COLOR_ENUM.find(value.to_sym)
|
|
287
|
+
elsif COLOR_ENUM.find("color_#{value.to_sym}")
|
|
288
|
+
return COLOR_ENUM.find("color_#{value.to_sym}")
|
|
289
|
+
elsif value =~ /^#?(0x)?([\da-f]){6}$/i
|
|
290
|
+
value = value.sub('#', '') if value.start_with?('#')
|
|
291
|
+
return value.start_with?('0x') ? value.to_i(16) : "0x#{value}".to_i(16)
|
|
292
|
+
else
|
|
293
|
+
raise ArgumentError, "Unknown color value #{orig_value.inspect}, expected hex string or color name"
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
return value if value.is_a?(Numeric)
|
|
298
|
+
|
|
299
|
+
raise ArgumentError, "Can not use #{value.class} (#{value.inspect}) for color value, expected String or Hex Number"
|
|
300
|
+
end
|
|
301
|
+
|
|
113
302
|
module AttributeHelper
|
|
114
303
|
def set(values)
|
|
115
304
|
values.each do |key, value|
|
|
116
305
|
if respond_to?("#{key}=")
|
|
117
306
|
send("#{key}=", value)
|
|
307
|
+
elsif respond_to?("set_#{key}=")
|
|
308
|
+
send("set_#{key}=", value)
|
|
118
309
|
else
|
|
119
310
|
self[key] = value
|
|
120
311
|
end
|
|
121
312
|
end
|
|
313
|
+
|
|
314
|
+
self
|
|
122
315
|
end
|
|
123
316
|
|
|
124
317
|
def fields_hash
|
|
@@ -141,15 +334,25 @@ module FastExcel
|
|
|
141
334
|
|
|
142
335
|
def initialize(struct)
|
|
143
336
|
@is_open = true
|
|
337
|
+
@sheet_names = Set.new
|
|
338
|
+
@sheets = []
|
|
144
339
|
super(struct)
|
|
145
340
|
end
|
|
146
341
|
|
|
342
|
+
def add_format(options = nil)
|
|
343
|
+
new_format = super()
|
|
344
|
+
new_format.set(options) if options
|
|
345
|
+
new_format
|
|
346
|
+
end
|
|
347
|
+
|
|
147
348
|
def bold_cell_format
|
|
148
349
|
bold = add_format
|
|
149
350
|
bold.set_bold
|
|
150
351
|
bold
|
|
151
352
|
end
|
|
152
353
|
|
|
354
|
+
alias_method :bold_format, :bold_cell_format
|
|
355
|
+
|
|
153
356
|
# "#,##0.00"
|
|
154
357
|
# "[$-409]m/d/yy h:mm AM/PM;@"
|
|
155
358
|
def number_format(pattern)
|
|
@@ -159,11 +362,31 @@ module FastExcel
|
|
|
159
362
|
end
|
|
160
363
|
|
|
161
364
|
def add_worksheet(sheetname = nil)
|
|
162
|
-
|
|
365
|
+
if !sheetname.nil?
|
|
366
|
+
if sheetname.length > Libxlsxwriter::SHEETNAME_MAX
|
|
367
|
+
raise ArgumentError, "Worksheet name '#{sheetname}' exceeds Excel's limit of #{Libxlsxwriter::SHEETNAME_MAX} characters"
|
|
368
|
+
elsif @sheet_names.include?(sheetname)
|
|
369
|
+
raise ArgumentError, "Worksheet name '#{sheetname}' is already in use"
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
sheet = super(sheetname)
|
|
374
|
+
sheet.workbook = self
|
|
375
|
+
@sheets << sheet
|
|
376
|
+
@sheet_names << sheet[:name]
|
|
377
|
+
sheet
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def get_worksheet_by_name(name)
|
|
381
|
+
sheet = super(name)
|
|
382
|
+
sheet.workbook = self
|
|
383
|
+
|
|
384
|
+
sheet
|
|
163
385
|
end
|
|
164
386
|
|
|
165
387
|
def close
|
|
166
388
|
@is_open = false
|
|
389
|
+
@sheets.each(&:close)
|
|
167
390
|
super
|
|
168
391
|
end
|
|
169
392
|
|
|
@@ -171,45 +394,159 @@ module FastExcel
|
|
|
171
394
|
close if @is_open
|
|
172
395
|
File.open(filename, 'rb', &:read)
|
|
173
396
|
ensure
|
|
174
|
-
|
|
397
|
+
remove_tmp_folder
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def remove_tmp_folder
|
|
401
|
+
FileUtils.remove_entry(File.dirname(filename)) if tmp_file
|
|
175
402
|
end
|
|
176
403
|
|
|
177
|
-
def
|
|
178
|
-
|
|
404
|
+
def constant_memory?
|
|
405
|
+
#FastExcel.print_ffi_obj(self[:options])
|
|
406
|
+
@constant_memory ||= self[:options][:constant_memory] != 0
|
|
179
407
|
end
|
|
180
408
|
end
|
|
181
409
|
|
|
182
410
|
module WorksheetExt
|
|
411
|
+
attr_accessor :workbook
|
|
412
|
+
|
|
183
413
|
include AttributeHelper
|
|
184
414
|
|
|
415
|
+
def initialize(struct)
|
|
416
|
+
@is_open = true
|
|
417
|
+
@col_formats = {}
|
|
418
|
+
@last_row_number = -1
|
|
419
|
+
super(struct)
|
|
420
|
+
end
|
|
421
|
+
|
|
185
422
|
def write_row(row_number, values, formats = nil)
|
|
186
423
|
values.each_with_index do |value, index|
|
|
187
424
|
format = if formats
|
|
188
425
|
formats.is_a?(Array) ? formats[index] : formats
|
|
189
426
|
end
|
|
190
427
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
428
|
+
write_value(row_number, index, value, format)
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def auto_width?
|
|
433
|
+
defined?(@auto_width) && @auto_width
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def auto_width=(v)
|
|
437
|
+
@auto_width = v
|
|
438
|
+
@column_widths = {}
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
def calculated_column_widths
|
|
442
|
+
@column_widths || {}
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def write_value(row_number, cell_number, value, format = nil)
|
|
446
|
+
|
|
447
|
+
if workbook.constant_memory? && row_number < @last_row_number
|
|
448
|
+
raise ArgumentError, "Can not write to saved row in constant_memory mode (attempted row: #{row_number}, last saved row: #{last_row_number})"
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
if value.is_a?(Numeric)
|
|
452
|
+
write_number(row_number, cell_number, value, format)
|
|
453
|
+
elsif defined?(Date) && value.is_a?(Date)
|
|
454
|
+
write_datetime(row_number, cell_number, FastExcel.lxw_datetime(value.to_datetime), format)
|
|
455
|
+
elsif value.is_a?(Time)
|
|
456
|
+
write_number(row_number, cell_number, FastExcel.date_num(value), format)
|
|
457
|
+
elsif defined?(DateTime) && value.is_a?(DateTime)
|
|
458
|
+
write_number(row_number, cell_number, FastExcel.date_num(value), format)
|
|
459
|
+
elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
|
460
|
+
write_boolean(row_number, cell_number, value ? 1 : 0, format)
|
|
461
|
+
elsif value.is_a?(FastExcel::Formula)
|
|
462
|
+
write_formula(row_number, cell_number, value.fml, format)
|
|
463
|
+
elsif value.is_a?(FastExcel::URL)
|
|
464
|
+
write_url(row_number, cell_number, value.url, format)
|
|
465
|
+
add_text_width(value.url, format, cell_number) if auto_width?
|
|
466
|
+
else
|
|
467
|
+
write_string(row_number, cell_number, value.to_s, format)
|
|
468
|
+
add_text_width(value, format, cell_number) if auto_width?
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
@last_row_number = row_number > @last_row_number ? row_number : @last_row_number
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
def add_text_width(value, format, cell_number)
|
|
475
|
+
font_size = 0
|
|
476
|
+
if format
|
|
477
|
+
font_size = format.font_size
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
if font_size == 0
|
|
481
|
+
if @col_formats[cell_number] && @col_formats[cell_number].font_size
|
|
482
|
+
font_size = @col_formats[cell_number].font_size
|
|
203
483
|
end
|
|
204
484
|
end
|
|
485
|
+
|
|
486
|
+
if font_size == 0
|
|
487
|
+
font_size = workbook.default_format.font_size
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
font_size = 13 if font_size == nil || font_size == 0
|
|
491
|
+
|
|
492
|
+
scale = 0.08
|
|
493
|
+
new_width = (scale * font_size * value.to_s.length )
|
|
494
|
+
@column_widths[cell_number] = if new_width > (@column_widths[cell_number] || 0)
|
|
495
|
+
new_width
|
|
496
|
+
else
|
|
497
|
+
@column_widths[cell_number]
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
def append_row(values, formats = nil)
|
|
502
|
+
@last_row_number += 1
|
|
503
|
+
write_row(last_row_number, values, formats)
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
def <<(values)
|
|
507
|
+
append_row(values)
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
def last_row_number
|
|
511
|
+
@last_row_number
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def set_column(start_col, end_col, width = nil, format = nil)
|
|
515
|
+
super(start_col, end_col, width || DEF_COL_WIDTH, format)
|
|
516
|
+
|
|
517
|
+
return unless format
|
|
518
|
+
start_col.upto(end_col) do |i|
|
|
519
|
+
@col_formats[i] = format
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
def set_column_width(col, width)
|
|
524
|
+
set_column(col, col, width, @col_formats[col])
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
def set_columns_width(start_col, end_col, width)
|
|
528
|
+
start_col.upto(end_col) do |i|
|
|
529
|
+
set_column_width(i, width)
|
|
530
|
+
end
|
|
205
531
|
end
|
|
206
532
|
|
|
533
|
+
def enable_filters!(start_col: 0, end_col:)
|
|
534
|
+
autofilter(start_col, 0, @last_row_number, end_col)
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
def close
|
|
538
|
+
if auto_width?
|
|
539
|
+
@column_widths.each do |num, width|
|
|
540
|
+
set_column_width(num, width + 0.2)
|
|
541
|
+
end
|
|
542
|
+
end
|
|
543
|
+
end
|
|
207
544
|
end
|
|
208
545
|
|
|
209
546
|
module FormatExt
|
|
210
547
|
include AttributeHelper
|
|
211
548
|
|
|
212
|
-
[:font_size, :underline, :font_script, :
|
|
549
|
+
[:font_size, :underline, :font_script, :rotation, :indent, :pattern, :border].each do |prop|
|
|
213
550
|
define_method(prop) do
|
|
214
551
|
self[prop]
|
|
215
552
|
end
|
|
@@ -237,73 +574,121 @@ module FastExcel
|
|
|
237
574
|
end
|
|
238
575
|
end
|
|
239
576
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
577
|
+
ALIGN_ENUM = Libxlsxwriter.enum_type(:format_alignments)
|
|
578
|
+
|
|
579
|
+
# Can be called as:
|
|
580
|
+
#
|
|
581
|
+
# format.align = :align_center
|
|
582
|
+
# format.align = "align_center"
|
|
583
|
+
# format.align = :center
|
|
584
|
+
# format.align = :align_center
|
|
585
|
+
# format.align = {v: "center", h: "center"}
|
|
586
|
+
#
|
|
587
|
+
# Possible values:
|
|
588
|
+
#
|
|
589
|
+
# :align_none, :align_left, :align_center, :align_right, :align_fill, :align_justify,
|
|
590
|
+
# :align_center_across, :align_distributed, :align_vertical_top, :align_vertical_bottom,
|
|
591
|
+
# :align_vertical_center, :align_vertical_justify, :align_vertical_distributed
|
|
592
|
+
#
|
|
593
|
+
def align=(value)
|
|
594
|
+
value = value.to_sym if value.is_a?(String)
|
|
595
|
+
|
|
596
|
+
if value.is_a?(Symbol)
|
|
597
|
+
if ALIGN_ENUM.find(value)
|
|
598
|
+
set_align(value)
|
|
599
|
+
elsif ALIGN_ENUM.find(prefixed = "align_#{value}".to_sym)
|
|
600
|
+
set_align(prefixed)
|
|
601
|
+
else
|
|
602
|
+
raise ArgumentError, "Can not set align = #{value.inspect}, possible values are: #{ALIGN_ENUM.symbols}"
|
|
603
|
+
end
|
|
604
|
+
elsif value.is_a?(Hash)
|
|
605
|
+
if value[:horizontal]
|
|
606
|
+
self.align = "align_#{value[:horizontal].to_s.sub(/^align_/, '')}".to_sym
|
|
607
|
+
end
|
|
608
|
+
if value[:h]
|
|
609
|
+
self.align = "align_#{value[:h].to_s.sub(/^align_/, '')}".to_sym
|
|
610
|
+
end
|
|
611
|
+
if value[:vertical]
|
|
612
|
+
self.align = "align_vertical_#{value[:vertical].to_s.sub(/^align_vertical_/, '')}".to_sym
|
|
613
|
+
end
|
|
614
|
+
if value[:v]
|
|
615
|
+
self.align = "align_vertical_#{value[:v].to_s.sub(/^align_vertical_/, '')}".to_sym
|
|
616
|
+
end
|
|
617
|
+
possible = [:horizontal, :h, :vertical, :v]
|
|
618
|
+
extras = value.keys - possible
|
|
619
|
+
if extras.size > 0
|
|
620
|
+
raise ArgumentError, "Not allowed keys for align: #{extras.inspect}, possible keys: #{possible.inspect}"
|
|
621
|
+
end
|
|
622
|
+
else
|
|
623
|
+
raise ArgumentError, "value must be a symbol or a hash"
|
|
243
624
|
end
|
|
244
|
-
super(value)
|
|
245
625
|
end
|
|
246
626
|
|
|
247
|
-
def
|
|
248
|
-
|
|
627
|
+
def align
|
|
628
|
+
{
|
|
629
|
+
horizontal: ALIGN_ENUM.find(self[:text_h_align]),
|
|
630
|
+
vertical: ALIGN_ENUM.find(self[:text_v_align])
|
|
631
|
+
}
|
|
249
632
|
end
|
|
250
633
|
|
|
251
|
-
|
|
252
|
-
|
|
634
|
+
[:font_color, :bg_color, :fg_color, :bottom_color, :diag_color, :left_color, :right_color, :top_color].each do |prop|
|
|
635
|
+
define_method("#{prop}=") do |value|
|
|
636
|
+
send("set_#{prop}", FastExcel.color_to_hex(value))
|
|
637
|
+
end
|
|
638
|
+
define_method(prop) do
|
|
639
|
+
self[prop]
|
|
640
|
+
end
|
|
253
641
|
end
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
module RowExt
|
|
257
|
-
include AttributeHelper
|
|
258
642
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
end
|
|
263
|
-
"<Libxlsxwriter::Row #{attr_str.join(" ")}>"
|
|
643
|
+
[:bottom_color, :left_color, :right_color, :top_color].each do |prop|
|
|
644
|
+
alias_method :"border_#{prop}=", :"#{prop}="
|
|
645
|
+
alias_method :"border_#{prop}", :"#{prop}"
|
|
264
646
|
end
|
|
265
|
-
end
|
|
266
647
|
|
|
267
|
-
|
|
268
|
-
include AttributeHelper
|
|
648
|
+
BORDER_ENUM = Libxlsxwriter.enum_type(:format_borders)
|
|
269
649
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
else
|
|
278
|
-
self[:user_data1]
|
|
650
|
+
[:bottom, :diag_border, :left, :right, :top].each do |prop|
|
|
651
|
+
define_method("#{prop}=") do |value|
|
|
652
|
+
|
|
653
|
+
send("set_#{prop}", border_value(value))
|
|
654
|
+
end
|
|
655
|
+
define_method(prop) do
|
|
656
|
+
BORDER_ENUM.find(self[prop])
|
|
279
657
|
end
|
|
280
|
-
end
|
|
281
658
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
659
|
+
unless prop == :diag_border
|
|
660
|
+
alias_method :"border_#{prop}=", :"#{prop}="
|
|
661
|
+
alias_method :"border_#{prop}", :"#{prop}"
|
|
662
|
+
end
|
|
285
663
|
end
|
|
286
664
|
|
|
287
|
-
def
|
|
288
|
-
|
|
665
|
+
def border_value(value)
|
|
666
|
+
# if a number
|
|
667
|
+
return value if value.is_a?(Numeric) && BORDER_ENUM.find(value)
|
|
668
|
+
|
|
669
|
+
orig_value = value
|
|
670
|
+
value = value.to_sym if value.is_a?(String)
|
|
671
|
+
|
|
672
|
+
return BORDER_ENUM.find(value) if BORDER_ENUM.find(value)
|
|
673
|
+
return BORDER_ENUM.find(:"border_#{value}") if BORDER_ENUM.find(:"border_#{value}")
|
|
674
|
+
|
|
675
|
+
short_symbols = BORDER_ENUM.symbols.map {|s| s.to_s.sub(/^border_/, '').to_sym }
|
|
676
|
+
raise ArgumentError, "Unknown value #{orig_value.inspect} for border. Possible values: #{short_symbols}"
|
|
289
677
|
end
|
|
290
678
|
|
|
291
|
-
def
|
|
292
|
-
|
|
679
|
+
def set_font_size(value)
|
|
680
|
+
if value < 0
|
|
681
|
+
raise ArgumentError, "font size should be >= 0 (use 0 for user default font size)"
|
|
682
|
+
end
|
|
683
|
+
super(value)
|
|
293
684
|
end
|
|
294
685
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
# end
|
|
299
|
-
# "<Libxlsxwriter::Row #{attr_str.join(" ")}>"
|
|
300
|
-
#end
|
|
686
|
+
def font_family
|
|
687
|
+
font_name
|
|
688
|
+
end
|
|
301
689
|
|
|
302
|
-
def
|
|
303
|
-
|
|
304
|
-
"@#{key}=#{val.inspect}"
|
|
305
|
-
end
|
|
306
|
-
"<Libxlsxwriter::Row #{attr_str.join(" ")}>"
|
|
690
|
+
def font_family=(value)
|
|
691
|
+
self.font_name = value
|
|
307
692
|
end
|
|
308
693
|
end
|
|
309
694
|
end
|
|
@@ -319,11 +704,3 @@ end
|
|
|
319
704
|
Libxlsxwriter::Worksheet.instance_eval do
|
|
320
705
|
include FastExcel::WorksheetExt
|
|
321
706
|
end
|
|
322
|
-
|
|
323
|
-
Libxlsxwriter::Row.instance_eval do
|
|
324
|
-
include FastExcel::RowExt
|
|
325
|
-
end
|
|
326
|
-
|
|
327
|
-
Libxlsxwriter::Cell.instance_eval do
|
|
328
|
-
include FastExcel::CellExt
|
|
329
|
-
end
|