xlsxrb 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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.devcontainer/Dockerfile +64 -0
  3. data/.devcontainer/devcontainer.json +17 -0
  4. data/CHANGELOG.md +12 -0
  5. data/CODE_OF_CONDUCT.md +10 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +384 -0
  8. data/Rakefile +290 -0
  9. data/benchmark.rb +390 -0
  10. data/docs/ARCHITECTURE.md +488 -0
  11. data/docs/SPEC_SOURCES.md +42 -0
  12. data/docs/visual/VisualGallery.md +4684 -0
  13. data/docs/wasm/wasm_doc_helper.css +189 -0
  14. data/docs/wasm/wasm_doc_helper.js +320 -0
  15. data/lib/xlsxrb/elements/cell.rb +77 -0
  16. data/lib/xlsxrb/elements/column.rb +28 -0
  17. data/lib/xlsxrb/elements/row.rb +47 -0
  18. data/lib/xlsxrb/elements/types.rb +36 -0
  19. data/lib/xlsxrb/elements/workbook.rb +47 -0
  20. data/lib/xlsxrb/elements/worksheet.rb +50 -0
  21. data/lib/xlsxrb/elements.rb +15 -0
  22. data/lib/xlsxrb/ooxml/reader.rb +8048 -0
  23. data/lib/xlsxrb/ooxml/shared_strings_parser.rb +75 -0
  24. data/lib/xlsxrb/ooxml/styles_parser.rb +194 -0
  25. data/lib/xlsxrb/ooxml/utils.rb +122 -0
  26. data/lib/xlsxrb/ooxml/workbook_parser.rb +78 -0
  27. data/lib/xlsxrb/ooxml/workbook_writer.rb +1000 -0
  28. data/lib/xlsxrb/ooxml/worksheet_parser.rb +455 -0
  29. data/lib/xlsxrb/ooxml/worksheet_writer.rb +1008 -0
  30. data/lib/xlsxrb/ooxml/writer.rb +5450 -0
  31. data/lib/xlsxrb/ooxml/xml_builder.rb +113 -0
  32. data/lib/xlsxrb/ooxml/xml_parser.rb +68 -0
  33. data/lib/xlsxrb/ooxml/zip_generator.rb +162 -0
  34. data/lib/xlsxrb/ooxml/zip_reader.rb +158 -0
  35. data/lib/xlsxrb/ooxml/zip_writer.rb +213 -0
  36. data/lib/xlsxrb/ooxml.rb +22 -0
  37. data/lib/xlsxrb/style_builder.rb +241 -0
  38. data/lib/xlsxrb/version.rb +5 -0
  39. data/lib/xlsxrb.rb +1427 -0
  40. data/measure_memory.rb +42 -0
  41. data/sig/xlsxrb.rbs +23 -0
  42. data/vendor/sdk_runner/Program.cs +91 -0
  43. data/vendor/sdk_runner/sdk_runner.csproj +13 -0
  44. metadata +144 -0
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ooxml/utils"
4
+ require_relative "ooxml/zip_reader"
5
+ require_relative "ooxml/zip_writer"
6
+ require_relative "ooxml/xml_parser"
7
+ require_relative "ooxml/xml_builder"
8
+ require_relative "ooxml/shared_strings_parser"
9
+ require_relative "ooxml/styles_parser"
10
+ require_relative "ooxml/workbook_parser"
11
+ require_relative "ooxml/worksheet_parser"
12
+ require_relative "ooxml/worksheet_writer"
13
+ require_relative "ooxml/workbook_writer"
14
+
15
+ module Xlsxrb
16
+ # Low-level OOXML infrastructure layer.
17
+ # Handles ZIP extraction, SAX XML parsing, and XML generation
18
+ # in strict accordance with ECMA-376.
19
+ module Ooxml
20
+ Event = Data.define(:type, :args, :source)
21
+ end
22
+ end
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xlsxrb
4
+ # Helper class for building cell styles with a fluent DSL.
5
+ # Encapsulates font, fill, border, alignment, and number format properties.
6
+ class StyleBuilder
7
+ def initialize(name = nil)
8
+ @name = name
9
+ @font_props = {}
10
+ @fill_props = {}
11
+ @border_props = {}
12
+ @num_fmt_id = nil
13
+ @alignment = {}
14
+ end
15
+
16
+ attr_reader :name, :font_props, :fill_props, :border_props, :num_fmt_id, :alignment
17
+
18
+ # Applies option-style definitions so callers can use add_style(name, **opts)
19
+ # as an alternative to block-based fluent chaining.
20
+ def apply_options!(**opts)
21
+ bold(opts[:bold]) if opts.key?(:bold)
22
+ italic(opts[:italic]) if opts.key?(:italic)
23
+ size(opts[:size]) if opts.key?(:size)
24
+ font_name(opts[:font_name]) if opts.key?(:font_name)
25
+ font_color(opts[:font_color]) if opts.key?(:font_color)
26
+ underline(opts[:underline]) if opts.key?(:underline)
27
+ strike(opts[:strike]) if opts.key?(:strike)
28
+ vert_align(opts[:vert_align]) if opts.key?(:vert_align)
29
+
30
+ fill_color(opts[:fill_color]) if opts.key?(:fill_color)
31
+ if opts.key?(:fill_pattern)
32
+ pattern = opts[:fill_pattern] || {}
33
+ fill_pattern(
34
+ pattern[:pattern] || "solid",
35
+ fg_color: pattern[:fg_color],
36
+ bg_color: pattern[:bg_color]
37
+ )
38
+ end
39
+ fill_gradient(**opts[:fill_gradient]) if opts.key?(:fill_gradient) && opts[:fill_gradient]
40
+
41
+ border_all(**opts[:border_all]) if opts.key?(:border_all) && opts[:border_all]
42
+ border_left(**opts[:border_left]) if opts.key?(:border_left) && opts[:border_left]
43
+ border_right(**opts[:border_right]) if opts.key?(:border_right) && opts[:border_right]
44
+ border_top(**opts[:border_top]) if opts.key?(:border_top) && opts[:border_top]
45
+ border_bottom(**opts[:border_bottom]) if opts.key?(:border_bottom) && opts[:border_bottom]
46
+
47
+ number_format(opts[:number_format]) if opts.key?(:number_format)
48
+
49
+ align_horizontal(opts[:align_horizontal]) if opts.key?(:align_horizontal)
50
+ align_vertical(opts[:align_vertical]) if opts.key?(:align_vertical)
51
+ wrap_text(opts[:wrap_text]) if opts.key?(:wrap_text)
52
+ text_rotation(opts[:text_rotation]) if opts.key?(:text_rotation)
53
+ indent(opts[:indent]) if opts.key?(:indent)
54
+ shrink_to_fit(opts[:shrink_to_fit]) if opts.key?(:shrink_to_fit)
55
+
56
+ self
57
+ end
58
+
59
+ # --- Font Properties ---
60
+
61
+ # rubocop:disable Style/OptionalBooleanParameter
62
+ def bold(value = true)
63
+ @font_props[:bold] = value
64
+ self
65
+ end
66
+
67
+ def italic(value = true)
68
+ @font_props[:italic] = value
69
+ self
70
+ end
71
+
72
+ def size(size_value)
73
+ @font_props[:sz] = size_value.to_i
74
+ self
75
+ end
76
+
77
+ def font_name(name)
78
+ @font_props[:name] = name
79
+ self
80
+ end
81
+
82
+ def font_color(color)
83
+ @font_props[:color] = color
84
+ self
85
+ end
86
+
87
+ def underline(val = "single")
88
+ @font_props[:underline] = val
89
+ self
90
+ end
91
+
92
+ def strike(value = true)
93
+ @font_props[:strike] = value
94
+ self
95
+ end
96
+
97
+ def vert_align(value)
98
+ @font_props[:vert_align] = value
99
+ self
100
+ end
101
+ # rubocop:enable Style/OptionalBooleanParameter
102
+
103
+ # --- Fill Properties ---
104
+
105
+ def fill_pattern(pattern, fg_color: nil, bg_color: nil)
106
+ @fill_props[:pattern] = pattern
107
+ @fill_props[:fg_color] = fg_color if fg_color
108
+ @fill_props[:bg_color] = bg_color if bg_color
109
+ self
110
+ end
111
+
112
+ def fill_color(color)
113
+ @fill_props[:pattern] = "solid"
114
+ @fill_props[:fg_color] = color
115
+ self
116
+ end
117
+
118
+ def fill(pattern: "solid", fg_color: nil, bg_color: nil)
119
+ fill_pattern(pattern, fg_color: fg_color, bg_color: bg_color)
120
+ end
121
+
122
+ def fill_gradient(type:, degree: nil, stops: [])
123
+ @fill_props[:gradient] = {
124
+ type: type,
125
+ degree: degree,
126
+ stops: stops
127
+ }.compact
128
+ self
129
+ end
130
+
131
+ # --- Border Properties ---
132
+
133
+ def border_all(style: "thin", color: nil)
134
+ color_opt = color ? { color: color } : {}
135
+ @border_props[:left] = { style: style, **color_opt }
136
+ @border_props[:right] = { style: style, **color_opt }
137
+ @border_props[:top] = { style: style, **color_opt }
138
+ @border_props[:bottom] = { style: style, **color_opt }
139
+ self
140
+ end
141
+
142
+ def border_left(style: "thin", color: nil)
143
+ @border_props[:left] = { style: style, color: color }.compact
144
+ self
145
+ end
146
+
147
+ def border_right(style: "thin", color: nil)
148
+ @border_props[:right] = { style: style, color: color }.compact
149
+ self
150
+ end
151
+
152
+ def border_top(style: "thin", color: nil)
153
+ @border_props[:top] = { style: style, color: color }.compact
154
+ self
155
+ end
156
+
157
+ def border_bottom(style: "thin", color: nil)
158
+ @border_props[:bottom] = { style: style, color: color }.compact
159
+ self
160
+ end
161
+
162
+ # rubocop:disable Naming/MethodParameterName
163
+ def border_diagonal(style: "thin", color: nil, up: false, down: false)
164
+ @border_props[:diagonal] = { style: style, color: color }.compact
165
+ @border_props[:diagonal_up] = true if up
166
+ @border_props[:diagonal_down] = true if down
167
+ self
168
+ end
169
+ # rubocop:enable Naming/MethodParameterName
170
+
171
+ # --- Alignment Properties ---
172
+
173
+ def align_horizontal(value)
174
+ @alignment[:horizontal] = value
175
+ self
176
+ end
177
+
178
+ def align_vertical(value)
179
+ @alignment[:vertical] = value
180
+ self
181
+ end
182
+
183
+ # rubocop:disable Style/OptionalBooleanParameter
184
+ def wrap_text(value = true)
185
+ @alignment[:wrap_text] = value
186
+ self
187
+ end
188
+
189
+ def shrink_to_fit(value = true)
190
+ @alignment[:shrink_to_fit] = value
191
+ self
192
+ end
193
+ # rubocop:enable Style/OptionalBooleanParameter
194
+
195
+ def text_rotation(value)
196
+ @alignment[:text_rotation] = value
197
+ self
198
+ end
199
+
200
+ def indent(value)
201
+ @alignment[:indent] = value.to_i
202
+ self
203
+ end
204
+
205
+ # --- Number Format ---
206
+
207
+ def number_format(num_fmt_id)
208
+ @num_fmt_id = num_fmt_id
209
+ self
210
+ end
211
+ alias num_fmt number_format
212
+
213
+ # Register this style with the given Writer, returning the style_id.
214
+ # writer:: Xlsxrb::Ooxml::Writer instance
215
+ def register_with(writer)
216
+ font_id = 0
217
+ fill_id = 0
218
+ border_id = 0
219
+
220
+ font_id = writer.add_font(**@font_props) if @font_props.any?
221
+ fill_id = writer.add_fill(**@fill_props) if @fill_props.any?
222
+ border_id = writer.add_border(**@border_props) if @border_props.any?
223
+
224
+ resolved_num_fmt_id = if @num_fmt_id.is_a?(String)
225
+ writer.add_number_format(@num_fmt_id)
226
+ else
227
+ @num_fmt_id
228
+ end
229
+
230
+ cell_style_opts = {
231
+ num_fmt_id: resolved_num_fmt_id,
232
+ font_id: font_id,
233
+ fill_id: fill_id,
234
+ border_id: border_id
235
+ }
236
+ cell_style_opts[:alignment] = @alignment if @alignment.any?
237
+
238
+ writer.add_cell_style(**cell_style_opts)
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xlsxrb
4
+ VERSION = "0.1.0"
5
+ end