xlsxrb 0.1.15 → 0.1.16

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +61 -0
  3. data/README.md +22 -3
  4. data/benchmark/micro_ips.rb +70 -0
  5. data/lib/xlsxrb/elements/styles.rb +427 -15
  6. data/lib/xlsxrb/elements/workbook.rb +31 -4
  7. data/lib/xlsxrb/elements/worksheet.rb +22 -7
  8. data/lib/xlsxrb/ooxml/cfb.rb +10 -0
  9. data/lib/xlsxrb/ooxml/reader/listeners/core_listeners.rb +56 -38
  10. data/lib/xlsxrb/ooxml/reader/listeners/feature_listeners.rb +7 -1
  11. data/lib/xlsxrb/ooxml/reader/listeners/style_listeners.rb +12 -7
  12. data/lib/xlsxrb/ooxml/reader.rb +165 -136
  13. data/lib/xlsxrb/ooxml/shared_strings_parser.rb +83 -28
  14. data/lib/xlsxrb/ooxml/styles_parser.rb +57 -14
  15. data/lib/xlsxrb/ooxml/utils.rb +59 -2
  16. data/lib/xlsxrb/ooxml/workbook_parser.rb +42 -3
  17. data/lib/xlsxrb/ooxml/workbook_writer.rb +215 -50
  18. data/lib/xlsxrb/ooxml/worksheet_parser.rb +192 -123
  19. data/lib/xlsxrb/ooxml/worksheet_writer.rb +238 -108
  20. data/lib/xlsxrb/ooxml/writer/drawing_xml.rb +16 -12
  21. data/lib/xlsxrb/ooxml/writer/styles_xml.rb +64 -15
  22. data/lib/xlsxrb/ooxml/writer.rb +31 -28
  23. data/lib/xlsxrb/ooxml/xml_builder.rb +12 -4
  24. data/lib/xlsxrb/ooxml/xml_parser.rb +45 -0
  25. data/lib/xlsxrb/ooxml/zip_reader.rb +48 -29
  26. data/lib/xlsxrb/stream_row.rb +4 -3
  27. data/lib/xlsxrb/stream_sheet.rb +26 -6
  28. data/lib/xlsxrb/stream_writer.rb +35 -14
  29. data/lib/xlsxrb/utils.rb +15 -0
  30. data/lib/xlsxrb/version.rb +1 -1
  31. data/lib/xlsxrb/workbook_builder.rb +110 -10
  32. data/lib/xlsxrb/worksheet_builder.rb +15 -7
  33. data/lib/xlsxrb.rb +416 -108
  34. data/sig/generated/xlsxrb/elements/styles.rbs +271 -5
  35. data/sig/generated/xlsxrb/elements/workbook.rbs +26 -3
  36. data/sig/generated/xlsxrb/elements/worksheet.rbs +12 -2
  37. data/sig/generated/xlsxrb/ooxml/reader/listeners/style_listeners.rbs +2 -0
  38. data/sig/generated/xlsxrb/ooxml/reader.rbs +12 -0
  39. data/sig/generated/xlsxrb/ooxml/shared_strings_parser.rbs +3 -1
  40. data/sig/generated/xlsxrb/ooxml/styles_parser.rbs +2 -0
  41. data/sig/generated/xlsxrb/ooxml/utils.rbs +22 -0
  42. data/sig/generated/xlsxrb/ooxml/workbook_parser.rbs +7 -0
  43. data/sig/generated/xlsxrb/ooxml/workbook_writer.rbs +12 -0
  44. data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +18 -2
  45. data/sig/generated/xlsxrb/ooxml/worksheet_writer.rbs +3 -1
  46. data/sig/generated/xlsxrb/ooxml/writer/styles_xml.rbs +5 -2
  47. data/sig/generated/xlsxrb/ooxml/xml_builder.rbs +4 -1
  48. data/sig/generated/xlsxrb/ooxml/zip_reader.rbs +6 -6
  49. data/sig/generated/xlsxrb/stream_row.rbs +3 -2
  50. data/sig/generated/xlsxrb/stream_sheet.rbs +21 -5
  51. data/sig/generated/xlsxrb/stream_writer.rbs +18 -11
  52. data/sig/generated/xlsxrb/utils.rbs +13 -0
  53. data/sig/generated/xlsxrb/worksheet_builder.rbs +3 -2
  54. data/sig/generated/xlsxrb.rbs +47 -10
  55. metadata +2 -1
