fast_xlsx 0.1.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 +7 -0
- data/CHANGELOG.md +34 -0
- data/Cargo.lock +517 -0
- data/Cargo.toml +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +298 -0
- data/Rakefile +22 -0
- data/bench/Gemfile +14 -0
- data/bench/Gemfile.lock +110 -0
- data/bench/compare.rb +113 -0
- data/bench/memory.rb +43 -0
- data/bench/write.rb +53 -0
- data/examples/showcase.rb +146 -0
- data/ext/fast_xlsx/Cargo.toml +18 -0
- data/ext/fast_xlsx/build.rs +5 -0
- data/ext/fast_xlsx/extconf.rb +6 -0
- data/ext/fast_xlsx/src/lib.rs +1339 -0
- data/lib/fast_xlsx/version.rb +5 -0
- data/lib/fast_xlsx.rb +205 -0
- data/sig/fast_xlsx.rbs +100 -0
- metadata +80 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Builds one workbook with a worksheet per feature, to check the output in
|
|
4
|
+
# Excel (or LibreOffice / Numbers) before a release.
|
|
5
|
+
#
|
|
6
|
+
# bundle exec rake compile
|
|
7
|
+
# ruby -Ilib examples/showcase.rb [showcase.xlsx]
|
|
8
|
+
require "date"
|
|
9
|
+
require "stringio"
|
|
10
|
+
require "zlib"
|
|
11
|
+
require "fast_xlsx"
|
|
12
|
+
|
|
13
|
+
path = ARGV[0] || "showcase.xlsx"
|
|
14
|
+
|
|
15
|
+
# A small gradient PNG, generated so the example needs no image file.
|
|
16
|
+
def gradient_png(width, height)
|
|
17
|
+
rows = (0...height).map do |y|
|
|
18
|
+
"\0".b + (0...width).map { |x| [x * 255 / width, y * 255 / height, 200].pack("C3") }.join
|
|
19
|
+
end
|
|
20
|
+
chunk = ->(type, data) { [data.bytesize].pack("N") + type + data + [Zlib.crc32(type + data)].pack("N") }
|
|
21
|
+
"\x89PNG\r\n\x1A\n".b +
|
|
22
|
+
chunk.call("IHDR", [width, height, 8, 2, 0, 0, 0].pack("NNC5")) +
|
|
23
|
+
chunk.call("IDAT", Zlib::Deflate.deflate(rows.join)) +
|
|
24
|
+
chunk.call("IEND", "".b)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
wb = FastXlsx::Workbook.new
|
|
28
|
+
wb.properties(title: "fast_xlsx showcase", author: "fast_xlsx", keywords: "example")
|
|
29
|
+
|
|
30
|
+
bold = FastXlsx::Format.new(bold: true)
|
|
31
|
+
header = FastXlsx::Format.new(bold: true, bg_color: "#DDEBF7", border_bottom: :thin, align: :center)
|
|
32
|
+
money = FastXlsx::Format.new(num_format: "#,##0.00")
|
|
33
|
+
date = FastXlsx::Format.new(num_format: "yyyy-mm-dd")
|
|
34
|
+
datetime = FastXlsx::Format.new(num_format: "yyyy-mm-dd hh:mm")
|
|
35
|
+
|
|
36
|
+
# Values: every cell type.
|
|
37
|
+
ws = wb.add_worksheet("Values")
|
|
38
|
+
ws.column_width(0, 22).column_width(1, 40)
|
|
39
|
+
ws.append(%w[Type Value], format: header)
|
|
40
|
+
ws << ["Integer", 42]
|
|
41
|
+
ws << ["Float", 3.14159]
|
|
42
|
+
ws.append(["Money format", 1_234_567.891], format: [nil, money])
|
|
43
|
+
ws.append(["Date", Date.new(2024, 2, 29)], format: [nil, date])
|
|
44
|
+
ws.append(["Time", Time.new(2024, 2, 29, 13, 45)], format: [nil, datetime])
|
|
45
|
+
ws << ["Boolean", true]
|
|
46
|
+
ws << ["nil (empty cell)", nil]
|
|
47
|
+
ws << ["Formula =SUM(B2:B3)", FastXlsx::Formula.new("SUM(B2:B3)")]
|
|
48
|
+
ws << ["URL", FastXlsx::URL.new("https://github.com/7a6163/fast_xlsx")]
|
|
49
|
+
ws << ["URL with text", FastXlsx::URL.new("https://github.com/7a6163/fast_xlsx", text: "fast_xlsx on GitHub")]
|
|
50
|
+
ws << ["Rich string",
|
|
51
|
+
FastXlsx::RichString.new(["Bold ", bold], "plain ", ["italic", FastXlsx::Format.new(italic: true)])]
|
|
52
|
+
ws << ["Symbol (to_s)", :symbol]
|
|
53
|
+
|
|
54
|
+
# Formats: one option per row.
|
|
55
|
+
ws = wb.add_worksheet("Formats")
|
|
56
|
+
ws.column_width(0, 24).column_width(1, 30)
|
|
57
|
+
ws.append(%w[Option Sample], format: header)
|
|
58
|
+
{
|
|
59
|
+
"bold" => { bold: true }, "italic" => { italic: true }, "strikeout" => { strikeout: true },
|
|
60
|
+
"underline :double" => { underline: :double }, "font_script :superscript" => { font_script: :superscript },
|
|
61
|
+
"font_size 16" => { font_size: 16 }, "font_name Courier New" => { font_name: "Courier New" },
|
|
62
|
+
"font_color #C00000" => { font_color: "#C00000" }, "bg_color #FFF2CC" => { bg_color: "#FFF2CC" },
|
|
63
|
+
"align :right" => { align: :right }, "valign :top" => { valign: :top }, "indent 2" => { indent: 2 },
|
|
64
|
+
"rotation 45" => { rotation: 45 }, "text_wrap" => { text_wrap: true }, "shrink" => { shrink: true },
|
|
65
|
+
"border :medium + color" => { border: :medium, border_color: "#2F5597" },
|
|
66
|
+
"border_bottom :double" => { border_bottom: :double }
|
|
67
|
+
}.each do |label, options|
|
|
68
|
+
text = label == "text_wrap" ? "long text that wraps inside the cell width" : "Sample text"
|
|
69
|
+
ws.append([label, text], format: [nil, FastXlsx::Format.new(**options)])
|
|
70
|
+
end
|
|
71
|
+
ws.row_height(14, 40)
|
|
72
|
+
|
|
73
|
+
# Layout: widths, autofit, merged title, frozen header, filter.
|
|
74
|
+
ws = wb.add_worksheet("Layout")
|
|
75
|
+
ws.merge_range(0, 0, 0, 3, "Merged title across A1:D1", FastXlsx::Format.new(bold: true, font_size: 14, align: :center))
|
|
76
|
+
ws.append(%w[Region Rep Amount Note], format: header)
|
|
77
|
+
20.times { |i| ws << [%w[North South East West][i % 4], "Rep #{i + 1}", (i + 1) * 125.5, "row #{i + 1}"] }
|
|
78
|
+
ws.column_format(2, money)
|
|
79
|
+
ws.column_width(3, 30) # kept by autofit
|
|
80
|
+
ws.autofit
|
|
81
|
+
ws.freeze_panes(2, 0)
|
|
82
|
+
ws.autofilter(1, 0, 21, 3)
|
|
83
|
+
|
|
84
|
+
# Conditional formats.
|
|
85
|
+
ws = wb.add_worksheet("Conditional")
|
|
86
|
+
red = FastXlsx::Format.new(font_color: "#9C0006", bg_color: "#FFC7CE")
|
|
87
|
+
green = FastXlsx::Format.new(font_color: "#006100", bg_color: "#C6EFCE")
|
|
88
|
+
ws.append(["Cell < 0", "Text contains 'error'", "Data bar", "Color scale", "Formula (A > 50)"], format: header)
|
|
89
|
+
values = [-30, 80, 15, -5, 60, 95, 40, -10, 70, 25]
|
|
90
|
+
values.each_with_index { |v, i| ws << [v, i.even? ? "ok" : "error #{i}", v.abs, v, v] }
|
|
91
|
+
ws.conditional_format(1, 0, 10, 0, type: :cell, criteria: :<, value: 0, format: red)
|
|
92
|
+
ws.conditional_format(1, 1, 10, 1, type: :text, criteria: :contains, value: "error", format: red)
|
|
93
|
+
ws.conditional_format(1, 2, 10, 2, type: :data_bar)
|
|
94
|
+
ws.conditional_format(1, 3, 10, 3, type: :color_scale)
|
|
95
|
+
ws.conditional_format(1, 4, 10, 4, type: :formula, value: "=$A2>50", format: green)
|
|
96
|
+
ws.column_width(0..4, 22)
|
|
97
|
+
|
|
98
|
+
# Data validation.
|
|
99
|
+
ws = wb.add_worksheet("Validation")
|
|
100
|
+
ws.append(["Status (dropdown)", "Quantity 1-10", "Price >= 0", "Code <= 5 chars"], format: header)
|
|
101
|
+
ws.data_validation(1, 0, 20, 0, type: :list, value: %w[Open Pending Closed],
|
|
102
|
+
input_title: "Status", input_message: "Pick a status")
|
|
103
|
+
ws.data_validation(1, 1, 20, 1, type: :whole_number, criteria: :between, value: [1, 10],
|
|
104
|
+
error_title: "Invalid quantity", error_message: "Enter a whole number from 1 to 10")
|
|
105
|
+
ws.data_validation(1, 2, 20, 2, type: :decimal, criteria: :>=, value: 0)
|
|
106
|
+
ws.data_validation(1, 3, 20, 3, type: :text_length, criteria: :<=, value: 5)
|
|
107
|
+
ws.column_width(0..3, 20)
|
|
108
|
+
|
|
109
|
+
# Table with a total row.
|
|
110
|
+
ws = wb.add_worksheet("Table")
|
|
111
|
+
sales = [%w[North Ann 1200], %w[South Bob 950], %w[East Cai 1430], %w[West Dee 780]].map { |r, n, a| [r, n, a.to_i] }
|
|
112
|
+
ws.add_table(0, 0, sales.size + 1, 2, total_row: true, style: :medium2,
|
|
113
|
+
columns: [{ header: "Region", total_label: "Total" }, "Rep",
|
|
114
|
+
{ header: "Sales", total: :sum, format: money }])
|
|
115
|
+
ws.concat(sales)
|
|
116
|
+
ws.column_width(0..2, 14)
|
|
117
|
+
|
|
118
|
+
# Charts.
|
|
119
|
+
ws = wb.add_worksheet("Chart")
|
|
120
|
+
ws.append(%w[Month Sales Costs], format: header)
|
|
121
|
+
ws.concat([["Jan", 10, 7], ["Feb", 25, 12], ["Mar", 18, 11], ["Apr", 30, 16], ["May", 27, 14]])
|
|
122
|
+
series = [{ name: "Sales", categories: "Chart!$A$2:$A$6", values: "Chart!$B$2:$B$6" },
|
|
123
|
+
{ name: "Costs", categories: "Chart!$A$2:$A$6", values: "Chart!$C$2:$C$6" }]
|
|
124
|
+
ws.insert_chart(0, 4, type: :column, series: series, title: "Column", x_axis: "Month", y_axis: "Amount")
|
|
125
|
+
ws.insert_chart(16, 4, type: :line, series: series, title: "Line", width: 600, height: 300)
|
|
126
|
+
ws.insert_chart(16, 0, type: :pie, series: [series.first], title: "Pie", width: 300, height: 300)
|
|
127
|
+
|
|
128
|
+
# Image and comment.
|
|
129
|
+
ws = wb.add_worksheet("Media")
|
|
130
|
+
ws << ["Generated PNG at scale 1, pixel size 240x60, and with an offset. B1 has a comment."]
|
|
131
|
+
ws.write_comment(0, 1, "This is a note (comment).", author: "fast_xlsx")
|
|
132
|
+
png = gradient_png(120, 40)
|
|
133
|
+
ws.insert_image(2, 0, StringIO.new(png), alt_text: "Gradient")
|
|
134
|
+
ws.insert_image(2, 3, StringIO.new(png), width: 240, height: 60)
|
|
135
|
+
ws.insert_image(8, 0, StringIO.new(png), x_offset: 20, y_offset: 10, scale: 1.5)
|
|
136
|
+
|
|
137
|
+
# Printing: check File > Print preview.
|
|
138
|
+
ws = wb.add_worksheet("Printing")
|
|
139
|
+
ws.page_header("&CPage &P of &N").page_footer("&L&A&R&D", margin: 0.2).margins(left: 0.5, right: 0.5)
|
|
140
|
+
ws.page_breaks([30])
|
|
141
|
+
ws.vertical_page_breaks([4])
|
|
142
|
+
ws.append(%w[Row A B C D E F], format: header)
|
|
143
|
+
60.times { |i| ws << [i + 1, *Array.new(6) { |c| (i + 1) * (c + 1) }] }
|
|
144
|
+
|
|
145
|
+
wb.save(path)
|
|
146
|
+
puts "wrote #{path}"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "fast_xlsx"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
authors = ["Zac <579103+7a6163@users.noreply.github.com>"]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
publish = false
|
|
8
|
+
|
|
9
|
+
[lib]
|
|
10
|
+
crate-type = ["cdylib"]
|
|
11
|
+
|
|
12
|
+
[dependencies]
|
|
13
|
+
magnus = "0.9"
|
|
14
|
+
rb-sys = { version = "0.9", features = ["stable-api-compiled-fallback"] }
|
|
15
|
+
rust_xlsxwriter = { version = "0.99.1", features = ["constant_memory", "zlib"] }
|
|
16
|
+
|
|
17
|
+
[build-dependencies]
|
|
18
|
+
rb-sys-env = "0.2.2"
|