kmadej_fast_excel_fork 0.2.2

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.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.travis.yml +28 -0
  4. data/CHANGELOG.md +13 -0
  5. data/Gemfile +17 -0
  6. data/Gemfile.lock +70 -0
  7. data/Makefile +14 -0
  8. data/README.md +95 -0
  9. data/Rakefile +24 -0
  10. data/appveyor.yml +25 -0
  11. data/benchmarks/1k_rows.rb +59 -0
  12. data/benchmarks/20k_rows.rb +26 -0
  13. data/benchmarks/init.rb +59 -0
  14. data/benchmarks/memory.rb +49 -0
  15. data/examples/example.rb +42 -0
  16. data/examples/example_align.rb +23 -0
  17. data/examples/example_chart.rb +21 -0
  18. data/examples/example_colors.rb +37 -0
  19. data/examples/example_formula.rb +18 -0
  20. data/examples/example_image.rb +13 -0
  21. data/examples/example_styles.rb +27 -0
  22. data/examples/logo.png +0 -0
  23. data/extconf.rb +0 -0
  24. data/fast_excel.gemspec +20 -0
  25. data/lib/fast_excel.rb +600 -0
  26. data/lib/fast_excel/binding.rb +2819 -0
  27. data/lib/fast_excel/binding/chart.rb +2666 -0
  28. data/lib/fast_excel/binding/format.rb +1177 -0
  29. data/lib/fast_excel/binding/workbook.rb +338 -0
  30. data/lib/fast_excel/binding/worksheet.rb +1555 -0
  31. data/libxlsxwriter/.gitignore +49 -0
  32. data/libxlsxwriter/.indent.pro +125 -0
  33. data/libxlsxwriter/.travis.yml +25 -0
  34. data/libxlsxwriter/CONTRIBUTING.md +226 -0
  35. data/libxlsxwriter/Changes.txt +557 -0
  36. data/libxlsxwriter/LICENSE.txt +89 -0
  37. data/libxlsxwriter/Makefile +156 -0
  38. data/libxlsxwriter/Readme.md +78 -0
  39. data/libxlsxwriter/cocoapods/libxlsxwriter-umbrella.h +30 -0
  40. data/libxlsxwriter/cocoapods/libxlsxwriter.modulemap +7 -0
  41. data/libxlsxwriter/include/xlsxwriter.h +23 -0
  42. data/libxlsxwriter/include/xlsxwriter/app.h +79 -0
  43. data/libxlsxwriter/include/xlsxwriter/chart.h +3476 -0
  44. data/libxlsxwriter/include/xlsxwriter/common.h +372 -0
  45. data/libxlsxwriter/include/xlsxwriter/content_types.h +74 -0
  46. data/libxlsxwriter/include/xlsxwriter/core.h +51 -0
  47. data/libxlsxwriter/include/xlsxwriter/custom.h +52 -0
  48. data/libxlsxwriter/include/xlsxwriter/drawing.h +111 -0
  49. data/libxlsxwriter/include/xlsxwriter/format.h +1214 -0
  50. data/libxlsxwriter/include/xlsxwriter/hash_table.h +76 -0
  51. data/libxlsxwriter/include/xlsxwriter/packager.h +80 -0
  52. data/libxlsxwriter/include/xlsxwriter/relationships.h +77 -0
  53. data/libxlsxwriter/include/xlsxwriter/shared_strings.h +83 -0
  54. data/libxlsxwriter/include/xlsxwriter/styles.h +77 -0
  55. data/libxlsxwriter/include/xlsxwriter/theme.h +47 -0
  56. data/libxlsxwriter/include/xlsxwriter/third_party/ioapi.h +214 -0
  57. data/libxlsxwriter/include/xlsxwriter/third_party/queue.h +694 -0
  58. data/libxlsxwriter/include/xlsxwriter/third_party/tmpfileplus.h +53 -0
  59. data/libxlsxwriter/include/xlsxwriter/third_party/tree.h +801 -0
  60. data/libxlsxwriter/include/xlsxwriter/third_party/zip.h +375 -0
  61. data/libxlsxwriter/include/xlsxwriter/utility.h +166 -0
  62. data/libxlsxwriter/include/xlsxwriter/workbook.h +757 -0
  63. data/libxlsxwriter/include/xlsxwriter/worksheet.h +2641 -0
  64. data/libxlsxwriter/include/xlsxwriter/xmlwriter.h +178 -0
  65. data/libxlsxwriter/lib/.gitignore +0 -0
  66. data/libxlsxwriter/libxlsxwriter.podspec +47 -0
  67. data/libxlsxwriter/src/Makefile +130 -0
  68. data/libxlsxwriter/src/app.c +443 -0
  69. data/libxlsxwriter/src/chart.c +6346 -0
  70. data/libxlsxwriter/src/content_types.c +345 -0
  71. data/libxlsxwriter/src/core.c +293 -0
  72. data/libxlsxwriter/src/custom.c +224 -0
  73. data/libxlsxwriter/src/drawing.c +746 -0
  74. data/libxlsxwriter/src/format.c +729 -0
  75. data/libxlsxwriter/src/hash_table.c +223 -0
  76. data/libxlsxwriter/src/packager.c +948 -0
  77. data/libxlsxwriter/src/relationships.c +245 -0
  78. data/libxlsxwriter/src/shared_strings.c +266 -0
  79. data/libxlsxwriter/src/styles.c +1088 -0
  80. data/libxlsxwriter/src/theme.c +348 -0
  81. data/libxlsxwriter/src/utility.c +515 -0
  82. data/libxlsxwriter/src/workbook.c +1930 -0
  83. data/libxlsxwriter/src/worksheet.c +5022 -0
  84. data/libxlsxwriter/src/xmlwriter.c +355 -0
  85. data/libxlsxwriter/third_party/minizip/Makefile +44 -0
  86. data/libxlsxwriter/third_party/minizip/Makefile.am +45 -0
  87. data/libxlsxwriter/third_party/minizip/Makefile.orig +25 -0
  88. data/libxlsxwriter/third_party/minizip/MiniZip64_Changes.txt +6 -0
  89. data/libxlsxwriter/third_party/minizip/MiniZip64_info.txt +74 -0
  90. data/libxlsxwriter/third_party/minizip/README.txt +5 -0
  91. data/libxlsxwriter/third_party/minizip/configure.ac +32 -0
  92. data/libxlsxwriter/third_party/minizip/crypt.h +131 -0
  93. data/libxlsxwriter/third_party/minizip/ioapi.c +247 -0
  94. data/libxlsxwriter/third_party/minizip/ioapi.h +208 -0
  95. data/libxlsxwriter/third_party/minizip/iowin32.c +456 -0
  96. data/libxlsxwriter/third_party/minizip/iowin32.h +28 -0
  97. data/libxlsxwriter/third_party/minizip/make_vms.com +25 -0
  98. data/libxlsxwriter/third_party/minizip/miniunz.c +660 -0
  99. data/libxlsxwriter/third_party/minizip/miniunzip.1 +63 -0
  100. data/libxlsxwriter/third_party/minizip/minizip.1 +46 -0
  101. data/libxlsxwriter/third_party/minizip/minizip.c +520 -0
  102. data/libxlsxwriter/third_party/minizip/minizip.pc.in +12 -0
  103. data/libxlsxwriter/third_party/minizip/mztools.c +291 -0
  104. data/libxlsxwriter/third_party/minizip/mztools.h +37 -0
  105. data/libxlsxwriter/third_party/minizip/unzip.c +2125 -0
  106. data/libxlsxwriter/third_party/minizip/unzip.h +437 -0
  107. data/libxlsxwriter/third_party/minizip/zip.c +2007 -0
  108. data/libxlsxwriter/third_party/minizip/zip.h +367 -0
  109. data/libxlsxwriter/third_party/tmpfileplus/Makefile +42 -0
  110. data/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.c +342 -0
  111. data/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.h +53 -0
  112. data/libxlsxwriter/version.txt +1 -0
  113. data/test/date_test.rb +22 -0
  114. data/test/default_format_test.rb +19 -0
  115. data/test/format_test.rb +171 -0
  116. data/test/test_helper.rb +52 -0
  117. data/test/tmpfile_test.rb +23 -0
  118. data/test/worksheet_test.rb +86 -0
  119. metadata +182 -0