@@ -24,12 +24,15 @@ module Xlsxrb
24
24
  @runs = []
25
25
  @current_font = {}
26
26
  @has_runs = false
27
+ @inside_rph = false
27
28
  end
28
29
 
29
30
  def start_element(_uri, local_name, qname, attributes)
30
31
  name = element_name(local_name, qname)
31
32
 
32
33
  case name
34
+ when "rPh"
35
+ @inside_rph = true
33
36
  when "si"
34
37
  @inside_si = true
35
38
  @text_buffer = +""
@@ -74,7 +77,7 @@ module Xlsxrb
74
77
  when "scheme"
75
78
  @current_font[:scheme] = attributes["val"] if @inside_rpr
76
79
  when "t"
77
- @inside_t = @inside_si
80
+ @inside_t = @inside_si && !@inside_rph
78
81
  @text_buffer = +"" if @inside_r
79
82
  end
80
83
  end
@@ -87,12 +90,16 @@ module Xlsxrb
87
90
  name = element_name(local_name, qname)
88
91
 
89
92
  case name
93
+ when "rPh"
94
+ @inside_rph = false
90
95
  when "t"
91
96
  @inside_t = false
92
97
  when "rPr"
93
98
  @inside_rpr = false
94
99
  when "r"
95
- run = { text: @text_buffer.dup }
100
+ decoded_text = XmlBuilder.unescape(@text_buffer)
101
+ decoded_text = decoded_text.gsub("\r\n", "\n").tr("\r", "\n") if decoded_text.include?("\r")
102
+ run = { text: decoded_text }
96
103
  run[:font] = @current_font.dup unless @current_font.empty?
97
104
  @runs << run
98
105
  @inside_r = false
@@ -100,7 +107,9 @@ module Xlsxrb
100
107
  @strings << if @has_runs
101
108
  Xlsxrb::Elements::RichText.new(runs: @runs)
102
109
  else
103
- @text_buffer.dup
110
+ decoded_text = XmlBuilder.unescape(@text_buffer)
111
+ decoded_text = decoded_text.gsub("\r\n", "\n").tr("\r", "\n") if decoded_text.include?("\r")
112
+ decoded_text
104
113
  end
105
114
  @inside_si = false
106
115
  @text_buffer = +""
@@ -145,18 +154,23 @@ module Xlsxrb
145
154
  @is_has_runs = false
146
155
  @is_current_font = {}
147
156
  @is_run_text = +""
157
+ @inside_rph = false
158
+ @has_inline_text = false
148
159
  end
149
160
 
150
161
  def start_element(_uri, local_name, qname, attributes)
151
162
  name = element_name(local_name, qname)
152
163
 
153
164
  case name
165
+ when "rPh"
166
+ @inside_rph = true
154
167
  when "row"
155
168
  parse_row_attributes(attributes)
156
169
  when "c"
157
170
  @current_cell_ref = attributes["r"]
158
171
  @current_cell_type = attributes["t"]
159
172
  @current_cell_ph = %w[1 true].include?(attributes["ph"])
173
+ @has_inline_text = false
160
174
  @value_buffer = +""
161
175
  @inline_text_buffer = +""
162
176
  @formula_buffer = +""
@@ -188,6 +202,7 @@ module Xlsxrb
188
202
  @formula_r1 = attributes["r1"] if attributes["r1"]
189
203
  @formula_r2 = attributes["r2"] if attributes["r2"]
190
204
  when "is"
205
+ @has_inline_text = true
191
206
  @inside_is = true if @current_cell_type == "inlineStr"
192
207
  when "r"
193
208
  if @inside_is
@@ -231,11 +246,15 @@ module Xlsxrb
231
246
  when "scheme"
232
247
  @is_current_font[:scheme] = attributes["val"] if @inside_is_rpr
233
248
  when "t"
234
- if @inside_is_r
235
- @is_run_text = +""
236
- @inside_inline_text = true
237
- elsif @inside_is || (@current_cell_type == "inlineStr" && !@current_cell_ref.nil?)
238
- @inside_inline_text = true
249
+ unless @inside_rph
250
+ if @inside_is_r
251
+ @is_run_text = +""
252
+ @inside_inline_text = true
253
+ @has_inline_text = true
254
+ elsif @inside_is || (@current_cell_type == "inlineStr" && !@current_cell_ref.nil?)
255
+ @inside_inline_text = true
256
+ @has_inline_text = true
257
+ end
239
258
  end
240
259
  end
241
260
  end
@@ -256,6 +275,8 @@ module Xlsxrb
256
275
  name = element_name(local_name, qname)
257
276
 
258
277
  case name
278
+ when "rPh"
279
+ @inside_rph = false
259
280
  when "v"
260
281
  @inside_value = false
261
282
  when "f"
@@ -309,7 +330,14 @@ module Xlsxrb
309
330
  return if @current_cell_ref.nil?
310
331
 
311
332
  unless @formula_buffer.empty? && @formula_type.nil?
312
- cached = @value_buffer.empty? ? nil : @value_buffer.dup
333
+ cached = if @value_buffer.empty?
334
+ nil
335
+ elsif @current_cell_type == "b"
336
+ v = @value_buffer.strip
337
+ v == "1" || v.casecmp?("true")
338
+ else
339
+ @value_buffer.dup
340
+ end
313
341
  f_type = { "shared" => :shared, "array" => :array, "dataTable" => :data_table }[@formula_type]