@@ -0,0 +1,49 @@
1
+ require_relative 'init'
2
+
3
+ HEADERS = ["id", "name", "age", "date", "random"]
4
+
5
+ DATA = []
6
+ 10_000.times do |n|
7
+ DATA << [n, "String string #{n}" * 5, (n * rand * 10).round, Time.at(n * 1000 + 1492922688), n * 100]
8
+ end
9
+
10
+ puts "warm up..."
11
+ write_fast_excel_20k
12
+ write_axlsx_20k
13
+ write_xlsx_20k
14
+
15
+ DATA.clear
16
+ 50_000.times do |n|
17
+ DATA << [n, "String string #{n}" * 5, (n * rand * 10).round, Time.at(n * 1000 + 1492922688), n * 100]
18
+ end
19
+
20
+ GC.start
21
+ sleep 5
22
+
23
+ def measure_memory(title)
24
+ puts "Running test: #{title}"
25
+ recorder = ProcessMemory.start_recording
26
+ yield
27
+ puts recorder.print("Done!")
28
+ recorder.stop
29
+ puts recorder.report_per_second_pretty
30
+ puts
31
+ end
32
+
33
+ measure_memory("FastExcel") do
34
+ write_fast_excel_20k
35
+ end
36
+
37
+ GC.start
38
+ sleep 5
39
+
40
+ measure_memory("Axlsx") do
41
+ write_axlsx_20k
42
+ end
43
+
44
+ GC.start
45
+ sleep 5
46
+
47
+ measure_memory("write_xlsx") do
48
+ write_xlsx_20k
49
+ end
@@ -0,0 +1,42 @@
1
+ require_relative '../lib/fast_excel'
2
+
3
+ workbook = FastExcel.open("example.xlsx", constant_memory: true)
4
+
5
+ workbook.default_format.set(
6
+ font_size: 0, # user's default
7
+ #font_family: "Arial"
8
+ )
9
+
10
+ # pp workbook.default_format
11
+
12
+ worksheet = workbook.add_worksheet("Payments Report")
13
+
14
+ bold = workbook.bold_cell_format
15
+ worksheet.set_column(0, 0, FastExcel::DEF_COL_WIDTH, bold)
16
+
17
+ price = workbook.number_format("#,##0.00")
18
+ worksheet.set_column(1, 1, 20, price)
19
+
20
+ date_format = workbook.number_format("[$-409]m/d/yy h:mm AM/PM;@")
21
+ worksheet.set_column(2, 2, 20, date_format)
22
+
23
+ worksheet.write_row(0, ["message", "price", "date"], bold)
24
+
25
+ for i in 1..1000
26
+ worksheet.write_row(i, ["Hello", (rand * 10_000_000).round(2), Time.now])
27
+
28
+ # Or manually
29
+ # worksheet.write_string(i, 0, "Hello", nil)
30
+ # worksheet.write_number(i, 1, (rand * 10_000_000).round(2), nil)
31
+ # date = Libxlsxwriter::Datetime.new
32
+ # date[:year] = 2017
33
+ # date[:month] = 2
34
+ # date[:day] = 24
35
+ # date[:hour] = i % 24
36
+ # date[:min] = i % 60
37
+ # date[:sec] = i % 60
38
+ # worksheet.write_datetime(i, 2, date, nil)
39
+ end
40
+
41
+ workbook.close
42
+ puts "Saved to file example.xlsx"
@@ -0,0 +1,23 @@
1
+ require_relative '../lib/fast_excel'
2
+
3
+ workbook = FastExcel.open("example_align.xlsx", constant_memory: true)
4
+
5
+ worksheet = workbook.add_worksheet
6
+
7
+ row_format = workbook.add_format
8
+
9
+ row_format.align = {h: :center, v: :center}
10
+ # Can also be called as:
11
+ # row_format.align = :align_center
12
+ # row_format.align = :align_vertical_center
13
+
14
+ print "Align set as: "
15
+ p row_format.align # => {horizontal: :align_center, vertical: :align_vertical_center}
16
+
17
+ worksheet.set_column_width(0, 30)
18
+ worksheet.set_row(0, 30, row_format)
19
+
20
+ worksheet.write_row(0, ["Hello"])
21
+
22
+ workbook.close
23
+ puts "Saved to file example_align.xlsx"
@@ -0,0 +1,21 @@
1
+ require_relative '../lib/fast_excel'
2
+
3
+ workbook = FastExcel.open("example_chart.xlsx", constant_memory: true)
4
+ worksheet = workbook.add_worksheet
5
+
6
+ for i in 0..5
7
+ for n in 0..3
8
+ worksheet.write_number(i, n, (i + 1) * (n + 1), nil)
9
+ end
10
+ end
11
+
12
+ chart = workbook.add_chart(Libxlsxwriter::enum_type(:chart_type)[:column])
13
+
14
+ chart.add_series("Bob", "Sheet1!$A$1:$A$5")
15
+ chart.add_series("Alice", "Sheet1!$B$1:$B$5")
16
+ chart.add_series("Montgomery", "Sheet1!$C$1:$C$5")
17
+
18
+ worksheet.insert_chart(1, 7, chart)
19
+
20
+ workbook.close
21
+ puts "Saved to file example_chart.xlsx"
@@ -0,0 +1,37 @@
1
+ require_relative '../lib/fast_excel'
2
+
3
+ workbook = FastExcel.open("example_colors.xlsx", constant_memory: true)
4
+
5
+ worksheet = workbook.add_worksheet
6
+
7
+ color_format = workbook.add_format
8
+
9
+ # We can use color names as string and symbols, color hex codes and color hex numbers
10
+
11
+ color_format.set(
12
+ font_color: '9900FF',
13
+ bg_color: '#FFAAAA',
14
+
15
+ border_bottom: :medium,
16
+ border_bottom_color: 'green',
17
+
18
+ border_left: :slant_dash_dot,
19
+ border_left_color: 0x00FF00,
20
+
21
+ border_right: :double,
22
+ border_right_color: :crimson,
23
+
24
+ border_top: :border_hair,
25
+ border_top_color: :medium_blue
26
+ )
27
+
28
+ # Possible border styles:
29
+ # [:none, :thin, :medium, :dashed, :dotted, :thick, :double, :hair, :medium_dashed,
30
+ # :dash_dot, :medium_dash_dot, :dash_dot_dot, :medium_dash_dot_dot, :slant_dash_dot]
31
+
32
+ worksheet.set_column_width(1, 30)
33
+
34
+ worksheet.write_value(1, 1, "Hello", color_format)
35
+
36
+ workbook.close
37
+ puts "Saved to file example_colors.xlsx"
@@ -0,0 +1,18 @@
1
+ require_relative '../lib/fast_excel'
2
+
3
+ workbook = FastExcel.open("example_formula.xlsx", constant_memory: false)
4
+
5
+ worksheet = workbook.add_worksheet
6
+
7
+ worksheet.write_row(0, ["Item", "Weight"])
8
+ worksheet.write_row(1, ["Laptop", 1.37])
9
+ worksheet.write_row(2, ["Phone", 0.138])
10
+ worksheet.write_row(3, ["Mouse", 0.099])
11
+ worksheet.write_row(4, ["Speaker", 2.5])
12
+ worksheet.write_row(5, ["Camera", 0.383])
13
+ worksheet.write_row(6, ["Total", FastExcel::Formula.new("SUM(B2:B6)")], workbook.bold_cell_format)
14
+
15
+ bold = workbook.bold_cell_format
16
+
17
+ workbook.close
18
+ puts "Saved to file example_formula.xlsx"
@@ -0,0 +1,13 @@
1
+ require_relative '../lib/fast_excel'
2
+
3
+ workbook = FastExcel.open("example_image.xlsx", constant_memory: false)
4
+ worksheet = workbook.add_worksheet
5
+
6
+ img_options = Libxlsxwriter::ImageOptions.new
7
+ img_options[:x_offset] = 0
8
+ img_options[:y_offset] = 0
9
+
10
+ worksheet.insert_image_opt(3, 3, "examples/logo.png", img_options)
11
+
12
+ workbook.close
13
+ puts "Saved to file example_image.xlsx"
@@ -0,0 +1,27 @@
1
+ require_relative '../lib/fast_excel'
2
+
3
+ workbook = FastExcel.open("example_styles.xlsx", constant_memory: true)
4
+
5
+ #workbook.default_format.set(
6
+ # font_size: 0, # user's default
7
+ # #font_family: "Arial"
8
+ #)
9
+
10
+ worksheet = workbook.add_worksheet
11
+
12
+ worksheet.set_columns_width(0, 11, 11)
13
+
14
+ worksheet.write_value(0, 0, "Bold", workbook.add_format(bold: true))
15
+ worksheet.write_value(0, 1, "Italic", workbook.add_format(italic: true))
16
+ worksheet.write_value(0, 2, "Underline", workbook.add_format(underline: :underline_single))
17
+ worksheet.write_value(0, 3, "Double line", workbook.add_format(underline: :underline_double))
18
+ worksheet.write_value(0, 4, "Indent", workbook.add_format(indent: 1))
19
+ worksheet.write_value(0, 5, "Border", workbook.add_format(border: :border_thin))
20
+ worksheet.write_value(0, 6, "Border2", workbook.add_format(border: :border_medium))
21
+ worksheet.write_value(0, 7, "Pattern", workbook.add_format(pattern: :pattern_light_up, bg_color: :yellow))
22
+ worksheet.write_value(0, 8, "Script", workbook.add_format(font_script: :font_subscript))
23
+ worksheet.write_value(0, 9, "Strike", workbook.add_format(font_strikeout: true))
24
+ worksheet.write_value(0, 10, "Shaddow", workbook.add_format(font_shadow: true))
25
+
26
+ workbook.close
27
+ puts "Saved to file example_styles.xlsx"
data/examples/logo.png ADDED
Binary file
data/extconf.rb ADDED
File without changes
@@ -0,0 +1,20 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "kmadej_fast_excel_fork"
3
+ s.version = "0.2.2"
4
+ s.author = ["Pavel Evstigneev"]
5
+ s.email = ["pavel.evst@gmail.com"]
6
+ s.homepage = "https://github.com/paxa/fast_excel"
7
+ s.summary = %q{Ultra Fast Excel Writter}
8
+ s.description = "Wrapper for libxlsxwriter using ffi"
9
+ s.license = 'MIT'
10
+ s.has_rdoc = false
11
+ s.required_ruby_version = '~> 2.0'
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = []
15
+
16
+ s.require_paths = ["lib"]
17
+ s.extensions = ["extconf.rb"]
18
+
19
+ s.add_runtime_dependency "ffi", ["> 1.9", "< 2"]
20
+ end
data/lib/fast_excel.rb ADDED
@@ -0,0 +1,600 @@
1
+ require_relative './fast_excel/binding'
2
+
3
+ module FastExcel
4
+
5
+ class Formula
6
+ attr_accessor :fml
7
+ def initialize(fml)
8
+ @fml = fml
9
+ end
10
+ end
11
+
12
+ DEF_COL_WIDTH = 8.43
13
+
14
+ def self.open(filename = nil, constant_memory: false, default_format: nil)
15
+ tmp_file = false
16
+ unless filename
17
+ require 'tmpdir'
18
+ filename = "#{Dir.mktmpdir}/fast_excel.xlsx"
19
+ tmp_file = true
20
+ end
21
+
22
+ unless filename
23
+ raise ArgumentError, "filename is required"
24
+ end
25
+
26
+ filename = filename.to_s if defined?(Pathname) && filename.is_a?(Pathname)
27
+
28
+ workbook = if constant_memory
29
+ opt = Libxlsxwriter::WorkbookOptions.new
30
+ opt[:constant_memory] = 1
31
+ Libxlsxwriter.workbook_new_opt(filename, opt)
32
+ else
33
+ Libxlsxwriter.workbook_new(filename)
34
+ end
35
+ result = Libxlsxwriter::Workbook.new(workbook)
36
+
37
+ if default_format
38
+ raise "default_format argument must be a hash" unless default_format.is_a?(Hash)
39
+ result.default_format.set(default_format)
40
+ end
41
+
42
+ result.tmp_file = tmp_file
43
+ result.filename = filename
44
+ result
45
+ end
46
+
47
+ # Creates internal Libxlsxwriter::Datetime from Datetime object
48
+ def self.lxw_datetime(time)
49
+ date = Libxlsxwriter::Datetime.new
50
+ date[:year] = time.year
51
+ date[:month] = time.month
52
+ date[:day] = time.day
53
+ date[:hour] = time.hour
54
+ date[:min] = time.minute
55
+ date[:sec] = time.second
56
+ date
57
+ end
58
+
59
+ # Creates internal Libxlsxwriter::Datetime from Time object
60
+ def self.lxw_time(time)
61
+ date = Libxlsxwriter::Datetime.new
62
+ date[:year] = time.year
63
+ date[:month] = time.month
64
+ date[:day] = time.day
65
+ date[:hour] = time.hour
66
+ date[:min] = time.min
67
+ date[:sec] = time.sec
68
+ date
69
+ end
70
+
71
+ # seconds in 1 day
72
+ XLSX_DATE_DAY = 86400.0
73
+
74
+ # days between 1970-jan-01 and 1900-jan-01
75
+ XLSX_DATE_EPOCH_DIFF = 25569
76
+
77
+ # Convert time to number of days, and change beginning point from 1st jan 1970 to 1st jan 1900
78
+ # Offset argument should be number of seconds, if not specified then it will use Time.zone.utc_offset || 0
79
+ #
80
+ # https://support.microsoft.com/en-us/help/214330/differences-between-the-1900-and-the-1904-date-system-in-excel
81
+ def self.date_num(time, offset = nil)
82
+ unless offset
83
+ # Try use Rails' app timezone
84
+ if Time.respond_to?(:zone)
85
+ offset = Time.zone.utc_offset
86
+ else
87
+ offset = 0 # rollback to UTC
88
+ end
89
+ end
90
+
91
+ time.to_f / XLSX_DATE_DAY + XLSX_DATE_EPOCH_DIFF + offset / XLSX_DATE_DAY
92
+ end
93
+
94
+ def self.print_ffi_obj(value)
95
+ puts "#{value.class}"
96
+ value.members.each do |key|
97
+ field_val = if value[key].is_a?(FFI::Pointer) && value[key].null? || value[key].nil?
98
+ "nil"
99
+ elsif value[key].is_a?(FFI::StructLayout::CharArray)
100
+ value[key].to_str.inspect
101
+ elsif value[key].is_a?(String)
102
+ value[key].inspect
103
+ elsif value[key].is_a?(Symbol)
104
+ value[key].inspect
105
+ else
106
+ value[key]
107
+ end
108
+ puts "* #{key}: #{field_val}"
109
+ end
110
+ nil
111
+ end
112
+
113
+
114
+ COLOR_ENUM = Libxlsxwriter.enum_type(:defined_colors)
115
+ EXTRA_COLORS = {
116
+ alice_blue: 0xF0F8FF,
117
+ antique_white: 0xFAEBD7,
118
+ aqua: 0x00FFFF,
119
+ aquamarine: 0x7FFFD4,
120
+ azure: 0xF0FFFF,
121
+ beige: 0xF5F5DC,
122
+ bisque: 0xFFE4C4,
123
+ black: 0x000000,
124
+ blanched_almond: 0xFFEBCD,
125
+ blue: 0x0000FF,
126
+ blue_violet: 0x8A2BE2,
127
+ brown: 0xA52A2A,
128
+ burly_wood: 0xDEB887,
129
+ cadet_blue: 0x5F9EA0,
130
+ chartreuse: 0x7FFF00,
131
+ chocolate: 0xD2691E,
132
+ coral: 0xFF7F50,
133
+ cornflower_blue: 0x6495ED,
134
+ cornsilk: 0xFFF8DC,
135
+ crimson: 0xDC143C,
136
+ cyan: 0x00FFFF,
137
+ dark_blue: 0x00008B,
138
+ dark_cyan: 0x008B8B,
139
+ dark_golden_rod: 0xB8860B,
140
+ dark_gray: 0xA9A9A9,
141
+ dark_grey: 0xA9A9A9,
142
+ dark_green: 0x006400,
143
+ dark_khaki: 0xBDB76B,
144
+ dark_magenta: 0x8B008B,
145
+ dark_olive_green: 0x556B2F,
146
+ dark_orange: 0xFF8C00,
147
+ dark_orchid: 0x9932CC,
148
+ dark_red: 0x8B0000,
149
+ dark_salmon: 0xE9967A,
150
+ dark_sea_green: 0x8FBC8F,
151
+ dark_slate_blue: 0x483D8B,
152
+ dark_slate_gray: 0x2F4F4F,
153
+ dark_slate_grey: 0x2F4F4F,
154
+ dark_turquoise: 0x00CED1,
155
+ dark_violet: 0x9400D3,
156
+ deep_pink: 0xFF1493,
157
+ deep_sky_blue: 0x00BFFF,
158
+ dim_gray: 0x696969,
159
+ dim_grey: 0x696969,
160
+ dodger_blue: 0x1E90FF,
161
+ fire_brick: 0xB22222,
162
+ floral_white: 0xFFFAF0,
163
+ forest_green: 0x228B22,
164
+ fuchsia: 0xFF00FF,
165
+ gainsboro: 0xDCDCDC,
166
+ ghost_white: 0xF8F8FF,
167
+ gold: 0xFFD700,
168
+ golden_rod: 0xDAA520,
169
+ gray: 0x808080,
170
+ grey: 0x808080,
171
+ green: 0x008000,
172
+ green_yellow: 0xADFF2F,
173
+ honey_dew: 0xF0FFF0,
174
+ hot_pink: 0xFF69B4,
175
+ indian_red: 0xCD5C5C,
176
+ indigo: 0x4B0082,
177
+ ivory: 0xFFFFF0,
178
+ khaki: 0xF0E68C,
179
+ lavender: 0xE6E6FA,
180
+ lavender_blush: 0xFFF0F5,
181
+ lawn_green: 0x7CFC00,
182
+ lemon_chiffon: 0xFFFACD,
183
+ light_blue: 0xADD8E6,
184
+ light_coral: 0xF08080,
185
+ light_cyan: 0xE0FFFF,
186
+ light_golden_rod_yellow: 0xFAFAD2,
187
+ light_gray: 0xD3D3D3,
188
+ light_grey: 0xD3D3D3,
189
+ light_green: 0x90EE90,
190
+ light_pink: 0xFFB6C1,
191
+ light_salmon: 0xFFA07A,
192
+ light_sea_green: 0x20B2AA,
193
+ light_sky_blue: 0x87CEFA,
194
+ light_slate_gray: 0x778899,
195
+ light_slate_grey: 0x778899,
196
+ light_steel_blue: 0xB0C4DE,
197
+ light_yellow: 0xFFFFE0,
198
+ lime: 0x00FF00,
199
+ lime_green: 0x32CD32,
200
+ linen: 0xFAF0E6,
201
+ magenta: 0xFF00FF,
202
+ maroon: 0x800000,
203
+ medium_aqua_marine: 0x66CDAA,
204
+ medium_blue: 0x0000CD,
205
+ medium_orchid: 0xBA55D3,
206
+ medium_purple: 0x9370DB,
207
+ medium_sea_green: 0x3CB371,
208
+ medium_slate_blue: 0x7B68EE,
209
+ medium_spring_green: 0x00FA9A,
210
+ medium_turquoise: 0x48D1CC,
211
+ medium_violet_red: 0xC71585,
212
+ midnight_blue: 0x191970,
213
+ mint_cream: 0xF5FFFA,
214
+ misty_rose: 0xFFE4E1,
215
+ moccasin: 0xFFE4B5,
216
+ navajo_white: 0xFFDEAD,
217
+ navy: 0x000080,
218
+ old_lace: 0xFDF5E6,
219
+ olive: 0x808000,
220
+ olive_drab: 0x6B8E23,
221
+ orange: 0xFFA500,
222
+ orange_red: 0xFF4500,
223
+ orchid: 0xDA70D6,
224
+ pale_golden_rod: 0xEEE8AA,
225
+ pale_green: 0x98FB98,
226
+ pale_turquoise: 0xAFEEEE,
227
+ pale_violet_red: 0xDB7093,
228
+ papaya_whip: 0xFFEFD5,
229
+ peach_puff: 0xFFDAB9,
230
+ peru: 0xCD853F,
231
+ pink: 0xFFC0CB,
232
+ plum: 0xDDA0DD,
233
+ powder_blue: 0xB0E0E6,
234
+ purple: 0x800080,
235
+ rebecca_purple: 0x663399,
236
+ red: 0xFF0000,
237
+ rosy_brown: 0xBC8F8F,
238
+ royal_blue: 0x4169E1,
239
+ saddle_brown: 0x8B4513,
240
+ salmon: 0xFA8072,
241
+ sandy_brown: 0xF4A460,
242
+ sea_green: 0x2E8B57,
243
+ sea_shell: 0xFFF5EE,
244
+ sienna: 0xA0522D,
245
+ silver: 0xC0C0C0,
246
+ sky_blue: 0x87CEEB,
247
+ slate_blue: 0x6A5ACD,
248
+ slate_gray: 0x708090,
249
+ slate_grey: 0x708090,
250
+ snow: 0xFFFAFA,
251
+ spring_green: 0x00FF7F,
252
+ steel_blue: 0x4682B4,
253
+ tan: 0xD2B48C,
254
+ teal: 0x008080,
255
+ thistle: 0xD8BFD8,
256
+ tomato: 0xFF6347,
257
+ turquoise: 0x40E0D0,
258
+ violet: 0xEE82EE,
259
+ wheat: 0xF5DEB3,
260
+ white: 0xFFFFFF,
261
+ white_smoke: 0xF5F5F5,
262
+ yellow: 0xFFFF00,
263
+ yellow_green: 0x9ACD32
264
+ }.freeze
265
+
266
+ # Convert hex string, color name or hex number to color hex number
267
+ def self.color_to_hex(value)
268
+ orig_value = value
269
+ value = value.to_s if value.is_a?(Symbol)
270
+
271
+ if value.is_a?(String)
272
+ if EXTRA_COLORS[value.to_sym]
273
+ return EXTRA_COLORS[value.to_sym]
274
+ elsif COLOR_ENUM.find(value.to_sym)
275
+ return COLOR_ENUM.find(value.to_sym)
276
+ elsif COLOR_ENUM.find("color_#{value.to_sym}")
277
+ return COLOR_ENUM.find("color_#{value.to_sym}")
278
+ elsif value =~ /^#?(0x)?([\da-f]){6}$/i
279
+ value = value.sub('#', '') if value.start_with?('#')
280
+ return value.start_with?('0x') ? value.to_i(16) : "0x#{value}".to_i(16)
281
+ else
282
+ raise ArgumentError, "Unknown color value #{orig_value.inspect}, expected hex string or color name"
283
+ end
284
+ end
285
+
286
+ return value if value.is_a?(Numeric)
287
+
288
+ raise ArgumentError, "Can not use #{value.class} (#{value.inspect}) for color value, expected String or Hex Number"
289
+ end
290
+
291
+ module AttributeHelper
292
+ def set(values)
293
+ values.each do |key, value|
294
+ if respond_to?("#{key}=")
295
+ send("#{key}=", value)
296
+ elsif respond_to?("set_#{key}=")
297
+ send("set_#{key}=", value)
298
+ else
299
+ self[key] = value
300
+ end
301
+ end
302
+
303
+ self
304
+ end
305
+
306
+ def fields_hash
307
+ res = {}
308
+ members.each do |key|
309
+ #p [key, self[key]]
310
+ res[key] = respond_to?(key) ? send(key) : self[key]
311
+ end
312
+ res
313
+ end
314
+
315
+ def pretty_print(pp)
316
+ pp fields_hash
317
+ end
318
+ end
319
+
320
+ module WorkbookExt
321
+ include AttributeHelper
322
+ attr_accessor :tmp_file, :is_open, :filename
323
+
324
+ def initialize(struct)
325
+ @is_open = true
326
+ super(struct)
327
+ end
328
+
329
+ def add_format(options = nil)
330
+ new_format = super()
331
+ new_format.set(options) if options
332
+ new_format
333
+ end
334
+
335
+ def bold_cell_format
336
+ bold = add_format
337
+ bold.set_bold
338
+ bold
339
+ end
340
+
341
+ # "#,##0.00"
342
+ # "[$-409]m/d/yy h:mm AM/PM;@"
343
+ def number_format(pattern)
344
+ format = add_format
345
+ format.set_num_format(pattern)
346
+ format
347
+ end
348
+
349
+ def add_worksheet(sheetname = nil)
350
+ sheet = super
351
+ sheet.workbook = self
352
+ sheet
353
+ end
354
+
355
+ def close
356
+ @is_open = false
357
+ super
358
+ end
359
+
360
+ def read_string
361
+ close if @is_open
362
+ File.open(filename, 'rb', &:read)
363
+ ensure
364
+ remove_tmp_file
365
+ end
366
+
367
+ def remove_tmp_file
368
+ File.delete(filename) if tmp_file
369
+ end
370
+
371
+ def constant_memory?
372
+ #FastExcel.print_ffi_obj(self[:options])
373
+ @constant_memory ||= self[:options][:constant_memory] != 0
374
+ end
375
+ end
376
+
377
+ module WorksheetExt
378
+ attr_accessor :workbook
379
+
380
+ include AttributeHelper
381
+
382
+ def write_row(row_number, values, formats = nil)
383
+ values.each_with_index do |value, index|
384
+ format = if formats
385
+ formats.is_a?(Array) ? formats[index] : formats
386
+ end
387
+
388
+ write_value(row_number, index, value, format)
389
+ end
390
+ end
391
+
392
+ def write_value(row_number, cell_number, value, format = nil)
393
+
394
+ if workbook.constant_memory? && row_number < last_row_number
395
+ raise ArgumentError, "Can not write to saved row in constant_memory mode (attempted row: #{row_number}, last saved row: #{last_row_number})"
396
+ end
397
+
398
+ if value.is_a?(Numeric)
399
+ write_number(row_number, cell_number, value, format)
400
+ elsif defined?(DateTime) && value.is_a?(DateTime)
401
+ write_datetime(row_number, cell_number, FastExcel.lxw_datetime(value), format)
402
+ elsif value.is_a?(Time)
403
+ write_datetime(row_number, cell_number, FastExcel.lxw_time(value), format)
404
+ elsif value.is_a?(Formula)
405
+ write_formula(row_number, cell_number, value.fml, format)
406
+ else
407
+ write_string(row_number, cell_number, value.to_s, format)
408
+ end
409
+
410
+ @last_row_number = row_number > last_row_number ? row_number : last_row_number
411
+ end
412
+
413
+ def append_row(values, formats = nil)
414
+ increment_last_row_number!
415
+ write_row(last_row_number, values, formats)
416
+ end
417
+
418
+ def last_row_number
419
+ defined?(@last_row_number) ? @last_row_number : -1
420
+ end
421
+
422
+ def increment_last_row_number!
423
+ @last_row_number = last_row_number + 1
424
+ end
425
+
426
+ def set_column(start_col, end_col, width, format = nil)
427
+ super(start_col, end_col, width, format)
428
+ end
429
+
430
+ def set_column_width(col, width)
431
+ set_column(col, col, width, nil)
432
+ end
433
+
434
+ def set_columns_width(start_col, end_col, width)
435
+ set_column(start_col, end_col, width, nil)
436
+ end
437
+
438
+ end
439
+
440
+ module FormatExt
441
+ include AttributeHelper
442
+
443
+ [:font_size, :underline, :font_script, :rotation, :indent, :pattern, :border].each do |prop|
444
+ define_method(prop) do
445
+ self[prop]
446
+ end
447
+ define_method("#{prop}=") do |value|
448
+ send("set_#{prop}", value)
449
+ end
450
+ end
451
+
452
+ [:bold, :italic, :font_outline, :font_shadow, :hidden, :text_wrap, :font_strikeout, :shrink, :text_justlast].each do |prop|
453
+ define_method(prop) do
454
+ self[prop]
455
+ end
456
+ define_method("#{prop}=") do |value|
457
+ value ? send("set_#{prop}") : self[prop] = false
458
+ end
459
+ end
460
+
461
+ [:num_format, :font_name].each do |prop|
462
+ define_method(prop) do
463
+ self[prop].to_ptr.read_string
464
+ end
465
+
466
+ define_method("#{prop}=") do |value|
467
+ send("set_#{prop}", value)
468
+ end
469
+ end
470
+
471
+ ALIGN_ENUM = Libxlsxwriter.enum_type(:format_alignments)
472
+
473
+ # Can be called as:
474
+ #
475
+ # format.align = :align_center
476
+ # format.align = "align_center"
477
+ # format.align = :center
478
+ # format.align = :align_center
479
+ # format.align = {v: "center", h: "center"}
480
+ #
481
+ # Possible values:
482
+ #
483
+ # :align_none, :align_left, :align_center, :align_right, :align_fill, :align_justify,
484
+ # :align_center_across, :align_distributed, :align_vertical_top, :align_vertical_bottom,
485
+ # :align_vertical_center, :align_vertical_justify, :align_vertical_distributed
486
+ #
487
+ def align=(value)
488
+ value = value.to_sym if value.is_a?(String)
489
+
490
+ if value.is_a?(Symbol)
491
+ if ALIGN_ENUM.find(value)
492
+ set_align(value)
493
+ elsif ALIGN_ENUM.find(prefixed = "align_#{value}".to_sym)
494
+ set_align(prefixed)
495
+ else
496
+ raise ArgumentError, "Can not set align = #{value.inspect}, possible values are: #{ALIGN_ENUM.symbols}"
497
+ end
498
+ elsif value.is_a?(Hash)
499
+ if value[:horizontal]
500
+ self.align = "align_#{value[:horizontal].to_s.sub(/^align_/, '')}".to_sym
501
+ end
502
+ if value[:h]
503
+ self.align = "align_#{value[:h].to_s.sub(/^align_/, '')}".to_sym
504
+ end
505
+ if value[:vertical]
506
+ self.align = "align_vertical_#{value[:vertical].to_s.sub(/^align_vertical_/, '')}".to_sym
507
+ end
508
+ if value[:v]
509
+ self.align = "align_vertical_#{value[:v].to_s.sub(/^align_vertical_/, '')}".to_sym
510
+ end
511
+ possible = [:horizontal, :h, :vertical, :v]
512
+ extras = value.keys - possible
513
+ if extras.size > 0
514
+ raise ArgumentError, "Not allowed keys for align: #{extras.inspect}, possible keys: #{possible.inspect}"
515
+ end
516
+ else
517
+ raise ArgumentError, "value must be a symbol or a hash"
518
+ end
519
+ end
520
+
521
+ def align
522
+ {
523
+ horizontal: ALIGN_ENUM.find(self[:text_h_align]),
524
+ vertical: ALIGN_ENUM.find(self[:text_v_align])
525
+ }
526
+ end
527
+
528
+ [:font_color, :bg_color, :fg_color, :bottom_color, :diag_color, :left_color, :right_color, :top_color].each do |prop|
529
+ define_method("#{prop}=") do |value|
530
+ send("set_#{prop}", FastExcel.color_to_hex(value))
531
+ end
532
+ define_method(prop) do
533
+ self[prop]
534
+ end
535
+ end
536
+
537
+ [:bottom_color, :left_color, :right_color, :top_color].each do |prop|
538
+ alias_method :"border_#{prop}=", :"#{prop}="
539
+ alias_method :"border_#{prop}", :"#{prop}"
540
+ end
541
+
542
+ BORDER_ENUM = Libxlsxwriter.enum_type(:format_borders)
543
+
544
+ [:bottom, :diag_border, :left, :right, :top].each do |prop|
545
+ define_method("#{prop}=") do |value|
546
+
547
+ send("set_#{prop}", border_value(value))
548
+ end
549
+ define_method(prop) do
550
+ BORDER_ENUM.find(self[prop])
551
+ end
552
+
553
+ unless prop == :diag_border
554
+ alias_method :"border_#{prop}=", :"#{prop}="
555
+ alias_method :"border_#{prop}", :"#{prop}"
556
+ end
557
+ end
558
+
559
+ def border_value(value)
560
+ # if a number
561
+ return value if value.is_a?(Numeric) && BORDER_ENUM.find(value)
562
+
563
+ orig_value = value
564
+ value = value.to_sym if value.is_a?(String)
565
+
566
+ return BORDER_ENUM.find(value) if BORDER_ENUM.find(value)
567
+ return BORDER_ENUM.find(:"border_#{value}") if BORDER_ENUM.find(:"border_#{value}")
568
+
569
+ short_symbols = BORDER_ENUM.symbols.map {|s| s.to_s.sub(/^border_/, '').to_sym }
570
+ raise ArgumentError, "Unknown value #{orig_value.inspect} for border. Possible values: #{short_symbols}"
571
+ end
572
+
573
+ def set_font_size(value)
574
+ if value < 0
575
+ raise ArgumentError, "font size should be >= 0 (use 0 for user default font size)"
576
+ end
577
+ super(value)
578
+ end
579
+
580
+ def font_family
581
+ font_name
582
+ end
583
+
584
+ def font_family=(value)
585
+ self.font_name = value
586
+ end
587
+ end
588
+ end
589
+
590
+ Libxlsxwriter::Workbook.instance_eval do
591
+ include FastExcel::WorkbookExt
592
+ end
593
+
594
+ Libxlsxwriter::Format.instance_eval do
595
+ include FastExcel::FormatExt
596
+ end
597
+
598
+ Libxlsxwriter::Worksheet.instance_eval do
599
+ include FastExcel::WorksheetExt
600
+ end