314
342
  @cells[@current_cell_ref] = Xlsxrb::Elements::Formula.new(
315
343
  expression: @formula_buffer.dup,
@@ -328,39 +356,29 @@ module Xlsxrb
328
356
  return
329
357
  end
330
358
 
331
- case @current_cell_type
332
- when "inlineStr"
333
- @cells[@current_cell_ref] = if @is_has_runs
334
- Xlsxrb::Elements::RichText.new(runs: @is_runs.map(&:dup))
335
- else
336
- @inline_text_buffer.dup
337
- end
338
- when "s"
339
- index = @value_buffer.to_i
340
- @cells[@current_cell_ref] = @shared_strings[index] || ""
341
- when "e"
342
- code = @value_buffer.dup
343
- @cells[@current_cell_ref] = if Xlsxrb::Elements::VALID_ERROR_CODES.include?(code)
344
- Xlsxrb::Elements::CellError.new(code:)
345
- else
346
- code
347
- end
348
- when "b"
349
- @cells[@current_cell_ref] = @value_buffer.strip == "1"
350
- when nil, "", "n"
351
- return if @value_buffer.empty?
352
-
353
- raw = @value_buffer.dup
354
- @cells[@current_cell_ref] = numeric_value(raw)
359
+ if @current_cell_type == "inlineStr" && @is_has_runs
360
+ @cells[@current_cell_ref] = Xlsxrb::Elements::RichText.new(runs: @is_runs.map(&:dup))
361
+ return
355
362
  end
363
+
364
+ v = if @current_cell_type == "inlineStr"
365
+ @has_inline_text ? @inline_text_buffer : nil
366
+ else
367
+ @value_buffer.empty? ? nil : @value_buffer
368
+ end
369
+
370
+ val = Xlsxrb::Ooxml::WorksheetParser.resolve_cell_value(@current_cell_type, v, nil, @shared_strings)
371
+ return if val.nil? && (@current_cell_type.nil? || @current_cell_type == "" || @current_cell_type == "n" || @current_cell_type == "s" || @current_cell_type == "inlineStr")
372
+
373
+ @cells[@current_cell_ref] = if @current_cell_type == "e" && val && Xlsxrb::Elements::VALID_ERROR_CODES.include?(val)
374
+ Xlsxrb::Elements::CellError.new(code: val)
375
+ else
376
+ val
377
+ end
356
378
  end
357
379
 
358
380
  def numeric_value(raw)
359
- if raw.include?(".")
360
- raw.to_f
361
- else
362
- raw.to_i
363
- end
381
+ Xlsxrb::Ooxml::WorksheetParser.numeric_cell_value(raw)
364
382
  end
365
383
 
366
384
  def element_name(local_name, qname)
@@ -962,11 +962,14 @@ module Xlsxrb
962
962
  when "formula2"
963
963
  @inside_formula2 = true
964
964
  @text_buffer = +""
965
+ when "sqref"
966
+ @inside_sqref = true
967
+ @text_buffer = +""
965
968
  end
966
969
  end
967
970
 
968
971
  def characters(text)
969
- @text_buffer << text if @inside_formula1 || @inside_formula2
972
+ @text_buffer << text if @inside_formula1 || @inside_formula2 || @inside_sqref
970
973
  end
971
974
 
972
975
  def end_element(_uri, local_name, qname)
@@ -978,6 +981,9 @@ module Xlsxrb
978
981
  when "formula2"
979
982
  @current_dv[:formula2] = @text_buffer.dup if @current_dv
980
983
  @inside_formula2 = false
984
+ when "sqref"
985
+ @current_dv[:sqref] = @text_buffer.strip if @current_dv && (@current_dv[:sqref].nil? || @current_dv[:sqref].empty?)
986
+ @inside_sqref = false
981
987
  when "dataValidation"
982
988
  @validations << @current_dv if @current_dv
983
989
  @current_dv = nil
@@ -135,19 +135,19 @@ module Xlsxrb
135
135
  when "font"
136
136
  @current_font = {} if @inside_fonts || @inside_dxfs
137
137
  when "b"
138
- @current_font[:bold] = true if @current_font
138
+ @current_font[:bold] = truthy_val?(attributes) if @current_font
139
139
  when "i"
140
- @current_font[:italic] = true if @current_font
140
+ @current_font[:italic] = truthy_val?(attributes) if @current_font
141
141
  when "strike"
142
- @current_font[:strike] = true if @current_font
142
+ @current_font[:strike] = truthy_val?(attributes) if @current_font
143
143
  when "shadow"
144
- @current_font[:shadow] = true if @current_font
144
+ @current_font[:shadow] = truthy_val?(attributes) if @current_font
145
145
  when "outline"
146
- @current_font[:outline] = true if @current_font
146
+ @current_font[:outline] = truthy_val?(attributes) if @current_font
147
147
  when "condense"
148
- @current_font[:condense] = true if @current_font
148
+ @current_font[:condense] = truthy_val?(attributes) if @current_font
149
149
  when "extend"
150
- @current_font[:extend] = true if @current_font
150
+ @current_font[:extend] = truthy_val?(attributes) if @current_font
151
151
  when "u"
152
152
  if @current_font
153
153
  val = attributes["val"]
@@ -303,6 +303,11 @@ module Xlsxrb
303
303
 
304
304
  private
305
305
 
306
+ def truthy_val?(attributes)
307
+ val = attributes["val"]
308
+ val.nil? || (val != "0" && val.downcase != "false")
309
+ end
310
+
306
311
  def parse_color(attributes)
307
312
  if @inside_mru_colors
308
313
  c = {}