xlsxrb 0.1.11 → 0.1.13

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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -34
  3. data/README.md +26 -21
  4. data/lib/xlsxrb/chart_builder.rb +18 -18
  5. data/lib/xlsxrb/dsl_helpers.rb +175 -0
  6. data/lib/xlsxrb/elements/cell.rb +56 -22
  7. data/lib/xlsxrb/elements/column.rb +27 -3
  8. data/lib/xlsxrb/elements/row.rb +27 -8
  9. data/lib/xlsxrb/elements/workbook.rb +27 -14
  10. data/lib/xlsxrb/elements/worksheet.rb +17 -1
  11. data/lib/xlsxrb/ooxml/cfb.rb +9 -6
  12. data/lib/xlsxrb/ooxml/crypto/agile.rb +5 -5
  13. data/lib/xlsxrb/ooxml/reader/listeners/core_listeners.rb +784 -0
  14. data/lib/xlsxrb/ooxml/reader/listeners/drawing_listeners.rb +3478 -0
  15. data/lib/xlsxrb/ooxml/reader/listeners/feature_listeners.rb +1796 -0
  16. data/lib/xlsxrb/ooxml/reader/listeners/style_listeners.rb +428 -0
  17. data/lib/xlsxrb/ooxml/reader/listeners.rb +4 -6446
  18. data/lib/xlsxrb/ooxml/shared_strings_parser.rb +3 -5
  19. data/lib/xlsxrb/ooxml/utils.rb +5 -6
  20. data/lib/xlsxrb/ooxml/workbook_writer.rb +5 -7
  21. data/lib/xlsxrb/ooxml/worksheet_parser.rb +172 -9
  22. data/lib/xlsxrb/ooxml/writer/drawing_xml.rb +1602 -0
  23. data/lib/xlsxrb/ooxml/writer/features_xml.rb +731 -0
  24. data/lib/xlsxrb/ooxml/writer/styles_xml.rb +348 -0
  25. data/lib/xlsxrb/ooxml/writer.rb +9 -2646
  26. data/lib/xlsxrb/ooxml/xml_builder.rb +31 -2
  27. data/lib/xlsxrb/ooxml/zip_generator.rb +26 -5
  28. data/lib/xlsxrb/ooxml/zip_reader.rb +364 -78
  29. data/lib/xlsxrb/ooxml/zip_writer.rb +32 -5
  30. data/lib/xlsxrb/stream_sheet.rb +94 -6
  31. data/lib/xlsxrb/stream_writer.rb +48 -115
  32. data/lib/xlsxrb/style_builder.rb +10 -1
  33. data/lib/xlsxrb/version.rb +1 -1
  34. data/lib/xlsxrb/workbook_builder.rb +1 -1
  35. data/lib/xlsxrb/worksheet_builder.rb +19 -81
  36. data/lib/xlsxrb.rb +449 -171
  37. data/sig/generated/xlsxrb/chart_builder.rbs +36 -36
  38. data/sig/generated/xlsxrb/dsl_helpers.rbs +92 -0
  39. data/sig/generated/xlsxrb/elements/cell.rbs +29 -0
  40. data/sig/generated/xlsxrb/elements/column.rbs +32 -21
  41. data/sig/generated/xlsxrb/elements/row.rbs +94 -14
  42. data/sig/generated/xlsxrb/elements/workbook.rbs +102 -10
  43. data/sig/generated/xlsxrb/elements/worksheet.rbs +9 -0
  44. data/sig/generated/xlsxrb/ooxml/reader/listeners/core_listeners.rbs +173 -0
  45. data/sig/generated/xlsxrb/ooxml/reader/listeners/drawing_listeners.rbs +400 -0
  46. data/sig/generated/xlsxrb/ooxml/reader/listeners/feature_listeners.rbs +442 -0
  47. data/sig/generated/xlsxrb/ooxml/reader/listeners/style_listeners.rbs +80 -0
  48. data/sig/generated/xlsxrb/ooxml/reader/listeners.rbs +0 -1072
  49. data/sig/generated/xlsxrb/ooxml/shared_strings_parser.rbs +0 -2
  50. data/sig/generated/xlsxrb/ooxml/utils.rbs +2 -0
  51. data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +5 -4
  52. data/sig/generated/xlsxrb/ooxml/writer/drawing_xml.rbs +79 -0
  53. data/sig/generated/xlsxrb/ooxml/writer/features_xml.rbs +83 -0
  54. data/sig/generated/xlsxrb/ooxml/writer/styles_xml.rbs +53 -0
  55. data/sig/generated/xlsxrb/ooxml/writer.rbs +6 -185
  56. data/sig/generated/xlsxrb/ooxml/xml_builder.rbs +18 -0
  57. data/sig/generated/xlsxrb/ooxml/zip_generator.rbs +15 -0
  58. data/sig/generated/xlsxrb/ooxml/zip_reader.rbs +96 -8
  59. data/sig/generated/xlsxrb/ooxml/zip_writer.rbs +5 -1
  60. data/sig/generated/xlsxrb/stream_sheet.rbs +43 -3
  61. data/sig/generated/xlsxrb/style_builder.rbs +7 -0
  62. data/sig/generated/xlsxrb.rbs +18 -11
  63. metadata +22 -144
  64. data/.devcontainer/Dockerfile +0 -65
  65. data/.devcontainer/devcontainer.json +0 -17
  66. data/.gem_rbs_collection/ast/2.4/.rbs_meta.yaml +0 -9
  67. data/.gem_rbs_collection/ast/2.4/ast.rbs +0 -73
  68. data/.gem_rbs_collection/concurrent-ruby/1.1/.rbs_meta.yaml +0 -9
  69. data/.gem_rbs_collection/concurrent-ruby/1.1/array.rbs +0 -4
  70. data/.gem_rbs_collection/concurrent-ruby/1.1/atomic_reference.rbs +0 -16
  71. data/.gem_rbs_collection/concurrent-ruby/1.1/executor.rbs +0 -96
  72. data/.gem_rbs_collection/concurrent-ruby/1.1/hash.rbs +0 -4
  73. data/.gem_rbs_collection/concurrent-ruby/1.1/map.rbs +0 -68
  74. data/.gem_rbs_collection/concurrent-ruby/1.1/promises.rbs +0 -249
  75. data/.gem_rbs_collection/concurrent-ruby/1.1/set.rbs +0 -4
  76. data/.gem_rbs_collection/concurrent-ruby/1.1/timer_task.rbs +0 -47
  77. data/.gem_rbs_collection/concurrent-ruby/1.1/utility/processor_counter.rbs +0 -5
  78. data/.gem_rbs_collection/csv/3.3/.rbs_meta.yaml +0 -9
  79. data/.gem_rbs_collection/csv/3.3/csv.rbs +0 -3871
  80. data/.gem_rbs_collection/csv/3.3/manifest.yaml +0 -3
  81. data/.gem_rbs_collection/lint_roller/1.1/.rbs_meta.yaml +0 -9
  82. data/.gem_rbs_collection/lint_roller/1.1/lint_roller.rbs +0 -48
  83. data/.gem_rbs_collection/listen/3.9/.rbs_meta.yaml +0 -9
  84. data/.gem_rbs_collection/listen/3.9/listen.rbs +0 -25
  85. data/.gem_rbs_collection/listen/3.9/listener.rbs +0 -24
  86. data/.gem_rbs_collection/logger/1.7/.rbs_meta.yaml +0 -9
  87. data/.gem_rbs_collection/logger/1.7/formatter.rbs +0 -45
  88. data/.gem_rbs_collection/logger/1.7/log_device.rbs +0 -100
  89. data/.gem_rbs_collection/logger/1.7/logger.rbs +0 -796
  90. data/.gem_rbs_collection/logger/1.7/manifest.yaml +0 -2
  91. data/.gem_rbs_collection/logger/1.7/period.rbs +0 -17
  92. data/.gem_rbs_collection/logger/1.7/severity.rbs +0 -34
  93. data/.gem_rbs_collection/nokogiri/1.11/.rbs_meta.yaml +0 -9
  94. data/.gem_rbs_collection/nokogiri/1.11/nokogiri.rbs +0 -2332
  95. data/.gem_rbs_collection/nokogiri/1.11/patch.rbs +0 -4
  96. data/.gem_rbs_collection/parallel/1.20/.rbs_meta.yaml +0 -9
  97. data/.gem_rbs_collection/parallel/1.20/parallel.rbs +0 -86
  98. data/.gem_rbs_collection/parser/3.2/.rbs_meta.yaml +0 -9
  99. data/.gem_rbs_collection/parser/3.2/manifest.yaml +0 -7
  100. data/.gem_rbs_collection/parser/3.2/parser.rbs +0 -194
  101. data/.gem_rbs_collection/parser/3.2/polyfill.rbs +0 -4
  102. data/.gem_rbs_collection/rainbow/3.0/.rbs_meta.yaml +0 -9
  103. data/.gem_rbs_collection/rainbow/3.0/global.rbs +0 -7
  104. data/.gem_rbs_collection/rainbow/3.0/presenter.rbs +0 -209
  105. data/.gem_rbs_collection/rainbow/3.0/rainbow.rbs +0 -5
  106. data/.gem_rbs_collection/rake/13.0/.rbs_meta.yaml +0 -9
  107. data/.gem_rbs_collection/rake/13.0/manifest.yaml +0 -2
  108. data/.gem_rbs_collection/rake/13.0/rake.rbs +0 -39
  109. data/.gem_rbs_collection/regexp_parser/2.8/.rbs_meta.yaml +0 -9
  110. data/.gem_rbs_collection/regexp_parser/2.8/regexp_parser.rbs +0 -17
  111. data/.gem_rbs_collection/rubocop/1.57/.rbs_meta.yaml +0 -9
  112. data/.gem_rbs_collection/rubocop/1.57/rubocop.rbs +0 -208
  113. data/.gem_rbs_collection/rubocop-ast/1.46/.rbs_meta.yaml +0 -9
  114. data/.gem_rbs_collection/rubocop-ast/1.46/rubocop-ast.rbs +0 -903
  115. data/.gem_rbs_collection/rubyzip/3.2/.rbs_meta.yaml +0 -9
  116. data/.gem_rbs_collection/rubyzip/3.2/manifest.yaml +0 -8
  117. data/.gem_rbs_collection/rubyzip/3.2/zip/central_directory.rbs +0 -42
  118. data/.gem_rbs_collection/rubyzip/3.2/zip/compressor.rbs +0 -5
  119. data/.gem_rbs_collection/rubyzip/3.2/zip/constants.rbs +0 -47
  120. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/aes_encryption.rbs +0 -30
  121. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/decrypted_io.rbs +0 -9
  122. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/encryption.rbs +0 -7
  123. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/null_encryption.rbs +0 -19
  124. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/traditional_encryption.rbs +0 -31
  125. data/.gem_rbs_collection/rubyzip/3.2/zip/decompressor.rbs +0 -18
  126. data/.gem_rbs_collection/rubyzip/3.2/zip/deflater.rbs +0 -12
  127. data/.gem_rbs_collection/rubyzip/3.2/zip/dirtyable.rbs +0 -11
  128. data/.gem_rbs_collection/rubyzip/3.2/zip/dos_time.rbs +0 -13
  129. data/.gem_rbs_collection/rubyzip/3.2/zip/entry.rbs +0 -95
  130. data/.gem_rbs_collection/rubyzip/3.2/zip/entry_set.rbs +0 -31
  131. data/.gem_rbs_collection/rubyzip/3.2/zip/errors.rbs +0 -58
  132. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/aes.rbs +0 -24
  133. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/generic.rbs +0 -17
  134. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/ntfs.rbs +0 -23
  135. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/old_unix.rbs +0 -22
  136. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/universal_time.rbs +0 -30
  137. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unix.rbs +0 -20
  138. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unknown.rbs +0 -15
  139. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/zip64.rbs +0 -26
  140. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field.rbs +0 -21
  141. data/.gem_rbs_collection/rubyzip/3.2/zip/file.rbs +0 -131
  142. data/.gem_rbs_collection/rubyzip/3.2/zip/file_split.rbs +0 -14
  143. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/dir.rbs +0 -33
  144. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/directory_iterator.rbs +0 -21
  145. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file.rbs +0 -63
  146. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file_stat.rbs +0 -55
  147. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/zip_file_name_mapper.rbs +0 -35
  148. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem.rbs +0 -7
  149. data/.gem_rbs_collection/rubyzip/3.2/zip/inflater.rbs +0 -10
  150. data/.gem_rbs_collection/rubyzip/3.2/zip/input_stream.rbs +0 -22
  151. data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_input_stream.rbs +0 -29
  152. data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_output_stream.rbs +0 -17
  153. data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras.rbs +0 -13
  154. data/.gem_rbs_collection/rubyzip/3.2/zip/null_compressor.rbs +0 -10
  155. data/.gem_rbs_collection/rubyzip/3.2/zip/null_decompressor.rbs +0 -8
  156. data/.gem_rbs_collection/rubyzip/3.2/zip/null_input_stream.rbs +0 -6
  157. data/.gem_rbs_collection/rubyzip/3.2/zip/output_stream.rbs +0 -30
  158. data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_compressor.rbs +0 -10
  159. data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_decompressor.rbs +0 -10
  160. data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_directory.rbs +0 -5
  161. data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_stream.rbs +0 -15
  162. data/.gem_rbs_collection/rubyzip/3.2/zip/version.rbs +0 -3
  163. data/.gem_rbs_collection/rubyzip/3.2/zip.rbs +0 -40
  164. data/Rakefile +0 -567
  165. data/Steepfile +0 -18
  166. data/benchmark.rb +0 -406
  167. data/docs/ARCHITECTURE.md +0 -514
  168. data/docs/DEVELOPMENT.md +0 -133
  169. data/docs/PEER_LIBRARIES.md +0 -121
  170. data/docs/QUALITY_ASSURANCE.md +0 -25
  171. data/docs/SPEC_SOURCES.md +0 -48
  172. data/docs/assets/benchmark_results.svg +0 -144
  173. data/docs/assets/lsp_autocompletion.png +0 -0
  174. data/docs/assets/playground_preview.png +0 -0
  175. data/docs/coi-serviceworker.js +0 -82
  176. data/docs/office_thread.js +0 -77
  177. data/docs/preview.html +0 -719
  178. data/docs/visual/VisualGallery.md +0 -4672
  179. data/docs/wasm/ruby.wasm +0 -0
  180. data/docs/wasm/wasm_doc_helper.css +0 -351
  181. data/docs/wasm/wasm_doc_helper.js +0 -439
  182. data/docs/zeta.js +0 -1107
  183. data/rbs_collection.lock.yaml +0 -252
  184. data/rbs_collection.yaml +0 -19
  185. data/vendor/sdk_runner/Program.cs +0 -93
  186. data/vendor/sdk_runner/sdk_runner.csproj +0 -13
@@ -0,0 +1,3478 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ require "rexml/parsers/sax2parser"
6
+ require "rexml/sax2listener"
7
+
8
+ module Xlsxrb
9
+ module Ooxml
10
+ class Reader
11
+ # SAX2 listener for parsing drawing XML to extract image information.
12
+ class DrawingImagesListener
13
+ include REXML::SAX2Listener
14
+
15
+ attr_reader :images
16
+
17
+ def initialize
18
+ @images = []
19
+ @current_image = nil
20
+ @inside_anchor = false
21
+ @inside_pic = false
22
+ @inside_from = false
23
+ @inside_to = false
24
+ @current_field = nil
25
+ @text_buffer = +""
26
+ @anchor_from = {}
27
+ @anchor_to = {}
28
+ @inside_ln = false
29
+ @inside_solid_fill = false
30
+ end
31
+
32
+ def start_element(_uri, local_name, qname, attributes)
33
+ name = element_name(local_name, qname)
34
+ case name
35
+ when "twoCellAnchor", "oneCellAnchor"
36
+ @inside_anchor = true
37
+ @anchor_from = {}
38
+ @anchor_to = {}
39
+ @anchor_edit_as = attributes["editAs"]
40
+ @anchor_published = %w[1 true].include?(attributes["fPublished"])
41
+ when "pic"
42
+ @inside_pic = true
43
+ @current_image = {}
44
+ @current_image[:macro] = attributes["macro"] if attributes["macro"] && !attributes["macro"].empty?
45
+ when "cNvPr"
46
+ if @inside_pic && @current_image
47
+ @current_image[:name] = attributes["name"] if attributes["name"]
48
+ @current_image[:id] = attributes["id"]&.to_i
49
+ @current_image[:description] = attributes["descr"] if attributes["descr"]
50
+ @current_image[:title] = attributes["title"] if attributes["title"]
51
+ @current_image[:hidden] = %w[1 true].include?(attributes["hidden"]) if attributes["hidden"]
52
+ end
53
+ when "blip"
54
+ rid = attributes["r:embed"] || attributes["embed"]
55
+ @current_image[:embed_rid] = rid if @inside_pic && @current_image && rid
56
+ when "alphaModFix"
57
+ @current_image[:alpha_mod_fix] = attributes["amt"].to_i if @inside_pic && @current_image && attributes["amt"]
58
+ when "srcRect"
59
+ if @inside_pic && @current_image
60
+ sr = {}
61
+ sr[:top] = attributes["t"].to_i if attributes["t"]
62
+ sr[:bottom] = attributes["b"].to_i if attributes["b"]
63
+ sr[:left] = attributes["l"].to_i if attributes["l"]
64
+ sr[:right] = attributes["r"].to_i if attributes["r"]
65
+ @current_image[:src_rect] = sr unless sr.empty?
66
+ end
67
+ when "picLocks"
68
+ if @inside_pic && @current_image
69
+ @current_image[:no_change_aspect] = true if %w[1 true].include?(attributes["noChangeAspect"])
70
+ @current_image[:no_crop] = true if %w[1 true].include?(attributes["noCrop"])
71
+ end
72
+ when "ln"
73
+ if @inside_pic && @current_image
74
+ @inside_ln = true
75
+ @current_image[:line_width] = attributes["w"].to_i if attributes["w"]
76
+ end
77
+ when "solidFill"
78
+ @inside_solid_fill = true if @inside_pic
79
+ when "srgbClr"
80
+ @current_image[:line_color] = attributes["val"] if @inside_pic && @current_image && @inside_solid_fill && @inside_ln && attributes["val"]
81
+ when "schemeClr"
82
+ @current_image[:line_color] = { scheme: attributes["val"] } if @inside_pic && @current_image && @inside_solid_fill && @inside_ln && attributes["val"]
83
+ when "xfrm"
84
+ @current_image[:rotation] = attributes["rot"].to_i if @inside_pic && @current_image && attributes["rot"]
85
+ when "from"
86
+ @inside_from = true if @inside_anchor
87
+ when "to"
88
+ @inside_to = true if @inside_anchor
89
+ when "ext"
90
+ if @inside_pic && @current_image
91
+ cx = attributes["cx"]
92
+ cy = attributes["cy"]
93
+ @current_image[:cx] = cx.to_i if cx
94
+ @current_image[:cy] = cy.to_i if cy
95
+ end
96
+ when "col", "colOff", "row", "rowOff"
97
+ @current_field = name
98
+ @text_buffer = +""
99
+ when "clientData"
100
+ if @inside_anchor
101
+ @anchor_locks_with_sheet = attributes["fLocksWithSheet"]
102
+ @anchor_prints_with_sheet = attributes["fPrintsWithSheet"]
103
+ end
104
+ end
105
+ end
106
+
107
+ def characters(text)
108
+ @text_buffer << text if @current_field
109
+ end
110
+
111
+ def end_element(_uri, local_name, qname)
112
+ name = element_name(local_name, qname)
113
+ case name
114
+ when "pic"
115
+ if @current_image && !@current_image.empty?
116
+ @anchor_from.each { |k, v| @current_image[:"from_#{k}"] = v }
117
+ @anchor_to.each { |k, v| @current_image[:"to_#{k}"] = v }
118
+ @current_image[:edit_as] = @anchor_edit_as if @anchor_edit_as
119
+ @images << @current_image
120
+ end
121
+ @current_image = nil
122
+ @inside_pic = false
123
+ when "twoCellAnchor", "oneCellAnchor"
124
+ @images.last[:locks_with_sheet] = @anchor_locks_with_sheet == "1" if @anchor_locks_with_sheet && !@images.empty?
125
+ @images.last[:prints_with_sheet] = @anchor_prints_with_sheet == "1" if @anchor_prints_with_sheet && !@images.empty?
126
+ @images.last[:published] = true if @anchor_published && !@images.empty?
127
+ @inside_anchor = false
128
+ @anchor_from = {}
129
+ @anchor_to = {}
130
+ @anchor_locks_with_sheet = nil
131
+ @anchor_prints_with_sheet = nil
132
+ @anchor_published = false
133
+ when "from"
134
+ @inside_from = false
135
+ when "to"
136
+ @inside_to = false
137
+ when "ln"
138
+ @inside_ln = false
139
+ when "solidFill"
140
+ @inside_solid_fill = false
141
+ when "col", "colOff", "row", "rowOff"
142
+ if @current_field
143
+ val = @text_buffer.to_i
144
+ if @inside_from
145
+ @anchor_from[@current_field] = val
146
+ elsif @inside_to
147
+ @anchor_to[@current_field] = val
148
+ end
149
+ end
150
+ @current_field = nil
151
+ end
152
+ end
153
+
154
+ private
155
+
156
+ def element_name(local_name, qname)
157
+ if local_name.nil? || local_name.empty?
158
+ qname.to_s.split(":").last
159
+ else
160
+ local_name
161
+ end
162
+ end
163
+ end
164
+
165
+ # SAX2 listener for parsing drawing XML to extract chart references.
166
+ class DrawingChartsListener
167
+ include REXML::SAX2Listener
168
+
169
+ attr_reader :charts
170
+
171
+ def initialize
172
+ @charts = []
173
+ @inside_graphic_frame = false
174
+ @current_chart = nil
175
+ @anchor_edit_as = nil
176
+ @inside_anchor = false
177
+ @inside_from = false
178
+ @inside_to = false
179
+ @anchor_from = {}
180
+ @anchor_to = {}
181
+ @current_field = nil
182
+ @text_buffer = +""
183
+ end
184
+
185
+ def start_element(_uri, local_name, qname, attributes)
186
+ name = element_name(local_name, qname)
187
+ case name
188
+ when "twoCellAnchor", "oneCellAnchor"
189
+ @anchor_edit_as = attributes["editAs"]
190
+ @anchor_published = %w[1 true].include?(attributes["fPublished"])
191
+ @inside_anchor = true
192
+ @anchor_from = {}
193
+ @anchor_to = {}
194
+ when "graphicFrame"
195
+ @inside_graphic_frame = true
196
+ @current_chart = {}
197
+ @current_chart[:frame_macro] = attributes["macro"] if attributes["macro"] && !attributes["macro"].empty?
198
+ when "cNvPr"
199
+ if @inside_graphic_frame && @current_chart
200
+ @current_chart[:name] = attributes["name"] if attributes["name"]
201
+ @current_chart[:description] = attributes["descr"] if attributes["descr"]
202
+ @current_chart[:frame_title] = attributes["title"] if attributes["title"]
203
+ @current_chart[:frame_hidden] = %w[1 true].include?(attributes["hidden"]) if attributes["hidden"]
204
+ end
205
+ when "chart"
206
+ rid = attributes["r:id"] || attributes["id"]
207
+ @current_chart[:rid] = rid if @inside_graphic_frame && @current_chart && rid
208
+ when "graphicFrameLocks"
209
+ @current_chart[:frame_no_grp] = true if @inside_graphic_frame && @current_chart && %w[1 true].include?(attributes["noGrp"])
210
+ when "from"
211
+ @inside_from = true if @inside_anchor
212
+ when "to"
213
+ @inside_to = true if @inside_anchor
214
+ when "col", "colOff", "row", "rowOff"
215
+ @current_field = name
216
+ @text_buffer = +""
217
+ when "clientData"
218
+ @anchor_locks_with_sheet = attributes["fLocksWithSheet"]
219
+ @anchor_prints_with_sheet = attributes["fPrintsWithSheet"]
220
+ end
221
+ end
222
+
223
+ def characters(text)
224
+ @text_buffer << text if @current_field
225
+ end
226
+
227
+ def end_element(_uri, local_name, qname)
228
+ name = element_name(local_name, qname)
229
+ case name
230
+ when "col", "colOff", "row", "rowOff"
231
+ val = @text_buffer.strip.to_i
232
+ @anchor_from[name] = val if @inside_from
233
+ @anchor_to[name] = val if @inside_to
234
+ @current_field = nil
235
+ when "from"
236
+ @inside_from = false
237
+ when "to"
238
+ @inside_to = false
239
+ when "graphicFrame"
240
+ if @current_chart && @current_chart[:rid]
241
+ @current_chart[:edit_as] = @anchor_edit_as if @anchor_edit_as
242
+ @current_chart[:from_col] = @anchor_from["col"] if @anchor_from["col"]
243
+ @current_chart[:from_row] = @anchor_from["row"] if @anchor_from["row"]
244
+ @current_chart[:from_col_off] = @anchor_from["colOff"] if @anchor_from["colOff"]
245
+ @current_chart[:from_row_off] = @anchor_from["rowOff"] if @anchor_from["rowOff"]
246
+ @current_chart[:to_col] = @anchor_to["col"] if @anchor_to["col"]
247
+ @current_chart[:to_row] = @anchor_to["row"] if @anchor_to["row"]
248
+ @current_chart[:to_col_off] = @anchor_to["colOff"] if @anchor_to["colOff"]
249
+ @current_chart[:to_row_off] = @anchor_to["rowOff"] if @anchor_to["rowOff"]
250
+ @charts << @current_chart
251
+ end
252
+ @current_chart = nil
253
+ @inside_graphic_frame = false
254
+ when "twoCellAnchor", "oneCellAnchor"
255
+ @charts.last[:locks_with_sheet] = @anchor_locks_with_sheet == "1" if @anchor_locks_with_sheet && !@charts.empty?
256
+ @charts.last[:prints_with_sheet] = @anchor_prints_with_sheet == "1" if @anchor_prints_with_sheet && !@charts.empty?
257
+ @charts.last[:published] = true if @anchor_published && !@charts.empty?
258
+ @inside_anchor = false
259
+ @anchor_edit_as = nil
260
+ @anchor_locks_with_sheet = nil
261
+ @anchor_prints_with_sheet = nil
262
+ @anchor_published = false
263
+ @anchor_from = {}
264
+ @anchor_to = {}
265
+ end
266
+ end
267
+
268
+ private
269
+
270
+ def element_name(local_name, qname)
271
+ if local_name.nil? || local_name.empty?
272
+ qname.to_s.split(":").last
273
+ else
274
+ local_name
275
+ end
276
+ end
277
+ end
278
+
279
+ # SAX2 listener for parsing drawing XML to extract shape elements.
280
+ class DrawingShapesListener
281
+ include REXML::SAX2Listener
282
+
283
+ attr_reader :shapes
284
+
285
+ def initialize
286
+ @shapes = []
287
+ @inside_anchor = false
288
+ @inside_sp = false
289
+ @current_shape = nil
290
+ @inside_from = false
291
+ @inside_to = false
292
+ @inside_tx_body = false
293
+ @inside_t = false
294
+ @current_field = nil
295
+ @text_buffer = +""
296
+ @anchor_from = {}
297
+ @anchor_to = {}
298
+ @inside_solid_fill = false
299
+ @inside_highlight = false
300
+ @inside_ln = false
301
+ @inside_rpr_ln = false
302
+ @inside_rpr_effect_lst = false
303
+ @inside_cust_dash = false
304
+ @inside_prst_geom = false
305
+ @inside_rpr = false
306
+ @inside_end_para_rpr = false
307
+ @inside_def_rpr = false
308
+ @current_text_font = nil
309
+ @inside_effect_lst = false
310
+ @inside_outer_shdw = false
311
+ @inside_inner_shdw = false
312
+ @inside_glow = false
313
+ @inside_grad_fill = false
314
+ @inside_patt_fill = false
315
+ @inside_fg_clr = false
316
+ @inside_bg_clr = false
317
+ @current_gs_pos = nil
318
+ @inside_spc_bef = false
319
+ @inside_spc_aft = false
320
+ @inside_lnspc = false
321
+ @inside_tab_lst = false
322
+ @inside_bu_clr = false
323
+ @current_paragraph = nil
324
+ @paragraphs_for_shape = nil
325
+ @inside_r = false
326
+ @current_run = nil
327
+ end
328
+
329
+ def start_element(_uri, local_name, qname, attributes)
330
+ name = element_name(local_name, qname)
331
+ case name
332
+ when "twoCellAnchor", "oneCellAnchor"
333
+ @inside_anchor = true
334
+ @anchor_from = {}
335
+ @anchor_to = {}
336
+ @anchor_edit_as = attributes["editAs"]
337
+ @anchor_published = %w[1 true].include?(attributes["fPublished"])
338
+ when "sp"
339
+ @inside_sp = true
340
+ @current_shape = {}
341
+ @current_shape[:macro] = attributes["macro"] if attributes["macro"] && !attributes["macro"].empty?
342
+ @current_shape[:textlink] = attributes["textlink"] if attributes["textlink"] && !attributes["textlink"].empty?
343
+ when "cNvPr"
344
+ if @inside_sp && @current_shape
345
+ @current_shape[:name] = attributes["name"] if attributes["name"]
346
+ @current_shape[:id] = attributes["id"]&.to_i
347
+ @current_shape[:description] = attributes["descr"] if attributes["descr"]
348
+ @current_shape[:title] = attributes["title"] if attributes["title"]
349
+ @current_shape[:hidden] = %w[1 true].include?(attributes["hidden"]) if attributes["hidden"]
350
+ end
351
+ when "prstGeom"
352
+ if @inside_sp && @current_shape && attributes["prst"]
353
+ @current_shape[:preset] = attributes["prst"]
354
+ @inside_prst_geom = true
355
+ end
356
+ when "gd"
357
+ if @inside_prst_geom && @inside_sp && @current_shape && attributes["name"] && attributes["fmla"]
358
+ @current_shape[:adjust_values] ||= []
359
+ @current_shape[:adjust_values] << { name: attributes["name"], fmla: attributes["fmla"] }
360
+ end
361
+ when "xfrm"
362
+ @current_shape[:rotation] = attributes["rot"].to_i if @inside_sp && @current_shape && attributes["rot"]
363
+ when "solidFill"
364
+ @inside_solid_fill = true if @inside_sp
365
+ when "highlight"
366
+ @inside_highlight = true if @inside_rpr && @inside_sp
367
+ when "gradFill"
368
+ if @inside_sp && @current_shape && !@inside_ln
369
+ @inside_grad_fill = true
370
+ gf = { stops: [] }
371
+ gf[:rot_with_shape] = %w[1 true].include?(attributes["rotWithShape"]) if attributes["rotWithShape"]
372
+ gf[:flip] = attributes["flip"] if attributes["flip"]
373
+ @current_shape[:gradient_fill] = gf
374
+ end
375
+ when "pattFill"
376
+ if @inside_sp && @current_shape && !@inside_ln
377
+ @inside_patt_fill = true
378
+ @current_shape[:pattern_fill] = { preset: attributes["prst"] } if attributes["prst"]
379
+ end
380
+ when "fgClr"
381
+ @inside_fg_clr = true if @inside_patt_fill
382
+ when "bgClr"
383
+ @inside_bg_clr = true if @inside_patt_fill
384
+ when "gs"
385
+ @current_gs_pos = attributes["pos"].to_i if @inside_grad_fill && attributes["pos"]
386
+ when "lin"
387
+ if @inside_grad_fill && @current_shape
388
+ @current_shape[:gradient_fill][:angle] = attributes["ang"].to_i if attributes["ang"]
389
+ @current_shape[:gradient_fill][:scaled] = %w[1 true].include?(attributes["scaled"]) if attributes["scaled"]
390
+ end
391
+ when "path"
392
+ @current_shape[:gradient_fill][:path] = attributes["path"] if @inside_grad_fill && @current_shape && attributes["path"]
393
+ when "tileRect"
394
+ if @inside_grad_fill && @current_shape
395
+ tr = {}
396
+ %w[l t r b].each { |a| tr[a.to_sym] = attributes[a] if attributes[a] }
397
+ @current_shape[:gradient_fill][:tile_rect] = tr unless tr.empty?
398
+ end
399
+ when "ln"
400
+ if @inside_rpr && @current_text_font
401
+ @inside_ln = true
402
+ @inside_rpr_ln = true
403
+ @current_text_font[:line_width] = attributes["w"].to_i if attributes["w"]
404
+ @current_text_font[:line_cap] = attributes["cap"] if attributes["cap"]
405
+ elsif @inside_sp && @current_shape
406
+ @inside_ln = true
407
+ @current_shape[:line_width] = attributes["w"].to_i if attributes["w"]
408
+ @current_shape[:line_cap] = attributes["cap"] if attributes["cap"]
409
+ @current_shape[:line_align] = attributes["algn"] if attributes["algn"]
410
+ @current_shape[:line_compound] = attributes["cmpd"] if attributes["cmpd"]
411
+ end
412
+ when "effectLst"
413
+ if @inside_rpr && @current_text_font
414
+ @inside_rpr_effect_lst = true
415
+ elsif @inside_sp
416
+ @inside_effect_lst = true
417
+ end
418
+ when "outerShdw"
419
+ if @inside_rpr_effect_lst && @current_text_font
420
+ @inside_outer_shdw = true
421
+ os = {}
422
+ os[:blur_rad] = attributes["blurRad"].to_i if attributes["blurRad"]
423
+ os[:dist] = attributes["dist"].to_i if attributes["dist"]
424
+ os[:dir] = attributes["dir"].to_i if attributes["dir"]
425
+ os[:algn] = attributes["algn"] if attributes["algn"]
426
+ os[:rot_with_shape] = %w[1 true].include?(attributes["rotWithShape"]) if attributes["rotWithShape"]
427
+ @current_text_font[:outer_shadow] = os
428
+ elsif @inside_sp && @inside_effect_lst && @current_shape
429
+ @inside_outer_shdw = true
430
+ os = {}
431
+ os[:blur_rad] = attributes["blurRad"].to_i if attributes["blurRad"]
432
+ os[:dist] = attributes["dist"].to_i if attributes["dist"]
433
+ os[:dir] = attributes["dir"].to_i if attributes["dir"]
434
+ os[:algn] = attributes["algn"] if attributes["algn"]
435
+ os[:rot_with_shape] = %w[1 true].include?(attributes["rotWithShape"]) if attributes["rotWithShape"]
436
+ @current_shape[:outer_shadow] = os
437
+ end
438
+ when "innerShdw"
439
+ if @inside_rpr_effect_lst && @current_text_font
440
+ @inside_inner_shdw = true
441
+ is = {}
442
+ is[:blur_rad] = attributes["blurRad"].to_i if attributes["blurRad"]
443
+ is[:dist] = attributes["dist"].to_i if attributes["dist"]
444
+ is[:dir] = attributes["dir"].to_i if attributes["dir"]
445
+ @current_text_font[:inner_shadow] = is
446
+ elsif @inside_sp && @inside_effect_lst && @current_shape
447
+ @inside_inner_shdw = true
448
+ is = {}
449
+ is[:blur_rad] = attributes["blurRad"].to_i if attributes["blurRad"]
450
+ is[:dist] = attributes["dist"].to_i if attributes["dist"]
451
+ is[:dir] = attributes["dir"].to_i if attributes["dir"]
452
+ @current_shape[:inner_shadow] = is
453
+ end
454
+ when "glow"
455
+ if @inside_rpr_effect_lst && @current_text_font
456
+ @inside_glow = true
457
+ gl = {}
458
+ gl[:rad] = attributes["rad"].to_i if attributes["rad"]
459
+ @current_text_font[:glow] = gl
460
+ elsif @inside_sp && @inside_effect_lst && @current_shape
461
+ @inside_glow = true
462
+ gl = {}
463
+ gl[:rad] = attributes["rad"].to_i if attributes["rad"]
464
+ @current_shape[:glow] = gl
465
+ end
466
+ when "softEdge"
467
+ if @inside_rpr_effect_lst && @current_text_font
468
+ se = {}
469
+ se[:rad] = attributes["rad"].to_i if attributes["rad"]
470
+ @current_text_font[:soft_edge] = se
471
+ elsif @inside_sp && @inside_effect_lst && @current_shape
472
+ se = {}
473
+ se[:rad] = attributes["rad"].to_i if attributes["rad"]
474
+ @current_shape[:soft_edge] = se
475
+ end
476
+ when "blur"
477
+ if @inside_rpr_effect_lst && @current_text_font
478
+ bl = {}
479
+ bl[:rad] = attributes["rad"].to_i if attributes["rad"]
480
+ bl[:grow] = %w[1 true].include?(attributes["grow"]) if attributes["grow"]
481
+ @current_text_font[:blur] = bl
482
+ elsif @inside_sp && @inside_effect_lst && @current_shape
483
+ bl = {}
484
+ bl[:rad] = attributes["rad"].to_i if attributes["rad"]
485
+ bl[:grow] = %w[1 true].include?(attributes["grow"]) if attributes["grow"]
486
+ @current_shape[:blur] = bl
487
+ end
488
+ when "reflection"
489
+ if @inside_rpr_effect_lst && @current_text_font
490
+ rf = {}
491
+ rf[:blur_rad] = attributes["blurRad"].to_i if attributes["blurRad"]
492
+ rf[:st_a] = attributes["stA"].to_i if attributes["stA"]
493
+ rf[:end_a] = attributes["endA"].to_i if attributes["endA"]
494
+ rf[:dist] = attributes["dist"].to_i if attributes["dist"]
495
+ rf[:dir] = attributes["dir"].to_i if attributes["dir"]
496
+ rf[:fade_dir] = attributes["fadeDir"].to_i if attributes["fadeDir"]
497
+ rf[:sx] = attributes["sx"].to_i if attributes["sx"]
498
+ rf[:sy] = attributes["sy"].to_i if attributes["sy"]
499
+ rf[:kx] = attributes["kx"].to_i if attributes["kx"]
500
+ rf[:ky] = attributes["ky"].to_i if attributes["ky"]
501
+ rf[:algn] = attributes["algn"] if attributes["algn"]
502
+ rf[:rot_with_shape] = %w[1 true].include?(attributes["rotWithShape"]) if attributes["rotWithShape"]
503
+ @current_text_font[:reflection] = rf
504
+ elsif @inside_sp && @inside_effect_lst && @current_shape
505
+ rf = {}
506
+ rf[:blur_rad] = attributes["blurRad"].to_i if attributes["blurRad"]
507
+ rf[:st_a] = attributes["stA"].to_i if attributes["stA"]
508
+ rf[:end_a] = attributes["endA"].to_i if attributes["endA"]
509
+ rf[:dist] = attributes["dist"].to_i if attributes["dist"]
510
+ rf[:dir] = attributes["dir"].to_i if attributes["dir"]
511
+ rf[:fade_dir] = attributes["fadeDir"].to_i if attributes["fadeDir"]
512
+ rf[:sx] = attributes["sx"].to_i if attributes["sx"]
513
+ rf[:sy] = attributes["sy"].to_i if attributes["sy"]
514
+ rf[:kx] = attributes["kx"].to_i if attributes["kx"]
515
+ rf[:ky] = attributes["ky"].to_i if attributes["ky"]
516
+ rf[:algn] = attributes["algn"] if attributes["algn"]
517
+ rf[:rot_with_shape] = %w[1 true].include?(attributes["rotWithShape"]) if attributes["rotWithShape"]
518
+ @current_shape[:reflection] = rf
519
+ end
520
+ when "prstDash"
521
+ if @inside_rpr_ln && @current_text_font && attributes["val"]
522
+ @current_text_font[:line_dash] = attributes["val"]
523
+ elsif @inside_sp && @inside_ln && @current_shape && attributes["val"]
524
+ @current_shape[:line_dash] = attributes["val"]
525
+ end
526
+ when "custDash"
527
+ @inside_cust_dash = true if @inside_sp && @inside_ln && @current_shape
528
+ when "ds"
529
+ if @inside_cust_dash && @inside_sp && @inside_ln && @current_shape
530
+ ds = {}
531
+ ds[:d] = attributes["d"].to_i if attributes["d"]
532
+ ds[:sp] = attributes["sp"].to_i if attributes["sp"]
533
+ @current_shape[:line_custom_dash] ||= []
534
+ @current_shape[:line_custom_dash] << ds
535
+ end
536
+ when "round"
537
+ if @inside_rpr_ln && @current_text_font
538
+ @current_text_font[:line_join] = "round"
539
+ elsif @inside_sp && @inside_ln && @current_shape
540
+ @current_shape[:line_join] = "round"
541
+ end
542
+ when "bevel"
543
+ if @inside_rpr_ln && @current_text_font
544
+ @current_text_font[:line_join] = "bevel"
545
+ elsif @inside_sp && @inside_ln && @current_shape
546
+ @current_shape[:line_join] = "bevel"
547
+ end
548
+ when "miter"
549
+ if @inside_rpr_ln && @current_text_font
550
+ @current_text_font[:line_join] = "miter"
551
+ @current_text_font[:line_miter_limit] = attributes["lim"].to_i if attributes["lim"]
552
+ elsif @inside_sp && @inside_ln && @current_shape
553
+ @current_shape[:line_join] = "miter"
554
+ @current_shape[:line_miter_limit] = attributes["lim"].to_i if attributes["lim"]
555
+ end
556
+ when "headEnd"
557
+ if @inside_sp && @inside_ln && @current_shape
558
+ he = {}
559
+ he[:type] = attributes["type"] if attributes["type"]
560
+ he[:w] = attributes["w"] if attributes["w"]
561
+ he[:len] = attributes["len"] if attributes["len"]
562
+ @current_shape[:head_end] = he
563
+ end
564
+ when "tailEnd"
565
+ if @inside_sp && @inside_ln && @current_shape
566
+ te = {}
567
+ te[:type] = attributes["type"] if attributes["type"]
568
+ te[:w] = attributes["w"] if attributes["w"]
569
+ te[:len] = attributes["len"] if attributes["len"]
570
+ @current_shape[:tail_end] = te
571
+ end
572
+ when "spLocks"
573
+ if @inside_sp && @current_shape
574
+ @current_shape[:f_locks_text] = true if %w[1 true].include?(attributes["fLocksText"])
575
+ @current_shape[:no_grp] = true if %w[1 true].include?(attributes["noGrp"])
576
+ @current_shape[:no_rot] = true if %w[1 true].include?(attributes["noRot"])
577
+ end
578
+ when "srgbClr"
579
+ assign_shape_color(attributes["val"]) if attributes["val"]
580
+ when "schemeClr"
581
+ assign_shape_color({ scheme: attributes["val"] }) if attributes["val"]
582
+ when "noFill"
583
+ if @inside_sp && @current_shape
584
+ if @inside_ln
585
+ @current_shape[:no_line] = true
586
+ else
587
+ @current_shape[:no_fill] = true
588
+ end
589
+ end
590
+ when "alpha"
591
+ if @inside_sp && @current_shape && attributes["val"]
592
+ alpha_val = attributes["val"].to_i
593
+ if @inside_outer_shdw && !@inside_rpr_effect_lst
594
+ @current_shape[:outer_shadow][:alpha] = alpha_val if @current_shape[:outer_shadow]
595
+ elsif @inside_inner_shdw && !@inside_rpr_effect_lst
596
+ @current_shape[:inner_shadow][:alpha] = alpha_val if @current_shape[:inner_shadow]
597
+ elsif @inside_glow && !@inside_rpr_effect_lst
598
+ @current_shape[:glow][:alpha] = alpha_val if @current_shape[:glow]
599
+ elsif @inside_ln && @inside_solid_fill && !@inside_rpr
600
+ @current_shape[:line_alpha] = alpha_val
601
+ elsif @inside_solid_fill && !@inside_ln && !@inside_rpr
602
+ @current_shape[:fill_alpha] = alpha_val
603
+ end
604
+ end
605
+ when "tint", "shade", "lumMod", "lumOff", "satMod", "satOff", "hueMod", "hueOff"
606
+ if @inside_sp && @current_shape && @inside_solid_fill && !@inside_rpr && attributes["val"]
607
+ t = { type: name, val: attributes["val"].to_i }
608
+ if @inside_ln
609
+ (@current_shape[:line_color_transforms] ||= []) << t
610
+ else
611
+ (@current_shape[:fill_color_transforms] ||= []) << t
612
+ end
613
+ end
614
+ when "from"
615
+ @inside_from = true if @inside_anchor
616
+ when "to"
617
+ @inside_to = true if @inside_anchor
618
+ when "txBody"
619
+ @inside_tx_body = true if @inside_sp
620
+ @paragraphs_for_shape = [] if @inside_sp
621
+ when "p"
622
+ @current_paragraph = {} if @inside_tx_body && @inside_sp
623
+ when "r"
624
+ if @inside_tx_body && @inside_sp && @current_paragraph
625
+ @inside_r = true
626
+ @current_run = {}
627
+ end
628
+ when "rPr"
629
+ if @inside_tx_body && @inside_sp && @current_shape
630
+ @inside_rpr = true
631
+ tf = {}
632
+ tf[:bold] = true if %w[1 true].include?(attributes["b"])
633
+ tf[:italic] = true if %w[1 true].include?(attributes["i"])
634
+ tf[:no_proof] = true if %w[1 true].include?(attributes["noProof"])
635
+ tf[:normalize_h] = true if %w[1 true].include?(attributes["normalizeH"])
636
+ tf[:kumimoji] = true if %w[1 true].include?(attributes["kumimoji"])
637
+ tf[:strike] = attributes["strike"] if attributes["strike"]
638
+ tf[:underline] = attributes["u"] if attributes["u"]
639
+ tf[:baseline] = attributes["baseline"].to_i if attributes["baseline"]
640
+ tf[:spacing] = attributes["spc"].to_i if attributes["spc"]
641
+ tf[:kern] = attributes["kern"].to_i if attributes["kern"]
642
+ tf[:cap] = attributes["cap"] if attributes["cap"]
643
+ tf[:lang] = attributes["lang"] if attributes["lang"]
644
+ tf[:alt_lang] = attributes["altLang"] if attributes["altLang"]
645
+ tf[:dirty] = true if %w[1 true].include?(attributes["dirty"])
646
+ tf[:smt_clean] = true if %w[1 true].include?(attributes["smtClean"])
647
+ tf[:err] = true if %w[1 true].include?(attributes["err"])
648
+ tf[:bmk] = attributes["bmk"] if attributes["bmk"]
649
+ tf[:size] = attributes["sz"].to_i if attributes["sz"]
650
+ @current_text_font = tf
651
+ end
652
+ when "endParaRPr"
653
+ if @inside_tx_body && @inside_sp && @current_shape
654
+ @inside_rpr = true
655
+ @inside_end_para_rpr = true
656
+ tf = {}
657
+ tf[:bold] = true if %w[1 true].include?(attributes["b"])
658
+ tf[:italic] = true if %w[1 true].include?(attributes["i"])
659
+ tf[:no_proof] = true if %w[1 true].include?(attributes["noProof"])
660
+ tf[:normalize_h] = true if %w[1 true].include?(attributes["normalizeH"])
661
+ tf[:kumimoji] = true if %w[1 true].include?(attributes["kumimoji"])
662
+ tf[:strike] = attributes["strike"] if attributes["strike"]
663
+ tf[:underline] = attributes["u"] if attributes["u"]
664
+ tf[:baseline] = attributes["baseline"].to_i if attributes["baseline"]
665
+ tf[:spacing] = attributes["spc"].to_i if attributes["spc"]
666
+ tf[:kern] = attributes["kern"].to_i if attributes["kern"]
667
+ tf[:cap] = attributes["cap"] if attributes["cap"]
668
+ tf[:lang] = attributes["lang"] if attributes["lang"]
669
+ tf[:alt_lang] = attributes["altLang"] if attributes["altLang"]
670
+ tf[:dirty] = true if %w[1 true].include?(attributes["dirty"])
671
+ tf[:smt_clean] = true if %w[1 true].include?(attributes["smtClean"])
672
+ tf[:err] = true if %w[1 true].include?(attributes["err"])
673
+ tf[:bmk] = attributes["bmk"] if attributes["bmk"]
674
+ tf[:size] = attributes["sz"].to_i if attributes["sz"]
675
+ @current_text_font = tf
676
+ end
677
+ when "latin"
678
+ @current_text_font[:name] = attributes["typeface"] if @inside_rpr && @current_text_font && attributes["typeface"]
679
+ when "ea"
680
+ @current_text_font[:ea_font] = attributes["typeface"] if @inside_rpr && @current_text_font && attributes["typeface"]
681
+ when "cs"
682
+ @current_text_font[:cs_font] = attributes["typeface"] if @inside_rpr && @current_text_font && attributes["typeface"]
683
+ when "sym"
684
+ @current_text_font[:sym_font] = attributes["typeface"] if @inside_rpr && @current_text_font && attributes["typeface"]
685
+ when "uFillTx"
686
+ @current_text_font[:u_fill_tx] = true if @inside_rpr && @current_text_font
687
+ when "uLnTx"
688
+ @current_text_font[:u_ln_tx] = true if @inside_rpr && @current_text_font
689
+ when "pPr"
690
+ if @inside_tx_body && @inside_sp && @current_paragraph
691
+ @current_paragraph[:align] = attributes["algn"] if attributes["algn"]
692
+ @current_paragraph[:font_align] = attributes["fontAlgn"] if attributes["fontAlgn"]
693
+ @current_paragraph[:def_tab_sz] = attributes["defTabSz"].to_i if attributes["defTabSz"]
694
+ @current_paragraph[:rtl] = %w[1 true].include?(attributes["rtl"]) if attributes["rtl"]
695
+ @current_paragraph[:ea_ln_brk] = %w[1 true].include?(attributes["eaLnBrk"]) if attributes["eaLnBrk"]
696
+ @current_paragraph[:latin_ln_brk] = %w[1 true].include?(attributes["latinLnBrk"]) if attributes["latinLnBrk"]
697
+ @current_paragraph[:hanging_punct] = %w[1 true].include?(attributes["hangingPunct"]) if attributes["hangingPunct"]
698
+ @current_paragraph[:level] = attributes["lvl"].to_i if attributes["lvl"]
699
+ ti = {}
700
+ ti[:left] = attributes["marL"].to_i if attributes["marL"]
701
+ ti[:right] = attributes["marR"].to_i if attributes["marR"]
702
+ ti[:indent] = attributes["indent"].to_i if attributes["indent"]
703
+ @current_paragraph[:indent] = ti unless ti.empty?
704
+ end
705
+ when "defRPr"
706
+ if @inside_tx_body && @inside_sp && @current_shape
707
+ @inside_rpr = true
708
+ @inside_def_rpr = true
709
+ tf = {}
710
+ tf[:bold] = true if %w[1 true].include?(attributes["b"])
711
+ tf[:italic] = true if %w[1 true].include?(attributes["i"])
712
+ tf[:no_proof] = true if %w[1 true].include?(attributes["noProof"])
713
+ tf[:normalize_h] = true if %w[1 true].include?(attributes["normalizeH"])
714
+ tf[:kumimoji] = true if %w[1 true].include?(attributes["kumimoji"])
715
+ tf[:strike] = attributes["strike"] if attributes["strike"]
716
+ tf[:underline] = attributes["u"] if attributes["u"]
717
+ tf[:baseline] = attributes["baseline"].to_i if attributes["baseline"]
718
+ tf[:spacing] = attributes["spc"].to_i if attributes["spc"]
719
+ tf[:kern] = attributes["kern"].to_i if attributes["kern"]
720
+ tf[:cap] = attributes["cap"] if attributes["cap"]
721
+ tf[:lang] = attributes["lang"] if attributes["lang"]
722
+ tf[:alt_lang] = attributes["altLang"] if attributes["altLang"]
723
+ tf[:dirty] = true if %w[1 true].include?(attributes["dirty"])
724
+ tf[:smt_clean] = true if %w[1 true].include?(attributes["smtClean"])
725
+ tf[:err] = true if %w[1 true].include?(attributes["err"])
726
+ tf[:bmk] = attributes["bmk"] if attributes["bmk"]
727
+ tf[:size] = attributes["sz"].to_i if attributes["sz"]
728
+ @current_text_font = tf
729
+ end
730
+ when "spcBef"
731
+ @inside_spc_bef = true if @inside_tx_body && @inside_sp
732
+ when "spcAft"
733
+ @inside_spc_aft = true if @inside_tx_body && @inside_sp
734
+ when "lnSpc"
735
+ @inside_lnspc = true if @inside_tx_body && @inside_sp
736
+ when "tabLst"
737
+ @inside_tab_lst = true if @inside_tx_body && @inside_sp
738
+ when "tab"
739
+ if @inside_tab_lst && @inside_tx_body && @inside_sp && @current_paragraph && attributes["pos"]
740
+ tab = { pos: attributes["pos"].to_i }
741
+ tab[:align] = attributes["algn"] if attributes["algn"]
742
+ @current_paragraph[:tab_stops] ||= []
743
+ @current_paragraph[:tab_stops] << tab
744
+ end
745
+ when "buNone"
746
+ if @inside_tx_body && @inside_sp && @current_paragraph
747
+ @current_paragraph[:bullet] ||= {}
748
+ @current_paragraph[:bullet][:type] = "none"
749
+ end
750
+ when "buClr"
751
+ @inside_bu_clr = true if @inside_tx_body && @inside_sp
752
+ when "buFont"
753
+ if @inside_tx_body && @inside_sp && @current_paragraph && attributes["typeface"]
754
+ @current_paragraph[:bullet] ||= {}
755
+ @current_paragraph[:bullet][:font] = attributes["typeface"]
756
+ end
757
+ when "buSzPts"
758
+ if @inside_tx_body && @inside_sp && @current_paragraph && attributes["val"]
759
+ @current_paragraph[:bullet] ||= {}
760
+ @current_paragraph[:bullet][:size_pts] = attributes["val"].to_i
761
+ end
762
+ when "buSzPct"
763
+ if @inside_tx_body && @inside_sp && @current_paragraph && attributes["val"]
764
+ @current_paragraph[:bullet] ||= {}
765
+ @current_paragraph[:bullet][:size_pct] = attributes["val"].to_i
766
+ end
767
+ when "buChar"
768
+ if @inside_tx_body && @inside_sp && @current_paragraph && attributes["char"]
769
+ @current_paragraph[:bullet] ||= {}
770
+ @current_paragraph[:bullet][:type] = "char"
771
+ @current_paragraph[:bullet][:char] = attributes["char"]
772
+ end
773
+ when "buAutoNum"
774
+ if @inside_tx_body && @inside_sp && @current_paragraph && attributes["type"]
775
+ @current_paragraph[:bullet] ||= {}
776
+ @current_paragraph[:bullet][:type] = "auto"
777
+ @current_paragraph[:bullet][:auto_type] = attributes["type"]
778
+ @current_paragraph[:bullet][:start_at] = attributes["startAt"].to_i if attributes["startAt"]
779
+ end
780
+ when "spcPts"
781
+ if @inside_tx_body && @inside_sp && @current_paragraph && attributes["val"]
782
+ @current_paragraph[:spacing] ||= {}
783
+ if @inside_spc_bef
784
+ @current_paragraph[:spacing][:before] = attributes["val"].to_i
785
+ elsif @inside_spc_aft
786
+ @current_paragraph[:spacing][:after] = attributes["val"].to_i
787
+ elsif @inside_lnspc
788
+ @current_paragraph[:spacing][:line] = attributes["val"].to_i
789
+ end
790
+ end
791
+ when "spcPct"
792
+ if @inside_tx_body && @inside_sp && @current_paragraph && attributes["val"]
793
+ @current_paragraph[:spacing] ||= {}
794
+ if @inside_spc_bef
795
+ @current_paragraph[:spacing][:before_pct] = attributes["val"].to_i
796
+ elsif @inside_spc_aft
797
+ @current_paragraph[:spacing][:after_pct] = attributes["val"].to_i
798
+ elsif @inside_lnspc
799
+ @current_paragraph[:spacing][:line_pct] = attributes["val"].to_i
800
+ end
801
+ end
802
+ when "bodyPr"
803
+ if @inside_sp && @current_shape
804
+ @current_shape[:text_rot] = attributes["rot"].to_i if attributes["rot"]
805
+ @current_shape[:text_spc_first_last_para] = %w[1 true].include?(attributes["spcFirstLastPara"]) if attributes["spcFirstLastPara"]
806
+ @current_shape[:text_wrap] = attributes["wrap"] if attributes["wrap"]
807
+ @current_shape[:text_anchor] = attributes["anchor"] if attributes["anchor"]
808
+ @current_shape[:text_anchor_ctr] = %w[1 true].include?(attributes["anchorCtr"]) if attributes["anchorCtr"]
809
+ @current_shape[:text_vert_overflow] = attributes["vertOverflow"] if attributes["vertOverflow"]
810
+ @current_shape[:text_horz_overflow] = attributes["horzOverflow"] if attributes["horzOverflow"]
811
+ @current_shape[:text_num_col] = attributes["numCol"].to_i if attributes["numCol"]
812
+ @current_shape[:text_spc_col] = attributes["spcCol"].to_i if attributes["spcCol"]
813
+ @current_shape[:text_rtl_col] = %w[1 true].include?(attributes["rtlCol"]) if attributes["rtlCol"]
814
+ @current_shape[:text_from_word_art] = %w[1 true].include?(attributes["fromWordArt"]) if attributes["fromWordArt"]
815
+ @current_shape[:text_upright] = %w[1 true].include?(attributes["upright"]) if attributes["upright"]
816
+ @current_shape[:text_compat_ln_spc] = %w[1 true].include?(attributes["compatLnSpc"]) if attributes["compatLnSpc"]
817
+ @current_shape[:text_force_aa] = %w[1 true].include?(attributes["forceAA"]) if attributes["forceAA"]
818
+ @current_shape[:text_vertical] = attributes["vert"] if attributes["vert"]
819
+ ins = {}
820
+ ins[:left] = attributes["lIns"].to_i if attributes["lIns"]
821
+ ins[:top] = attributes["tIns"].to_i if attributes["tIns"]
822
+ ins[:right] = attributes["rIns"].to_i if attributes["rIns"]
823
+ ins[:bottom] = attributes["bIns"].to_i if attributes["bIns"]
824
+ @current_shape[:text_insets] = ins unless ins.empty?
825
+ end
826
+ when "noAutofit"
827
+ @current_shape[:autofit] = "none" if @inside_sp && @current_shape
828
+ when "spAutoFit"
829
+ @current_shape[:autofit] = "shape" if @inside_sp && @current_shape
830
+ when "normAutofit"
831
+ if @inside_sp && @current_shape
832
+ if attributes["fontScale"] || attributes["lnSpcReduction"]
833
+ af = { type: "normal" }
834
+ af[:font_scale] = attributes["fontScale"].to_i if attributes["fontScale"]
835
+ af[:ln_spc_reduction] = attributes["lnSpcReduction"].to_i if attributes["lnSpcReduction"]
836
+ @current_shape[:autofit] = af
837
+ else
838
+ @current_shape[:autofit] = "normal"
839
+ end
840
+ end
841
+ when "prstTxWarp"
842
+ @current_shape[:text_warp] = { preset: attributes["prst"] } if @inside_sp && @current_shape && attributes["prst"]
843
+ when "t"
844
+ @inside_t = true if @inside_tx_body
845
+ @text_buffer = +""
846
+ when "col", "colOff", "row", "rowOff"
847
+ @current_field = name
848
+ @text_buffer = +""
849
+ when "clientData"
850
+ if @inside_anchor
851
+ @anchor_locks_with_sheet = attributes["fLocksWithSheet"]
852
+ @anchor_prints_with_sheet = attributes["fPrintsWithSheet"]
853
+ end
854
+ end
855
+ end
856
+
857
+ def characters(text)
858
+ @text_buffer << text if @current_field || @inside_t
859
+ end
860
+
861
+ def end_element(_uri, local_name, qname)
862
+ name = element_name(local_name, qname)
863
+ case name
864
+ when "sp"
865
+ if @current_shape && !@current_shape.empty?
866
+ @anchor_from.each { |k, v| @current_shape[:"from_#{k}"] = v }
867
+ @anchor_to.each { |k, v| @current_shape[:"to_#{k}"] = v }
868
+ @current_shape[:edit_as] = @anchor_edit_as if @anchor_edit_as
869
+ @shapes << @current_shape
870
+ end
871
+ @current_shape = nil
872
+ @inside_sp = false
873
+ @inside_tx_body = false
874
+ when "twoCellAnchor", "oneCellAnchor"
875
+ @shapes.last[:locks_with_sheet] = @anchor_locks_with_sheet == "1" if @anchor_locks_with_sheet && !@shapes.empty?
876
+ @shapes.last[:prints_with_sheet] = @anchor_prints_with_sheet == "1" if @anchor_prints_with_sheet && !@shapes.empty?
877
+ @shapes.last[:published] = true if @anchor_published && !@shapes.empty?
878
+ @inside_anchor = false
879
+ @anchor_from = {}
880
+ @anchor_to = {}
881
+ @anchor_locks_with_sheet = nil
882
+ @anchor_prints_with_sheet = nil
883
+ @anchor_published = false
884
+ when "from"
885
+ @inside_from = false
886
+ when "to"
887
+ @inside_to = false
888
+ when "solidFill"
889
+ @inside_solid_fill = false
890
+ when "highlight"
891
+ @inside_highlight = false
892
+ when "ln"
893
+ @inside_ln = false
894
+ @inside_rpr_ln = false
895
+ @inside_cust_dash = false
896
+ when "effectLst"
897
+ @inside_effect_lst = false
898
+ @inside_rpr_effect_lst = false
899
+ when "outerShdw"
900
+ @inside_outer_shdw = false
901
+ when "innerShdw"
902
+ @inside_inner_shdw = false
903
+ when "glow"
904
+ @inside_glow = false
905
+ when "gradFill"
906
+ @inside_grad_fill = false
907
+ when "pattFill"
908
+ @inside_patt_fill = false
909
+ when "fgClr"
910
+ @inside_fg_clr = false
911
+ when "bgClr"
912
+ @inside_bg_clr = false
913
+ when "prstGeom"
914
+ @inside_prst_geom = false
915
+ when "spcBef"
916
+ @inside_spc_bef = false
917
+ when "spcAft"
918
+ @inside_spc_aft = false
919
+ when "lnSpc"
920
+ @inside_lnspc = false
921
+ when "tabLst"
922
+ @inside_tab_lst = false
923
+ when "buClr"
924
+ @inside_bu_clr = false
925
+ when "rPr"
926
+ if @inside_rpr && !@inside_end_para_rpr && !@inside_def_rpr && @current_text_font&.any?
927
+ if @current_run
928
+ @current_run[:font] = @current_text_font
929
+ elsif @current_paragraph
930
+ @current_paragraph[:font] = @current_text_font
931
+ end
932
+ end
933
+ @inside_rpr = false
934
+ @current_text_font = nil
935
+ when "endParaRPr"
936
+ @current_paragraph[:end_para_rpr] = @current_text_font if @inside_end_para_rpr && @current_text_font&.any? && @current_paragraph
937
+ @inside_rpr = false
938
+ @inside_end_para_rpr = false
939
+ @current_text_font = nil
940
+ when "defRPr"
941
+ @current_paragraph[:def_rpr] = @current_text_font if @inside_def_rpr && @current_text_font&.any? && @current_paragraph
942
+ @inside_rpr = false
943
+ @inside_def_rpr = false
944
+ @current_text_font = nil
945
+ when "t"
946
+ if @inside_t && @inside_tx_body
947
+ if @current_run
948
+ @current_run[:text] = (@current_run[:text] || +"") << @text_buffer
949
+ elsif @current_paragraph
950
+ @current_paragraph[:text] = (@current_paragraph[:text] || +"") << @text_buffer
951
+ end
952
+ end
953
+ @inside_t = false
954
+ when "r"
955
+ if @inside_r && @current_run && @current_paragraph
956
+ @current_paragraph[:runs] ||= []
957
+ @current_paragraph[:runs] << @current_run
958
+ @current_run = nil
959
+ @inside_r = false
960
+ end
961
+ when "p"
962
+ if @inside_tx_body && @inside_sp && @current_paragraph && @paragraphs_for_shape
963
+ finalize_paragraph_runs(@current_paragraph)
964
+ @paragraphs_for_shape << @current_paragraph
965
+ merge_paragraph_to_shape(@current_paragraph, @current_shape) if @current_shape
966
+ @current_paragraph = nil
967
+ end
968
+ when "txBody"
969
+ @current_shape[:text_paragraphs] = @paragraphs_for_shape if @inside_tx_body && @inside_sp && @current_shape && @paragraphs_for_shape && @paragraphs_for_shape.size > 1
970
+ @inside_tx_body = false
971
+ @paragraphs_for_shape = nil
972
+ when "col", "colOff", "row", "rowOff"
973
+ if @current_field
974
+ val = @text_buffer.to_i
975
+ if @inside_from
976
+ @anchor_from[@current_field] = val
977
+ elsif @inside_to
978
+ @anchor_to[@current_field] = val
979
+ end
980
+ end
981
+ @current_field = nil
982
+ end
983
+ end
984
+
985
+ private
986
+
987
+ def assign_shape_color(color_value)
988
+ if @inside_rpr_ln && @current_text_font && @inside_solid_fill
989
+ @current_text_font[:line_color] = color_value
990
+ elsif @inside_rpr && @current_text_font && @inside_solid_fill
991
+ @current_text_font[:color] = color_value
992
+ elsif @inside_rpr && @current_text_font && @inside_highlight
993
+ @current_text_font[:highlight] = color_value
994
+ elsif @inside_bu_clr && @inside_sp && @current_paragraph
995
+ @current_paragraph[:bullet] ||= {}
996
+ @current_paragraph[:bullet][:color] = color_value
997
+ elsif @inside_outer_shdw && @inside_rpr_effect_lst && @current_text_font
998
+ @current_text_font[:outer_shadow][:color] = color_value
999
+ elsif @inside_inner_shdw && @inside_rpr_effect_lst && @current_text_font
1000
+ @current_text_font[:inner_shadow][:color] = color_value
1001
+ elsif @inside_glow && @inside_rpr_effect_lst && @current_text_font
1002
+ @current_text_font[:glow][:color] = color_value
1003
+ elsif @inside_outer_shdw && @current_shape
1004
+ @current_shape[:outer_shadow][:color] = color_value
1005
+ elsif @inside_inner_shdw && @current_shape
1006
+ @current_shape[:inner_shadow][:color] = color_value
1007
+ elsif @inside_glow && @current_shape
1008
+ @current_shape[:glow][:color] = color_value
1009
+ elsif @inside_grad_fill && @current_gs_pos && @current_shape
1010
+ @current_shape[:gradient_fill][:stops] << { pos: @current_gs_pos, color: color_value }
1011
+ @current_gs_pos = nil
1012
+ elsif @inside_patt_fill && @current_shape
1013
+ if @inside_fg_clr
1014
+ @current_shape[:pattern_fill][:fg_color] = color_value
1015
+ elsif @inside_bg_clr
1016
+ @current_shape[:pattern_fill][:bg_color] = color_value
1017
+ end
1018
+ elsif @inside_sp && @current_shape && @inside_solid_fill && !@inside_rpr
1019
+ if @inside_ln
1020
+ @current_shape[:line_color] = color_value
1021
+ else
1022
+ @current_shape[:fill_color] = color_value
1023
+ end
1024
+ end
1025
+ end
1026
+
1027
+ def finalize_paragraph_runs(para)
1028
+ return unless para[:runs]&.any?
1029
+
1030
+ # Set paragraph text from concatenation of all run texts
1031
+ para[:text] = para[:runs].filter_map { |r| r[:text] }.join unless para[:text]
1032
+ # Set paragraph font from last run's font for backward compat
1033
+ last_font = para[:runs].rfind { |r| r[:font] }&.dig(:font)
1034
+ para[:font] = last_font if last_font && !para[:font]
1035
+ # Remove :runs key when only one run (simplify output)
1036
+ para.delete(:runs) if para[:runs].size <= 1
1037
+ end
1038
+
1039
+ def merge_paragraph_to_shape(para, shape)
1040
+ shape[:text] = shape[:text] ? "#{shape[:text]}\n#{para[:text]}" : para[:text] if para[:text]
1041
+ shape[:text_font] = para[:font] if para[:font]
1042
+ shape[:text_align] = para[:align] if para[:align]
1043
+ shape[:text_font_align] = para[:font_align] if para[:font_align]
1044
+ shape[:text_def_tab_sz] = para[:def_tab_sz] if para[:def_tab_sz]
1045
+ shape[:text_rtl] = para[:rtl] unless para[:rtl].nil?
1046
+ shape[:text_ea_ln_brk] = para[:ea_ln_brk] unless para[:ea_ln_brk].nil?
1047
+ shape[:text_latin_ln_brk] = para[:latin_ln_brk] unless para[:latin_ln_brk].nil?
1048
+ shape[:text_hanging_punct] = para[:hanging_punct] unless para[:hanging_punct].nil?
1049
+ shape[:text_level] = para[:level] if para[:level]
1050
+ shape[:text_indent] = para[:indent] if para[:indent]
1051
+ shape[:text_spacing] = para[:spacing] if para[:spacing]
1052
+ shape[:text_tab_stops] = para[:tab_stops] if para[:tab_stops]
1053
+ shape[:text_bullet] = para[:bullet] if para[:bullet]
1054
+ shape[:text_end_para_rpr] = para[:end_para_rpr] if para[:end_para_rpr]
1055
+ shape[:text_def_rpr] = para[:def_rpr] if para[:def_rpr]
1056
+ end
1057
+
1058
+ def element_name(local_name, qname)
1059
+ if local_name.nil? || local_name.empty?
1060
+ qname.to_s.split(":").last
1061
+ else
1062
+ local_name
1063
+ end
1064
+ end
1065
+ end
1066
+
1067
+ # SAX2 listener for parsing chart XML to identify chart type and title.
1068
+ class ChartTypeListener
1069
+ include REXML::SAX2Listener
1070
+
1071
+ attr_reader :chart_type, :title, :title_overlay, :title_font,
1072
+ :title_fill_color, :title_no_fill, :title_line_color, :title_line_width, :title_line_dash,
1073
+ :series, :legend, :data_labels, :cat_axis_title, :val_axis_title,
1074
+ :grouping, :bar_dir, :vary_colors, :plot_vis_only, :disp_blanks_as, :style, :auto_title_deleted,
1075
+ :rounded_corners, :cat_axis_tick_lbl_pos, :val_axis_tick_lbl_pos,
1076
+ :cat_axis_major_gridlines, :val_axis_major_gridlines,
1077
+ :cat_axis_minor_gridlines, :val_axis_minor_gridlines,
1078
+ :show_d_lbls_over_max, :cat_axis_delete, :val_axis_delete,
1079
+ :cat_axis_orientation, :val_axis_orientation,
1080
+ :gap_width, :overlap, :view_3d,
1081
+ :gap_depth, :bar_shape,
1082
+ :bubble_3d, :bubble_scale, :show_neg_bubbles, :size_represents,
1083
+ :cat_axis_num_fmt, :val_axis_num_fmt,
1084
+ :cat_axis_major_tick_mark, :cat_axis_minor_tick_mark,
1085
+ :val_axis_major_tick_mark, :val_axis_minor_tick_mark,
1086
+ :cat_axis_crosses, :val_axis_crosses,
1087
+ :cat_axis_crosses_at, :val_axis_crosses_at,
1088
+ :cat_axis_tick_lbl_skip, :cat_axis_tick_mark_skip,
1089
+ :cat_axis_lbl_offset, :cat_axis_auto, :cat_axis_lbl_algn,
1090
+ :cat_axis_no_multi_lvl_lbl,
1091
+ :val_axis_cross_between, :val_axis_major_unit, :val_axis_minor_unit,
1092
+ :val_axis_disp_units,
1093
+ :cat_axis_scaling_max, :cat_axis_scaling_min,
1094
+ :val_axis_scaling_max, :val_axis_scaling_min,
1095
+ :cat_axis_log_base, :val_axis_log_base,
1096
+ :first_slice_ang, :hole_size,
1097
+ :smooth, :marker,
1098
+ :drop_lines, :hi_low_lines, :ser_lines,
1099
+ :up_down_bars,
1100
+ :scatter_style, :radar_style,
1101
+ :cat_axis_pos, :val_axis_pos,
1102
+ :wireframe,
1103
+ :band_fmts,
1104
+ :of_pie_type, :split_type, :split_pos, :cust_split, :second_pie_size,
1105
+ :data_table,
1106
+ :plot_area_fill, :plot_area_no_fill, :plot_area_line_color, :plot_area_line_width, :plot_area_line_dash,
1107
+ :plot_area_layout,
1108
+ :cat_axis_label_rotation, :val_axis_label_rotation,
1109
+ :cat_axis_font, :val_axis_font,
1110
+ :cat_axis_fill, :cat_axis_no_fill, :val_axis_fill, :val_axis_no_fill,
1111
+ :cat_axis_line_color, :cat_axis_line_width, :cat_axis_line_dash,
1112
+ :val_axis_line_color, :val_axis_line_width, :val_axis_line_dash,
1113
+ :floor, :side_wall, :back_wall,
1114
+ :legend_font,
1115
+ :cat_axis_type, :cat_axis_base_time_unit,
1116
+ :cat_axis_major_time_unit, :cat_axis_minor_time_unit,
1117
+ :cat_axis_major_unit, :cat_axis_minor_unit,
1118
+ :cat_axis_title_font, :val_axis_title_font,
1119
+ :cat_axis_title_fill, :cat_axis_title_no_fill, :cat_axis_title_line_color, :cat_axis_title_line_width, :cat_axis_title_line_dash,
1120
+ :val_axis_title_fill, :val_axis_title_no_fill, :val_axis_title_line_color, :val_axis_title_line_width, :val_axis_title_line_dash,
1121
+ :title_layout, :cat_axis_title_layout, :val_axis_title_layout,
1122
+ :title_rotation, :cat_axis_title_rotation, :val_axis_title_rotation,
1123
+ :chart_fill, :chart_no_fill, :chart_line_color, :chart_line_width, :chart_line_dash,
1124
+ :protection, :print_settings, :chart_font
1125
+
1126
+ CHART_TYPES = %w[barChart lineChart pieChart areaChart scatterChart doughnutChart radarChart
1127
+ bar3DChart line3DChart pie3DChart area3DChart surfaceChart surface3DChart stockChart bubbleChart
1128
+ ofPieChart].freeze
1129
+
1130
+ def initialize
1131
+ @chart_type = nil
1132
+ @title = nil
1133
+ @title_overlay = nil
1134
+ @title_font = nil
1135
+ @title_fill_color = nil
1136
+ @title_no_fill = nil
1137
+ @title_line_color = nil
1138
+ @title_line_width = nil
1139
+ @title_line_dash = nil
1140
+ @title_layout = nil
1141
+ @cat_axis_title_layout = nil
1142
+ @val_axis_title_layout = nil
1143
+ @title_rotation = nil
1144
+ @cat_axis_title_rotation = nil
1145
+ @val_axis_title_rotation = nil
1146
+ @inside_title_layout = false
1147
+ @inside_title_sp_pr = false
1148
+ @inside_title_ln = false
1149
+ @inside_title_solid_fill = false
1150
+ @chart_fill = nil
1151
+ @chart_no_fill = nil
1152
+ @chart_line_color = nil
1153
+ @chart_line_width = nil
1154
+ @chart_line_dash = nil
1155
+ @inside_chart = false
1156
+ @inside_chart_space_sp_pr = false
1157
+ @inside_chart_space_ln = false
1158
+ @inside_chart_space_solid_fill = false
1159
+ @chart_font = nil
1160
+ @inside_chart_space_tx_pr = false
1161
+ @protection = nil
1162
+ @inside_protection = false
1163
+ @print_settings = nil
1164
+ @inside_print_settings = false
1165
+ @inside_ps_header_footer = false
1166
+ @inside_ps_odd_header = false
1167
+ @inside_ps_odd_footer = false
1168
+ @inside_ps_even_header = false
1169
+ @inside_ps_even_footer = false
1170
+ @inside_ps_first_header = false
1171
+ @inside_ps_first_footer = false
1172
+ @series = []
1173
+ @legend = {}
1174
+ @data_labels = {}
1175
+ @cat_axis_title = nil
1176
+ @val_axis_title = nil
1177
+ @grouping = nil
1178
+ @bar_dir = nil
1179
+ @vary_colors = nil
1180
+ @plot_vis_only = nil
1181
+ @disp_blanks_as = nil
1182
+ @style = nil
1183
+ @auto_title_deleted = nil
1184
+ @rounded_corners = nil
1185
+ @cat_axis_tick_lbl_pos = nil
1186
+ @val_axis_tick_lbl_pos = nil
1187
+ @cat_axis_major_gridlines = false
1188
+ @val_axis_major_gridlines = false
1189
+ @cat_axis_minor_gridlines = false
1190
+ @val_axis_minor_gridlines = false
1191
+ @inside_gridlines = false
1192
+ @inside_gridlines_sp_pr = false
1193
+ @inside_gridlines_ln = false
1194
+ @inside_gridlines_solid_fill = false
1195
+ @gridlines_target = nil
1196
+ @show_d_lbls_over_max = nil
1197
+ @cat_axis_delete = nil
1198
+ @val_axis_delete = nil
1199
+ @cat_axis_orientation = nil
1200
+ @val_axis_orientation = nil
1201
+ @gap_width = nil
1202
+ @overlap = nil
1203
+ @gap_depth = nil
1204
+ @bar_shape = nil
1205
+ @bubble_3d = nil
1206
+ @bubble_scale = nil
1207
+ @show_neg_bubbles = nil
1208
+ @size_represents = nil
1209
+ @view_3d = nil
1210
+ @cat_axis_num_fmt = nil
1211
+ @val_axis_num_fmt = nil
1212
+ @cat_axis_major_tick_mark = nil
1213
+ @cat_axis_minor_tick_mark = nil
1214
+ @val_axis_major_tick_mark = nil
1215
+ @val_axis_minor_tick_mark = nil
1216
+ @cat_axis_crosses = nil
1217
+ @val_axis_crosses = nil
1218
+ @cat_axis_crosses_at = nil
1219
+ @val_axis_crosses_at = nil
1220
+ @cat_axis_tick_lbl_skip = nil
1221
+ @cat_axis_tick_mark_skip = nil
1222
+ @cat_axis_lbl_offset = nil
1223
+ @cat_axis_auto = nil
1224
+ @cat_axis_lbl_algn = nil
1225
+ @cat_axis_no_multi_lvl_lbl = nil
1226
+ @val_axis_cross_between = nil
1227
+ @val_axis_major_unit = nil
1228
+ @val_axis_minor_unit = nil
1229
+ @val_axis_disp_units = nil
1230
+ @inside_disp_units = false
1231
+ @inside_disp_units_lbl = false
1232
+ @inside_disp_units_lbl_sp_pr = false
1233
+ @inside_disp_units_lbl_ln = false
1234
+ @inside_disp_units_lbl_solid_fill = false
1235
+ @disp_units_lbl_font = nil
1236
+ @cat_axis_scaling_max = nil
1237
+ @cat_axis_scaling_min = nil
1238
+ @val_axis_scaling_max = nil
1239
+ @val_axis_scaling_min = nil
1240
+ @cat_axis_log_base = nil
1241
+ @val_axis_log_base = nil
1242
+ @first_slice_ang = nil
1243
+ @hole_size = nil
1244
+ @smooth = nil
1245
+ @marker = nil
1246
+ @drop_lines = nil
1247
+ @inside_drop_lines = false
1248
+ @inside_drop_lines_sp_pr = false
1249
+ @inside_drop_lines_ln = false
1250
+ @inside_drop_lines_solid_fill = false
1251
+ @hi_low_lines = nil
1252
+ @inside_hi_low_lines = false
1253
+ @inside_hi_low_lines_sp_pr = false
1254
+ @inside_hi_low_lines_ln = false
1255
+ @inside_hi_low_lines_solid_fill = false
1256
+ @ser_lines = nil
1257
+ @inside_ser_lines = false
1258
+ @inside_ser_lines_sp_pr = false
1259
+ @inside_ser_lines_ln = false
1260
+ @inside_ser_lines_solid_fill = false
1261
+ @up_down_bars = nil
1262
+ @inside_up_down_bars = false
1263
+ @inside_up_bars = false
1264
+ @inside_down_bars = false
1265
+ @inside_up_down_bar_sp_pr = false
1266
+ @inside_up_down_bar_ln = false
1267
+ @inside_up_down_bar_solid_fill = false
1268
+ @scatter_style = nil
1269
+ @radar_style = nil
1270
+ @cat_axis_pos = nil
1271
+ @cat_axis_type = nil
1272
+ @cat_axis_base_time_unit = nil
1273
+ @cat_axis_major_time_unit = nil
1274
+ @cat_axis_minor_time_unit = nil
1275
+ @cat_axis_major_unit = nil
1276
+ @cat_axis_minor_unit = nil
1277
+ @val_axis_pos = nil
1278
+ @wireframe = nil
1279
+ @band_fmts = nil
1280
+ @of_pie_type = nil
1281
+ @split_type = nil
1282
+ @split_pos = nil
1283
+ @cust_split = nil
1284
+ @inside_cust_split = false
1285
+ @second_pie_size = nil
1286
+ @inside_band_fmts = false
1287
+ @inside_band_fmt = false
1288
+ @inside_band_fmt_sp_pr = false
1289
+ @inside_band_fmt_ln = false
1290
+ @inside_band_fmt_solid_fill = false
1291
+ @current_band_fmt = nil
1292
+ @data_table = nil
1293
+ @plot_area_fill = nil
1294
+ @plot_area_no_fill = nil
1295
+ @plot_area_line_color = nil
1296
+ @plot_area_line_width = nil
1297
+ @plot_area_line_dash = nil
1298
+ @inside_plot_area = false
1299
+ @inside_plot_area_layout = false
1300
+ @inside_plot_area_sp_pr = false
1301
+ @inside_plot_area_ln = false
1302
+ @inside_plot_area_solid_fill = false
1303
+ @inside_ax_sp_pr = false
1304
+ @inside_ax_ln = false
1305
+ @inside_ax_solid_fill = false
1306
+ @cat_axis_fill = nil
1307
+ @cat_axis_no_fill = nil
1308
+ @val_axis_fill = nil
1309
+ @val_axis_no_fill = nil
1310
+ @cat_axis_line_color = nil
1311
+ @cat_axis_line_width = nil
1312
+ @cat_axis_line_dash = nil
1313
+ @val_axis_line_color = nil
1314
+ @val_axis_line_width = nil
1315
+ @val_axis_line_dash = nil
1316
+ @inside_d_table = false
1317
+ @inside_d_table_sp_pr = false
1318
+ @inside_d_table_ln = false
1319
+ @inside_d_table_solid_fill = false
1320
+ @d_table_font = nil
1321
+ @inside_view_3d = false
1322
+ @inside_wall = false
1323
+ @current_wall = nil
1324
+ @inside_wall_sp_pr = false
1325
+ @inside_wall_ln = false
1326
+ @inside_wall_solid_fill = false
1327
+ @floor = nil
1328
+ @side_wall = nil
1329
+ @back_wall = nil
1330
+ @inside_scaling = false
1331
+ @inside_title = false
1332
+ @inside_t = false
1333
+ @text_buffer = +""
1334
+ @inside_ser = false
1335
+ @inside_ser_sp_pr = false
1336
+ @inside_ser_solid_fill = false
1337
+ @inside_ser_ln = false
1338
+ @inside_ser_marker = false
1339
+ @inside_marker_sp_pr = false
1340
+ @inside_marker_ln = false
1341
+ @inside_marker_solid_fill = false
1342
+ @inside_dpt = false
1343
+ @inside_dpt_marker = false
1344
+ @inside_dpt_marker_sp_pr = false
1345
+ @inside_dpt_marker_solid_fill = false
1346
+ @inside_dpt_marker_ln = false
1347
+ @inside_dpt_sp_pr = false
1348
+ @inside_dpt_solid_fill = false
1349
+ @inside_dpt_ln = false
1350
+ @current_dpt = nil
1351
+ @inside_trendline = false
1352
+ @inside_trendline_name = false
1353
+ @current_trendline = nil
1354
+ @inside_trendline_sp_pr = false
1355
+ @inside_trendline_ln = false
1356
+ @inside_trendline_solid_fill = false
1357
+ @inside_trendline_lbl = false
1358
+ @inside_trendline_lbl_layout = false
1359
+ @inside_trendline_lbl_tx = false
1360
+ @inside_trendline_lbl_sp_pr = false
1361
+ @inside_trendline_lbl_ln = false
1362
+ @inside_trendline_lbl_solid_fill = false
1363
+ @trendline_lbl_font = nil
1364
+ @inside_err_bars = false
1365
+ @current_err_bars = nil
1366
+ @inside_err_bars_sp_pr = false
1367
+ @inside_err_bars_ln = false
1368
+ @inside_err_bars_solid_fill = false
1369
+ @inside_err_bars_plus = false
1370
+ @inside_err_bars_minus = false
1371
+ @current_ser = nil
1372
+ @inside_cat = false
1373
+ @inside_val = false
1374
+ @inside_bubble_size = false
1375
+ @inside_f = false
1376
+ @inside_num_cache = false
1377
+ @inside_str_cache = false
1378
+ @inside_cache_pt = false
1379
+ @inside_cache_v = false
1380
+ @current_cache_idx = 0
1381
+ @cache_values = []
1382
+ @inside_legend = false
1383
+ @inside_legend_entry = false
1384
+ @inside_legend_layout = false
1385
+ @current_legend_entry = nil
1386
+ @legend_font = nil
1387
+ @inside_legend_sp_pr = false
1388
+ @inside_legend_ln = false
1389
+ @inside_legend_solid_fill = false
1390
+ @inside_dlbls = false
1391
+ @inside_dlbl = false
1392
+ @inside_dlbl_tx = false
1393
+ @inside_dlbl_sp_pr = false
1394
+ @inside_dlbl_solid_fill = false
1395
+ @inside_dlbl_ln = false
1396
+ @inside_dlbl_layout = false
1397
+ @current_dlbl = nil
1398
+ @inside_dlbls_sp_pr = false
1399
+ @inside_dlbls_ln = false
1400
+ @inside_dlbls_solid_fill = false
1401
+ @dlbls_font = nil
1402
+ @dlbl_target = nil
1403
+ @inside_separator = false
1404
+ @inside_leader_lines = false
1405
+ @inside_leader_lines_sp_pr = false
1406
+ @inside_leader_lines_ln = false
1407
+ @inside_leader_lines_solid_fill = false
1408
+ @inside_cat_ax = false
1409
+ @inside_val_ax = false
1410
+ @inside_ax_title = false
1411
+ @inside_ax_title_rpr = false
1412
+ @inside_ax_title_sp_pr = false
1413
+ @inside_ax_title_ln = false
1414
+ @inside_ax_title_solid_fill = false
1415
+ @cat_axis_title_font = nil
1416
+ @val_axis_title_font = nil
1417
+ @cat_axis_title_fill = nil
1418
+ @cat_axis_title_no_fill = nil
1419
+ @cat_axis_title_line_color = nil
1420
+ @cat_axis_title_line_width = nil
1421
+ @cat_axis_title_line_dash = nil
1422
+ @val_axis_title_fill = nil
1423
+ @val_axis_title_no_fill = nil
1424
+ @val_axis_title_line_color = nil
1425
+ @val_axis_title_line_width = nil
1426
+ @val_axis_title_line_dash = nil
1427
+ @inside_title_rpr = false
1428
+ @title_depth = 0
1429
+ @inside_axis_tx_pr = false
1430
+ @inside_axis_def_rpr = false
1431
+ @cat_axis_font = nil
1432
+ @val_axis_font = nil
1433
+ end
1434
+
1435
+ def start_element(_uri, local_name, qname, attributes)
1436
+ name = element_name(local_name, qname)
1437
+ @chart_type = name if CHART_TYPES.include?(name)
1438
+
1439
+ case name
1440
+ when "chart"
1441
+ @inside_chart = true
1442
+ when "plotArea"
1443
+ @inside_plot_area = true
1444
+ when "grouping"
1445
+ @grouping = attributes["val"] if attributes["val"]
1446
+ when "barDir"
1447
+ @bar_dir = attributes["val"] if attributes["val"]
1448
+ when "varyColors"
1449
+ @vary_colors = attributes["val"] == "1" if attributes["val"]
1450
+ when "autoTitleDeleted"
1451
+ @auto_title_deleted = attributes["val"] == "1" if attributes["val"]
1452
+ when "view3D"
1453
+ @inside_view_3d = true
1454
+ @view_3d = {}
1455
+ when "rotX"
1456
+ @view_3d[:rot_x] = attributes["val"].to_i if @inside_view_3d && attributes["val"]
1457
+ when "hPercent"
1458
+ @view_3d[:h_percent] = attributes["val"].to_i if @inside_view_3d && attributes["val"]
1459
+ when "rotY"
1460
+ @view_3d[:rot_y] = attributes["val"].to_i if @inside_view_3d && attributes["val"]
1461
+ when "depthPercent"
1462
+ @view_3d[:depth_percent] = attributes["val"].to_i if @inside_view_3d && attributes["val"]
1463
+ when "rAngAx"
1464
+ @view_3d[:r_ang_ax] = attributes["val"] == "1" if @inside_view_3d && attributes["val"]
1465
+ when "perspective"
1466
+ @view_3d[:perspective] = attributes["val"].to_i if @inside_view_3d && attributes["val"]
1467
+ when "floor"
1468
+ @inside_wall = true
1469
+ @current_wall = (@floor ||= {})
1470
+ when "sideWall"
1471
+ @inside_wall = true
1472
+ @current_wall = (@side_wall ||= {})
1473
+ when "backWall"
1474
+ @inside_wall = true
1475
+ @current_wall = (@back_wall ||= {})
1476
+ when "gapWidth"
1477
+ if @inside_up_down_bars && attributes["val"]
1478
+ @up_down_bars[:gap_width] = attributes["val"].to_i
1479
+ elsif attributes["val"]
1480
+ @gap_width = attributes["val"].to_i
1481
+ end
1482
+ when "overlap"
1483
+ @overlap = attributes["val"]&.to_i if attributes["val"]
1484
+ when "gapDepth"
1485
+ @gap_depth = attributes["val"]&.to_i if attributes["val"]
1486
+ when "shape"
1487
+ if @inside_ser && @current_ser && attributes["val"]
1488
+ @current_ser[:shape] = attributes["val"]
1489
+ elsif attributes["val"]
1490
+ @bar_shape = attributes["val"]
1491
+ end
1492
+ when "bubble3D"
1493
+ if @inside_dpt && @current_dpt && attributes["val"]
1494
+ @current_dpt[:bubble_3d] = attributes["val"] == "1"
1495
+ elsif attributes["val"] && !@inside_ser
1496
+ @bubble_3d = attributes["val"] == "1"
1497
+ end
1498
+ when "bubbleScale"
1499
+ @bubble_scale = attributes["val"]&.to_i if attributes["val"]
1500
+ when "showNegBubbles"
1501
+ @show_neg_bubbles = attributes["val"] == "1" if attributes["val"]
1502
+ when "sizeRepresents"
1503
+ @size_represents = attributes["val"] if attributes["val"]
1504
+ when "firstSliceAng"
1505
+ @first_slice_ang = attributes["val"]&.to_i if attributes["val"]
1506
+ when "holeSize"
1507
+ @hole_size = attributes["val"]&.to_i if attributes["val"]
1508
+ when "smooth"
1509
+ if @inside_ser && @current_ser && attributes["val"]
1510
+ @current_ser[:smooth] = attributes["val"] == "1"
1511
+ elsif attributes["val"]
1512
+ @smooth = attributes["val"] == "1"
1513
+ end
1514
+ when "invertIfNegative"
1515
+ if @inside_dpt && @current_dpt && attributes["val"]
1516
+ @current_dpt[:invert_if_negative] = attributes["val"] == "1"
1517
+ elsif @inside_ser && @current_ser && attributes["val"]
1518
+ @current_ser[:invert_if_negative] = attributes["val"] == "1"
1519
+ end
1520
+ when "explosion"
1521
+ if @inside_dpt && @current_dpt && attributes["val"]
1522
+ @current_dpt[:explosion] = attributes["val"].to_i
1523
+ elsif @inside_ser && @current_ser && attributes["val"]
1524
+ @current_ser[:explosion] = attributes["val"].to_i
1525
+ end
1526
+ when "marker"
1527
+ if @inside_dpt
1528
+ @inside_dpt_marker = true
1529
+ elsif attributes["val"] && !@inside_ser
1530
+ @marker = attributes["val"] == "1"
1531
+ elsif @inside_ser
1532
+ @inside_ser_marker = true
1533
+ end
1534
+ when "symbol"
1535
+ if @inside_dpt_marker && @current_dpt && attributes["val"]
1536
+ @current_dpt[:marker_symbol] = attributes["val"]
1537
+ elsif @inside_ser_marker && @current_ser && attributes["val"]
1538
+ @current_ser[:marker_symbol] = attributes["val"]
1539
+ end
1540
+ when "size"
1541
+ if @inside_dpt_marker && @current_dpt && attributes["val"]
1542
+ @current_dpt[:marker_size] = attributes["val"].to_i
1543
+ elsif @inside_ser_marker && @current_ser && attributes["val"]
1544
+ @current_ser[:marker_size] = attributes["val"].to_i
1545
+ end
1546
+ when "scatterStyle"
1547
+ @scatter_style = attributes["val"] if attributes["val"]
1548
+ when "radarStyle"
1549
+ @radar_style = attributes["val"] if attributes["val"]
1550
+ when "ofPieType"
1551
+ @of_pie_type = attributes["val"] if attributes["val"]
1552
+ when "splitType"
1553
+ @split_type = attributes["val"] if attributes["val"]
1554
+ when "splitPos"
1555
+ @split_pos = attributes["val"].to_f if attributes["val"]
1556
+ when "custSplit"
1557
+ @cust_split = []
1558
+ @inside_cust_split = true
1559
+ when "secondPiePt"
1560
+ @cust_split << attributes["val"].to_i if @inside_cust_split && attributes["val"]
1561
+ when "secondPieSize"
1562
+ @second_pie_size = attributes["val"].to_i if attributes["val"]
1563
+ when "dropLines"
1564
+ @drop_lines = true
1565
+ @inside_drop_lines = true
1566
+ when "hiLowLines"
1567
+ @hi_low_lines = true
1568
+ @inside_hi_low_lines = true
1569
+ when "serLines"
1570
+ @ser_lines = true
1571
+ @inside_ser_lines = true
1572
+ when "upDownBars"
1573
+ @inside_up_down_bars = true
1574
+ @up_down_bars = {}
1575
+ when "upBars"
1576
+ @inside_up_bars = true if @inside_up_down_bars
1577
+ when "downBars"
1578
+ @inside_down_bars = true if @inside_up_down_bars
1579
+ when "wireframe"
1580
+ @wireframe = attributes["val"] == "1" if attributes["val"]
1581
+ when "bandFmts"
1582
+ @inside_band_fmts = true
1583
+ @band_fmts = []
1584
+ when "bandFmt"
1585
+ if @inside_band_fmts
1586
+ @inside_band_fmt = true
1587
+ @current_band_fmt = {}
1588
+ end
1589
+ when "ser"
1590
+ @inside_ser = true
1591
+ @current_ser = {}
1592
+ when "dPt"
1593
+ if @inside_ser
1594
+ @inside_dpt = true
1595
+ @current_dpt = {}
1596
+ end
1597
+ when "trendline"
1598
+ if @inside_ser
1599
+ @inside_trendline = true
1600
+ @current_trendline = {}
1601
+ end
1602
+ when "trendlineType"
1603
+ @current_trendline[:type] = attributes["val"] if @inside_trendline && @current_trendline && attributes["val"]
1604
+ when "order"
1605
+ if @inside_trendline && @current_trendline && attributes["val"]
1606
+ @current_trendline[:order] = attributes["val"].to_i
1607
+ elsif @inside_ser && @current_ser && attributes["val"]
1608
+ @current_ser[:order] = attributes["val"].to_i
1609
+ end
1610
+ when "period"
1611
+ @current_trendline[:period] = attributes["val"].to_i if @inside_trendline && @current_trendline && attributes["val"]
1612
+ when "forward"
1613
+ @current_trendline[:forward] = attributes["val"].to_f if @inside_trendline && @current_trendline && attributes["val"]
1614
+ when "backward"
1615
+ @current_trendline[:backward] = attributes["val"].to_f if @inside_trendline && @current_trendline && attributes["val"]
1616
+ when "intercept"
1617
+ @current_trendline[:intercept] = attributes["val"].to_f if @inside_trendline && @current_trendline && attributes["val"]
1618
+ when "dispRSqr"
1619
+ @current_trendline[:disp_r_sqr] = attributes["val"] == "1" if @inside_trendline && @current_trendline && attributes["val"]
1620
+ when "dispEq"
1621
+ @current_trendline[:disp_eq] = attributes["val"] == "1" if @inside_trendline && @current_trendline && attributes["val"]
1622
+ when "trendlineLbl"
1623
+ @inside_trendline_lbl = true if @inside_trendline
1624
+ when "errBars"
1625
+ if @inside_ser
1626
+ @inside_err_bars = true
1627
+ @current_err_bars = {}
1628
+ end
1629
+ when "errDir"
1630
+ @current_err_bars[:direction] = attributes["val"] if @inside_err_bars && @current_err_bars && attributes["val"]
1631
+ when "errBarType"
1632
+ @current_err_bars[:bar_type] = attributes["val"] if @inside_err_bars && @current_err_bars && attributes["val"]
1633
+ when "errValType"
1634
+ @current_err_bars[:val_type] = attributes["val"] if @inside_err_bars && @current_err_bars && attributes["val"]
1635
+ when "noEndCap"
1636
+ @current_err_bars[:no_end_cap] = attributes["val"] == "1" if @inside_err_bars && @current_err_bars && attributes["val"]
1637
+ when "plus"
1638
+ @inside_err_bars_plus = true if @inside_err_bars
1639
+ when "minus"
1640
+ @inside_err_bars_minus = true if @inside_err_bars
1641
+ when "name"
1642
+ if @inside_trendline
1643
+ @inside_trendline_name = true
1644
+ @text_buffer = +""
1645
+ end
1646
+ when "spPr"
1647
+ if @inside_band_fmt
1648
+ @inside_band_fmt_sp_pr = true
1649
+ elsif @inside_dpt_marker
1650
+ @inside_dpt_marker_sp_pr = true
1651
+ elsif @inside_dpt
1652
+ @inside_dpt_sp_pr = true
1653
+ elsif @inside_ser_marker
1654
+ @inside_marker_sp_pr = true
1655
+ elsif @inside_leader_lines
1656
+ @inside_leader_lines_sp_pr = true
1657
+ elsif @inside_dlbl
1658
+ @inside_dlbl_sp_pr = true
1659
+ elsif @inside_dlbls && !@inside_dlbl
1660
+ @inside_dlbls_sp_pr = true
1661
+ elsif @inside_trendline_lbl
1662
+ @inside_trendline_lbl_sp_pr = true
1663
+ elsif @inside_trendline
1664
+ @inside_trendline_sp_pr = true
1665
+ elsif @inside_err_bars
1666
+ @inside_err_bars_sp_pr = true
1667
+ elsif @inside_ser
1668
+ @inside_ser_sp_pr = true
1669
+ elsif @inside_drop_lines
1670
+ @inside_drop_lines_sp_pr = true
1671
+ elsif @inside_hi_low_lines
1672
+ @inside_hi_low_lines_sp_pr = true
1673
+ elsif @inside_ser_lines
1674
+ @inside_ser_lines_sp_pr = true
1675
+ elsif @inside_up_bars || @inside_down_bars
1676
+ @inside_up_down_bar_sp_pr = true
1677
+ elsif @inside_gridlines
1678
+ @inside_gridlines_sp_pr = true
1679
+ elsif @inside_ax_title
1680
+ @inside_ax_title_sp_pr = true
1681
+ elsif @inside_disp_units_lbl
1682
+ @inside_disp_units_lbl_sp_pr = true
1683
+ elsif @inside_cat_ax || @inside_val_ax
1684
+ @inside_ax_sp_pr = true
1685
+ elsif @inside_wall && @current_wall
1686
+ @inside_wall_sp_pr = true
1687
+ elsif @inside_legend
1688
+ @inside_legend_sp_pr = true
1689
+ elsif @inside_d_table
1690
+ @inside_d_table_sp_pr = true
1691
+ elsif @inside_title && @title_depth == 1 && !@inside_ax_title
1692
+ @inside_title_sp_pr = true
1693
+ elsif @inside_plot_area
1694
+ @inside_plot_area_sp_pr = true
1695
+ elsif !@inside_chart
1696
+ @inside_chart_space_sp_pr = true
1697
+ end
1698
+ when "ln"
1699
+ if @inside_band_fmt_sp_pr && @current_band_fmt
1700
+ @inside_band_fmt_ln = true
1701
+ @current_band_fmt[:line_width] = attributes["w"].to_i / 12_700.0 if attributes["w"]
1702
+ elsif @inside_dpt_marker_sp_pr && @current_dpt
1703
+ @inside_dpt_marker_ln = true
1704
+ @current_dpt[:marker_line_width] = attributes["w"].to_i / 12_700.0 if attributes["w"]
1705
+ elsif @inside_dpt && @inside_dpt_sp_pr
1706
+ @inside_dpt_ln = true
1707
+ @current_dpt[:line_width] = attributes["w"].to_i / 12_700.0 if @current_dpt && attributes["w"]
1708
+ elsif @inside_marker_sp_pr && @current_ser
1709
+ @inside_marker_ln = true
1710
+ @current_ser[:marker_line_width] = attributes["w"].to_i / 12_700.0 if attributes["w"]
1711
+ elsif @inside_dlbl_sp_pr && @current_dlbl
1712
+ @inside_dlbl_ln = true
1713
+ @current_dlbl[:line_width] = attributes["w"].to_i / 12_700.0 if attributes["w"]
1714
+ elsif @inside_dlbls_sp_pr
1715
+ @inside_dlbls_ln = true
1716
+ if attributes["w"]
1717
+ lw = attributes["w"].to_i / 12_700.0
1718
+ dl_target = @dlbl_target || @data_labels
1719
+ dl_target[:line_width] = lw
1720
+ @data_labels[:line_width] = lw if dl_target != @data_labels
1721
+ end
1722
+ elsif @inside_trendline_lbl_sp_pr
1723
+ @inside_trendline_lbl_ln = true
1724
+ (@current_trendline[:label] ||= {})[:line_width] = attributes["w"].to_i / 12_700.0 if @current_trendline && attributes["w"]
1725
+ elsif @inside_trendline_sp_pr
1726
+ @inside_trendline_ln = true
1727
+ @current_trendline[:line_width] = attributes["w"].to_i / 12_700.0 if @current_trendline && attributes["w"]
1728
+ elsif @inside_err_bars_sp_pr
1729
+ @inside_err_bars_ln = true
1730
+ @current_err_bars[:line_width] = attributes["w"].to_i / 12_700.0 if @current_err_bars && attributes["w"]
1731
+ elsif @inside_leader_lines_sp_pr
1732
+ @inside_leader_lines_ln = true
1733
+ if attributes["w"] && @inside_dlbls
1734
+ ll = (@dlbl_target[:leader_lines] ||= {})
1735
+ ll[:line_width] = attributes["w"].to_i / 12_700.0
1736
+ if @dlbl_target != @data_labels
1737
+ ll2 = (@data_labels[:leader_lines] ||= {})
1738
+ ll2[:line_width] = attributes["w"].to_i / 12_700.0
1739
+ end
1740
+ end
1741
+ elsif @inside_ser && @inside_ser_sp_pr
1742
+ @inside_ser_ln = true
1743
+ @current_ser[:line_width] = attributes["w"].to_i / 12_700.0 if @current_ser && attributes["w"]
1744
+ @current_ser[:line_cap] = attributes["cap"] if @current_ser && attributes["cap"]
1745
+ elsif @inside_drop_lines_sp_pr
1746
+ @inside_drop_lines_ln = true
1747
+ if attributes["w"]
1748
+ @drop_lines = {} if @drop_lines == true
1749
+ @drop_lines[:line_width] = attributes["w"].to_i / 12_700.0
1750
+ end
1751
+ elsif @inside_hi_low_lines_sp_pr
1752
+ @inside_hi_low_lines_ln = true
1753
+ if attributes["w"]
1754
+ @hi_low_lines = {} if @hi_low_lines == true
1755
+ @hi_low_lines[:line_width] = attributes["w"].to_i / 12_700.0
1756
+ end
1757
+ elsif @inside_ser_lines_sp_pr
1758
+ @inside_ser_lines_ln = true
1759
+ if attributes["w"]
1760
+ @ser_lines = {} if @ser_lines == true
1761
+ @ser_lines[:line_width] = attributes["w"].to_i / 12_700.0
1762
+ end
1763
+ elsif @inside_up_down_bar_sp_pr
1764
+ @inside_up_down_bar_ln = true
1765
+ bar_key = @inside_up_bars ? :up_bars : :down_bars
1766
+ if attributes["w"]
1767
+ @up_down_bars[bar_key] ||= {}
1768
+ @up_down_bars[bar_key][:line_width] = attributes["w"].to_i / 12_700.0
1769
+ end
1770
+ elsif @inside_ax_title_sp_pr
1771
+ @inside_ax_title_ln = true
1772
+ if attributes["w"]
1773
+ lw = attributes["w"].to_i / 12_700.0
1774
+ if @inside_cat_ax
1775
+ @cat_axis_title_line_width = lw
1776
+ elsif @inside_val_ax
1777
+ @val_axis_title_line_width = lw
1778
+ end
1779
+ end
1780
+ elsif @inside_disp_units_lbl_sp_pr
1781
+ @inside_disp_units_lbl_ln = true
1782
+ if attributes["w"]
1783
+ du = @val_axis_disp_units
1784
+ du = {} unless du.is_a?(Hash)
1785
+ @val_axis_disp_units = du
1786
+ (du[:label] ||= {})[:line_width] = attributes["w"].to_i / 12_700.0
1787
+ end
1788
+ elsif @inside_gridlines_sp_pr
1789
+ @inside_gridlines_ln = true
1790
+ if attributes["w"] && @gridlines_target
1791
+ gl = instance_variable_get(:"@#{@gridlines_target}")
1792
+ gl = {} if gl == true
1793
+ gl[:line_width] = attributes["w"].to_i / 12_700.0
1794
+ instance_variable_set(:"@#{@gridlines_target}", gl)
1795
+ end
1796
+ elsif @inside_plot_area_sp_pr
1797
+ @inside_plot_area_ln = true
1798
+ @plot_area_line_width = attributes["w"].to_i if attributes["w"]
1799
+ elsif @inside_ax_sp_pr
1800
+ @inside_ax_ln = true
1801
+ if attributes["w"]
1802
+ if @inside_cat_ax
1803
+ @cat_axis_line_width = attributes["w"].to_i
1804
+ elsif @inside_val_ax
1805
+ @val_axis_line_width = attributes["w"].to_i
1806
+ end
1807
+ end
1808
+ elsif @inside_wall_sp_pr
1809
+ @inside_wall_ln = true
1810
+ @current_wall[:line_width] = attributes["w"].to_i if @current_wall && attributes["w"]
1811
+ elsif @inside_legend_sp_pr
1812
+ @inside_legend_ln = true
1813
+ @legend[:line_width] = attributes["w"].to_i / 12_700.0 if attributes["w"]
1814
+ elsif @inside_d_table_sp_pr
1815
+ @inside_d_table_ln = true
1816
+ @data_table[:line_width] = attributes["w"].to_i / 12_700.0 if @data_table && attributes["w"]
1817
+ elsif @inside_title_sp_pr
1818
+ @inside_title_ln = true
1819
+ @title_line_width = attributes["w"].to_i / 12_700.0 if attributes["w"]
1820
+ elsif @inside_chart_space_sp_pr
1821
+ @inside_chart_space_ln = true
1822
+ @chart_line_width = attributes["w"].to_i / 12_700.0 if attributes["w"]
1823
+ end
1824
+ when "round"
1825
+ @current_ser[:line_join] = "round" if @inside_ser && @inside_ser_ln && @current_ser
1826
+ when "bevel"
1827
+ @current_ser[:line_join] = "bevel" if @inside_ser && @inside_ser_ln && @current_ser
1828
+ when "prstDash"
1829
+ if @inside_band_fmt_ln && @current_band_fmt && attributes["val"]
1830
+ @current_band_fmt[:line_dash] = attributes["val"]
1831
+ elsif @inside_gridlines_ln && @gridlines_target && attributes["val"]
1832
+ gl = instance_variable_get(:"@#{@gridlines_target}")
1833
+ gl = {} if gl == true
1834
+ gl[:line_dash] = attributes["val"]
1835
+ instance_variable_set(:"@#{@gridlines_target}", gl)
1836
+ elsif @inside_trendline_lbl_ln && @current_trendline && attributes["val"]
1837
+ (@current_trendline[:label] ||= {})[:line_dash] = attributes["val"]
1838
+ elsif @inside_trendline_ln && @current_trendline && attributes["val"]
1839
+ @current_trendline[:line_dash] = attributes["val"]
1840
+ elsif @inside_err_bars_ln && @current_err_bars && attributes["val"]
1841
+ @current_err_bars[:line_dash] = attributes["val"]
1842
+ elsif @inside_ser && @inside_ser_ln && @current_ser && attributes["val"]
1843
+ @current_ser[:line_dash] = attributes["val"]
1844
+ elsif @inside_marker_sp_pr && @inside_marker_ln && @current_ser && attributes["val"]
1845
+ @current_ser[:marker_line_dash] = attributes["val"]
1846
+ elsif @inside_dpt_marker_sp_pr && @inside_dpt_marker_ln && @current_dpt && attributes["val"]
1847
+ @current_dpt[:marker_line_dash] = attributes["val"]
1848
+ elsif @inside_drop_lines_ln && attributes["val"]
1849
+ @drop_lines = {} if @drop_lines == true
1850
+ @drop_lines[:line_dash] = attributes["val"]
1851
+ elsif @inside_hi_low_lines_ln && attributes["val"]
1852
+ @hi_low_lines = {} if @hi_low_lines == true
1853
+ @hi_low_lines[:line_dash] = attributes["val"]
1854
+ elsif @inside_ser_lines_ln && attributes["val"]
1855
+ @ser_lines = {} if @ser_lines == true
1856
+ @ser_lines[:line_dash] = attributes["val"]
1857
+ elsif @inside_leader_lines_ln && @inside_dlbls && attributes["val"]
1858
+ ll = (@dlbl_target[:leader_lines] ||= {})
1859
+ ll[:line_dash] = attributes["val"]
1860
+ if @dlbl_target != @data_labels
1861
+ ll2 = (@data_labels[:leader_lines] ||= {})
1862
+ ll2[:line_dash] = attributes["val"]
1863
+ end
1864
+ elsif @inside_legend_sp_pr && @inside_legend_ln && attributes["val"]
1865
+ @legend[:line_dash] = attributes["val"]
1866
+ elsif @inside_d_table_sp_pr && @inside_d_table_ln && attributes["val"]
1867
+ @data_table[:line_dash] = attributes["val"]
1868
+ elsif @inside_dlbl_sp_pr && @inside_dlbl_ln && @current_dlbl && attributes["val"]
1869
+ @current_dlbl[:line_dash] = attributes["val"]
1870
+ elsif @inside_dlbls_sp_pr && @inside_dlbls_ln && attributes["val"]
1871
+ @data_labels[:line_dash] = attributes["val"]
1872
+ elsif @inside_dpt && @inside_dpt_ln && @current_dpt && attributes["val"]
1873
+ @current_dpt[:line_dash] = attributes["val"]
1874
+ elsif @inside_up_down_bar_sp_pr && @inside_up_down_bar_ln && attributes["val"]
1875
+ bar_key = @inside_up_bars ? :up_bars : :down_bars
1876
+ @up_down_bars[bar_key] ||= {}
1877
+ @up_down_bars[bar_key][:line_dash] = attributes["val"]
1878
+ elsif @inside_wall_sp_pr && @inside_wall_ln && @current_wall && attributes["val"]
1879
+ @current_wall[:line_dash] = attributes["val"]
1880
+ elsif @inside_plot_area_sp_pr && @inside_plot_area_ln && attributes["val"]
1881
+ @plot_area_line_dash = attributes["val"]
1882
+ elsif @inside_ax_title_sp_pr && @inside_ax_title_ln && attributes["val"]
1883
+ if @inside_cat_ax
1884
+ @cat_axis_title_line_dash = attributes["val"]
1885
+ elsif @inside_val_ax
1886
+ @val_axis_title_line_dash = attributes["val"]
1887
+ end
1888
+ elsif @inside_disp_units_lbl_ln && attributes["val"]
1889
+ du = @val_axis_disp_units
1890
+ du = {} unless du.is_a?(Hash)
1891
+ @val_axis_disp_units = du
1892
+ (du[:label] ||= {})[:line_dash] = attributes["val"]
1893
+ elsif @inside_ax_sp_pr && @inside_ax_ln && attributes["val"]
1894
+ if @inside_cat_ax
1895
+ @cat_axis_line_dash = attributes["val"]
1896
+ elsif @inside_val_ax
1897
+ @val_axis_line_dash = attributes["val"]
1898
+ end
1899
+ elsif @inside_title_sp_pr && @inside_title_ln && attributes["val"]
1900
+ @title_line_dash = attributes["val"]
1901
+ elsif @inside_chart_space_sp_pr && @inside_chart_space_ln && attributes["val"]
1902
+ @chart_line_dash = attributes["val"]
1903
+ end
1904
+ when "miter"
1905
+ if @inside_ser && @inside_ser_ln && @current_ser
1906
+ @current_ser[:line_join] = "miter"
1907
+ @current_ser[:line_miter_limit] = attributes["lim"].to_i if attributes["lim"]
1908
+ end
1909
+ when "solidFill"
1910
+ if @inside_band_fmt_sp_pr
1911
+ @inside_band_fmt_solid_fill = true
1912
+ elsif @inside_dpt_marker_sp_pr
1913
+ @inside_dpt_marker_solid_fill = true
1914
+ elsif @inside_dpt && @inside_dpt_sp_pr
1915
+ @inside_dpt_solid_fill = true
1916
+ elsif @inside_marker_sp_pr
1917
+ @inside_marker_solid_fill = true
1918
+ elsif @inside_dlbl_sp_pr
1919
+ @inside_dlbl_solid_fill = true
1920
+ elsif @inside_dlbls_sp_pr
1921
+ @inside_dlbls_solid_fill = true
1922
+ elsif @inside_trendline_lbl_sp_pr
1923
+ @inside_trendline_lbl_solid_fill = true
1924
+ elsif @inside_trendline_sp_pr
1925
+ @inside_trendline_solid_fill = true
1926
+ elsif @inside_err_bars_sp_pr
1927
+ @inside_err_bars_solid_fill = true
1928
+ elsif @inside_leader_lines_sp_pr
1929
+ @inside_leader_lines_solid_fill = true
1930
+ elsif @inside_ser && @inside_ser_sp_pr
1931
+ @inside_ser_solid_fill = true
1932
+ elsif @inside_drop_lines_sp_pr
1933
+ @inside_drop_lines_solid_fill = true
1934
+ elsif @inside_hi_low_lines_sp_pr
1935
+ @inside_hi_low_lines_solid_fill = true
1936
+ elsif @inside_ser_lines_sp_pr
1937
+ @inside_ser_lines_solid_fill = true
1938
+ elsif @inside_up_down_bar_sp_pr
1939
+ @inside_up_down_bar_solid_fill = true
1940
+ elsif @inside_ax_title_sp_pr
1941
+ @inside_ax_title_solid_fill = true
1942
+ elsif @inside_disp_units_lbl_sp_pr
1943
+ @inside_disp_units_lbl_solid_fill = true
1944
+ elsif @inside_plot_area_sp_pr
1945
+ @inside_plot_area_solid_fill = true
1946
+ elsif @inside_gridlines_sp_pr
1947
+ @inside_gridlines_solid_fill = true
1948
+ elsif @inside_ax_sp_pr
1949
+ @inside_ax_solid_fill = true
1950
+ elsif @inside_wall_sp_pr
1951
+ @inside_wall_solid_fill = true
1952
+ elsif @inside_legend_sp_pr
1953
+ @inside_legend_solid_fill = true
1954
+ elsif @inside_d_table_sp_pr
1955
+ @inside_d_table_solid_fill = true
1956
+ elsif @inside_title_sp_pr
1957
+ @inside_title_solid_fill = true
1958
+ elsif @inside_chart_space_sp_pr
1959
+ @inside_chart_space_solid_fill = true
1960
+ end
1961
+ when "noFill"
1962
+ if @inside_band_fmt_sp_pr && @current_band_fmt
1963
+ @current_band_fmt[:no_fill] = true
1964
+ elsif @inside_dpt_marker_sp_pr && @inside_dpt_marker_ln && @current_dpt
1965
+ @current_dpt[:marker_no_line] = true
1966
+ elsif @inside_dpt_marker_sp_pr && @current_dpt
1967
+ @current_dpt[:marker_no_fill] = true
1968
+ elsif @inside_ser && @inside_ser_ln && @current_ser
1969
+ @current_ser[:no_line] = true
1970
+ elsif @inside_marker_sp_pr && @inside_marker_ln && @current_ser
1971
+ @current_ser[:marker_no_line] = true
1972
+ elsif @inside_ser && @inside_ser_sp_pr && @current_ser
1973
+ @current_ser[:no_fill] = true
1974
+ elsif @inside_marker_sp_pr && @current_ser
1975
+ @current_ser[:marker_no_fill] = true
1976
+ elsif @inside_dpt && @inside_dpt_ln && @current_dpt
1977
+ @current_dpt[:no_line] = true
1978
+ elsif @inside_dpt && @inside_dpt_sp_pr && @current_dpt
1979
+ @current_dpt[:no_fill] = true
1980
+ elsif @inside_up_down_bar_sp_pr
1981
+ bar_key = @inside_up_bars ? :up_bars : :down_bars
1982
+ @up_down_bars[bar_key] ||= {}
1983
+ @up_down_bars[bar_key][:no_fill] = true
1984
+ elsif @inside_wall_sp_pr && @current_wall
1985
+ @current_wall[:no_fill] = true
1986
+ elsif @inside_ax_title_sp_pr && @inside_cat_ax
1987
+ @cat_axis_title_no_fill = true
1988
+ elsif @inside_ax_title_sp_pr && @inside_val_ax
1989
+ @val_axis_title_no_fill = true
1990
+ elsif @inside_title_sp_pr
1991
+ @title_no_fill = true
1992
+ elsif @inside_dlbl_sp_pr && @inside_dlbl_ln && @current_dlbl
1993
+ @current_dlbl[:no_line] = true
1994
+ elsif @inside_dlbl_sp_pr && @current_dlbl
1995
+ @current_dlbl[:no_fill] = true
1996
+ elsif @inside_dlbls_sp_pr
1997
+ @data_labels[:no_fill] = true
1998
+ elsif @inside_trendline_lbl_sp_pr && @current_trendline
1999
+ (@current_trendline[:label] ||= {})[:no_fill] = true
2000
+ elsif @inside_err_bars_sp_pr && @current_err_bars
2001
+ @current_err_bars[:no_fill] = true
2002
+ elsif @inside_disp_units_lbl_sp_pr
2003
+ du = @val_axis_disp_units
2004
+ du = {} unless du.is_a?(Hash)
2005
+ @val_axis_disp_units = du
2006
+ (du[:label] ||= {})[:no_fill] = true
2007
+ elsif @inside_legend_sp_pr
2008
+ @legend[:no_fill] = true
2009
+ elsif @inside_d_table_sp_pr && @data_table
2010
+ @data_table[:no_fill] = true
2011
+ elsif @inside_ax_sp_pr && @inside_cat_ax
2012
+ @cat_axis_no_fill = true
2013
+ elsif @inside_ax_sp_pr && @inside_val_ax
2014
+ @val_axis_no_fill = true
2015
+ elsif @inside_plot_area_sp_pr
2016
+ @plot_area_no_fill = true
2017
+ elsif @inside_chart_space_sp_pr
2018
+ @chart_no_fill = true
2019
+ end
2020
+ when "srgbClr"
2021
+ assign_chart_color(attributes["val"]) if attributes["val"]
2022
+ when "schemeClr"
2023
+ assign_chart_color({ scheme: attributes["val"] }) if attributes["val"]
2024
+ when "cat", "xVal"
2025
+ @inside_cat = true if @inside_ser
2026
+ when "val", "yVal"
2027
+ if @inside_err_bars && @current_err_bars && attributes["val"]
2028
+ @current_err_bars[:val] = attributes["val"].to_f
2029
+ elsif @inside_ser
2030
+ @inside_val = true
2031
+ end
2032
+ when "bubbleSize"
2033
+ @inside_bubble_size = true if @inside_ser
2034
+ when "f"
2035
+ @inside_f = true
2036
+ @text_buffer = +""
2037
+ when "numRef"
2038
+ @current_ser[:cat_ref_type] = :num if @inside_ser && @inside_cat && @current_ser
2039
+ when "numCache"
2040
+ @inside_num_cache = true
2041
+ @cache_values = []
2042
+ when "strCache"
2043
+ @inside_str_cache = true
2044
+ @cache_values = []
2045
+ when "pt"
2046
+ if (@inside_num_cache || @inside_str_cache) && attributes["idx"]
2047
+ @inside_cache_pt = true
2048
+ @current_cache_idx = attributes["idx"].to_i
2049
+ end
2050
+ when "v"
2051
+ if @inside_cache_pt
2052
+ @inside_cache_v = true
2053
+ @text_buffer = +""
2054
+ end
2055
+ when "title"
2056
+ @title_depth += 1
2057
+ if @inside_cat_ax || @inside_val_ax
2058
+ @inside_ax_title = true
2059
+ elsif @title_depth == 1
2060
+ @inside_title = true
2061
+ end
2062
+ when "t"
2063
+ @inside_t = true
2064
+ @text_buffer = +""
2065
+ when "rPr"
2066
+ if @inside_ax_title
2067
+ if @inside_cat_ax
2068
+ @cat_axis_title_font ||= {}
2069
+ @cat_axis_title_font[:bold] = true if attributes["b"] == "1"
2070
+ @cat_axis_title_font[:italic] = true if attributes["i"] == "1"
2071
+ @cat_axis_title_font[:size] = attributes["sz"].to_i if attributes["sz"]
2072
+ elsif @inside_val_ax
2073
+ @val_axis_title_font ||= {}
2074
+ @val_axis_title_font[:bold] = true if attributes["b"] == "1"
2075
+ @val_axis_title_font[:italic] = true if attributes["i"] == "1"
2076
+ @val_axis_title_font[:size] = attributes["sz"].to_i if attributes["sz"]
2077
+ end
2078
+ @inside_ax_title_rpr = true
2079
+ elsif @inside_title && @title_depth == 1
2080
+ @inside_title_rpr = true
2081
+ @title_font ||= {}
2082
+ @title_font[:bold] = true if attributes["b"] == "1"
2083
+ @title_font[:italic] = true if attributes["i"] == "1"
2084
+ @title_font[:size] = attributes["sz"].to_i if attributes["sz"]
2085
+ end
2086
+ when "latin"
2087
+ if @inside_axis_def_rpr && attributes["typeface"]
2088
+ if @inside_disp_units_lbl
2089
+ (@disp_units_lbl_font ||= {})[:name] = attributes["typeface"]
2090
+ elsif @inside_cat_ax
2091
+ (@cat_axis_font ||= {})[:name] = attributes["typeface"]
2092
+ elsif @inside_val_ax
2093
+ (@val_axis_font ||= {})[:name] = attributes["typeface"]
2094
+ elsif @inside_legend_entry && @current_legend_entry
2095
+ (@current_legend_entry[:font] ||= {})[:name] = attributes["typeface"]
2096
+ elsif @inside_legend
2097
+ (@legend_font ||= {})[:name] = attributes["typeface"]
2098
+ elsif @inside_d_table
2099
+ (@d_table_font ||= {})[:name] = attributes["typeface"]
2100
+ elsif @inside_dlbl && @current_dlbl
2101
+ (@current_dlbl[:font] ||= {})[:name] = attributes["typeface"]
2102
+ elsif @inside_dlbls
2103
+ (@dlbls_font ||= {})[:name] = attributes["typeface"]
2104
+ elsif @inside_trendline_lbl && @current_trendline
2105
+ (@trendline_lbl_font ||= {})[:name] = attributes["typeface"]
2106
+ elsif @inside_chart_space_tx_pr
2107
+ (@chart_font ||= {})[:name] = attributes["typeface"]
2108
+ end
2109
+ elsif @inside_title_rpr && @title_font && attributes["typeface"]
2110
+ @title_font[:name] = attributes["typeface"]
2111
+ elsif @inside_ax_title_rpr && attributes["typeface"]
2112
+ if @inside_cat_ax
2113
+ (@cat_axis_title_font ||= {})[:name] = attributes["typeface"]
2114
+ elsif @inside_val_ax
2115
+ (@val_axis_title_font ||= {})[:name] = attributes["typeface"]
2116
+ end
2117
+ end
2118
+ when "legend"
2119
+ @inside_legend = true
2120
+ when "legendPos"
2121
+ @legend[:position] = attributes["val"] if @inside_legend && attributes["val"]
2122
+ when "manualLayout"
2123
+ @inside_legend_layout = true if @inside_legend
2124
+ @inside_plot_area_layout = true if @inside_plot_area && !@inside_legend && !@inside_title
2125
+ @inside_dlbl_layout = true if @inside_dlbl
2126
+ @inside_title_layout = true if (@inside_title || @inside_ax_title) && !@inside_legend && !@inside_dlbl
2127
+ @inside_trendline_lbl_layout = true if @inside_trendline_lbl
2128
+ when "layoutTarget"
2129
+ (@legend[:layout] ||= {})[:target] = attributes["val"] if @inside_legend_layout && attributes["val"]
2130
+ (@plot_area_layout ||= {})[:target] = attributes["val"] if @inside_plot_area_layout && attributes["val"]
2131
+ when "xMode"
2132
+ (@legend[:layout] ||= {})[:x_mode] = attributes["val"] if @inside_legend_layout && attributes["val"]
2133
+ (@plot_area_layout ||= {})[:x_mode] = attributes["val"] if @inside_plot_area_layout && attributes["val"]
2134
+ when "yMode"
2135
+ (@legend[:layout] ||= {})[:y_mode] = attributes["val"] if @inside_legend_layout && attributes["val"]
2136
+ (@plot_area_layout ||= {})[:y_mode] = attributes["val"] if @inside_plot_area_layout && attributes["val"]
2137
+ when "wMode"
2138
+ (@legend[:layout] ||= {})[:w_mode] = attributes["val"] if @inside_legend_layout && attributes["val"]
2139
+ (@plot_area_layout ||= {})[:w_mode] = attributes["val"] if @inside_plot_area_layout && attributes["val"]
2140
+ when "hMode"
2141
+ (@legend[:layout] ||= {})[:h_mode] = attributes["val"] if @inside_legend_layout && attributes["val"]
2142
+ (@plot_area_layout ||= {})[:h_mode] = attributes["val"] if @inside_plot_area_layout && attributes["val"]
2143
+ when "x"
2144
+ (@legend[:layout] ||= {})[:x] = attributes["val"].to_f if @inside_legend_layout && attributes["val"]
2145
+ (@plot_area_layout ||= {})[:x] = attributes["val"].to_f if @inside_plot_area_layout && attributes["val"]
2146
+ (@current_dlbl[:layout] ||= {})[:x] = attributes["val"].to_f if @inside_dlbl_layout && @current_dlbl && attributes["val"]
2147
+ assign_title_layout_value(:x, attributes["val"].to_f) if @inside_title_layout && attributes["val"]
2148
+ ((@current_trendline[:label] ||= {})[:layout] ||= {})[:x] = attributes["val"].to_f if @inside_trendline_lbl_layout && @current_trendline && attributes["val"]
2149
+ when "y"
2150
+ (@legend[:layout] ||= {})[:y] = attributes["val"].to_f if @inside_legend_layout && attributes["val"]
2151
+ (@plot_area_layout ||= {})[:y] = attributes["val"].to_f if @inside_plot_area_layout && attributes["val"]
2152
+ (@current_dlbl[:layout] ||= {})[:y] = attributes["val"].to_f if @inside_dlbl_layout && @current_dlbl && attributes["val"]
2153
+ assign_title_layout_value(:y, attributes["val"].to_f) if @inside_title_layout && attributes["val"]
2154
+ ((@current_trendline[:label] ||= {})[:layout] ||= {})[:y] = attributes["val"].to_f if @inside_trendline_lbl_layout && @current_trendline && attributes["val"]
2155
+ when "w"
2156
+ (@legend[:layout] ||= {})[:w] = attributes["val"].to_f if @inside_legend_layout && attributes["val"]
2157
+ (@plot_area_layout ||= {})[:w] = attributes["val"].to_f if @inside_plot_area_layout && attributes["val"]
2158
+ assign_title_layout_value(:w, attributes["val"].to_f) if @inside_title_layout && attributes["val"]
2159
+ when "h"
2160
+ (@legend[:layout] ||= {})[:h] = attributes["val"].to_f if @inside_legend_layout && attributes["val"]
2161
+ (@plot_area_layout ||= {})[:h] = attributes["val"].to_f if @inside_plot_area_layout && attributes["val"]
2162
+ assign_title_layout_value(:h, attributes["val"].to_f) if @inside_title_layout && attributes["val"]
2163
+ when "legendEntry"
2164
+ if @inside_legend
2165
+ @inside_legend_entry = true
2166
+ @current_legend_entry = {}
2167
+ end
2168
+ when "idx"
2169
+ if @inside_band_fmt && @current_band_fmt && attributes["val"]
2170
+ @current_band_fmt[:idx] = attributes["val"].to_i
2171
+ elsif @inside_dlbl && @current_dlbl && attributes["val"]
2172
+ @current_dlbl[:idx] = attributes["val"].to_i
2173
+ elsif @inside_dpt && @current_dpt && attributes["val"]
2174
+ @current_dpt[:idx] = attributes["val"].to_i
2175
+ elsif @inside_legend_entry && @current_legend_entry && attributes["val"]
2176
+ @current_legend_entry[:idx] = attributes["val"].to_i
2177
+ end
2178
+ when "delete"
2179
+ if @inside_dlbl && @current_dlbl && attributes["val"]
2180
+ @current_dlbl[:delete] = attributes["val"] == "1"
2181
+ elsif @inside_legend_entry && @current_legend_entry && attributes["val"]
2182
+ @current_legend_entry[:delete] = attributes["val"] == "1"
2183
+ elsif attributes["val"]
2184
+ if @inside_cat_ax
2185
+ @cat_axis_delete = attributes["val"] == "1"
2186
+ elsif @inside_val_ax
2187
+ @val_axis_delete = attributes["val"] == "1"
2188
+ end
2189
+ end
2190
+ when "overlay"
2191
+ if @inside_legend && attributes["val"]
2192
+ @legend[:overlay] = attributes["val"] == "1"
2193
+ elsif @inside_title && @title_depth == 1 && !@inside_ax_title && attributes["val"]
2194
+ @title_overlay = attributes["val"] == "1"
2195
+ end
2196
+ when "dLbls"
2197
+ if @inside_ser || @chart_type
2198
+ @inside_dlbls = true
2199
+ @dlbl_target = if @inside_ser && @current_ser
2200
+ (@current_ser[:data_labels] ||= {})
2201
+ else
2202
+ @data_labels
2203
+ end
2204
+ end
2205
+ when "dLbl"
2206
+ if @inside_dlbls
2207
+ @inside_dlbl = true
2208
+ @current_dlbl = {}
2209
+ end
2210
+ when "tx"
2211
+ @inside_dlbl_tx = true if @inside_dlbl
2212
+ @inside_trendline_lbl_tx = true if @inside_trendline_lbl
2213
+ when "showVal"
2214
+ if @inside_dlbl && @current_dlbl
2215
+ @current_dlbl[:show_val] = attributes["val"] == "1"
2216
+ elsif @inside_dlbls
2217
+ @dlbl_target[:show_val] = attributes["val"] == "1"
2218
+ @data_labels[:show_val] = attributes["val"] == "1" if @dlbl_target != @data_labels
2219
+ end
2220
+ when "showCatName"
2221
+ if @inside_dlbl && @current_dlbl
2222
+ @current_dlbl[:show_cat_name] = attributes["val"] == "1"
2223
+ elsif @inside_dlbls
2224
+ @dlbl_target[:show_cat_name] = attributes["val"] == "1"
2225
+ @data_labels[:show_cat_name] = attributes["val"] == "1" if @dlbl_target != @data_labels
2226
+ end
2227
+ when "showSerName"
2228
+ if @inside_dlbl && @current_dlbl
2229
+ @current_dlbl[:show_ser_name] = attributes["val"] == "1"
2230
+ elsif @inside_dlbls
2231
+ @dlbl_target[:show_ser_name] = attributes["val"] == "1"
2232
+ @data_labels[:show_ser_name] = attributes["val"] == "1" if @dlbl_target != @data_labels
2233
+ end
2234
+ when "showPercent"
2235
+ if @inside_dlbl && @current_dlbl
2236
+ @current_dlbl[:show_percent] = attributes["val"] == "1"
2237
+ elsif @inside_dlbls
2238
+ @dlbl_target[:show_percent] = attributes["val"] == "1"
2239
+ @data_labels[:show_percent] = attributes["val"] == "1" if @dlbl_target != @data_labels
2240
+ end
2241
+ when "showLegendKey"
2242
+ if @inside_dlbl && @current_dlbl
2243
+ @current_dlbl[:show_legend_key] = attributes["val"] == "1"
2244
+ elsif @inside_dlbls
2245
+ @dlbl_target[:show_legend_key] = attributes["val"] == "1"
2246
+ @data_labels[:show_legend_key] = attributes["val"] == "1" if @dlbl_target != @data_labels
2247
+ end
2248
+ when "dLblPos"
2249
+ if @inside_dlbl && @current_dlbl && attributes["val"]
2250
+ @current_dlbl[:position] = attributes["val"]
2251
+ elsif @inside_dlbls && attributes["val"]
2252
+ @dlbl_target[:position] = attributes["val"]
2253
+ @data_labels[:position] = attributes["val"] if @dlbl_target != @data_labels
2254
+ end
2255
+ when "showBubbleSize"
2256
+ if @inside_dlbl && @current_dlbl
2257
+ @current_dlbl[:show_bubble_size] = attributes["val"] == "1"
2258
+ elsif @inside_dlbls
2259
+ @dlbl_target[:show_bubble_size] = attributes["val"] == "1"
2260
+ @data_labels[:show_bubble_size] = attributes["val"] == "1" if @dlbl_target != @data_labels
2261
+ end
2262
+ when "separator"
2263
+ if @inside_dlbls
2264
+ @inside_separator = true
2265
+ @text_buffer = +""
2266
+ end
2267
+ when "showLeaderLines"
2268
+ if @inside_dlbls && attributes["val"]
2269
+ @dlbl_target[:show_leader_lines] = attributes["val"] == "1"
2270
+ @data_labels[:show_leader_lines] = attributes["val"] == "1" if @dlbl_target != @data_labels
2271
+ end
2272
+ when "leaderLines"
2273
+ @inside_leader_lines = true if @inside_dlbls
2274
+ when "catAx"
2275
+ @inside_cat_ax = true
2276
+ when "dateAx"
2277
+ @inside_cat_ax = true
2278
+ @cat_axis_type = :date
2279
+ when "valAx"
2280
+ @inside_val_ax = true
2281
+ when "scaling"
2282
+ @inside_scaling = true if @inside_cat_ax || @inside_val_ax
2283
+ when "logBase"
2284
+ if @inside_scaling && attributes["val"]
2285
+ if @inside_cat_ax
2286
+ @cat_axis_log_base = attributes["val"].to_f
2287
+ elsif @inside_val_ax
2288
+ @val_axis_log_base = attributes["val"].to_f
2289
+ end
2290
+ end
2291
+ when "max"
2292
+ if @inside_scaling && attributes["val"]
2293
+ if @inside_cat_ax
2294
+ @cat_axis_scaling_max = attributes["val"].to_f
2295
+ elsif @inside_val_ax
2296
+ @val_axis_scaling_max = attributes["val"].to_f
2297
+ end
2298
+ end
2299
+ when "min"
2300
+ if @inside_scaling && attributes["val"]
2301
+ if @inside_cat_ax
2302
+ @cat_axis_scaling_min = attributes["val"].to_f
2303
+ elsif @inside_val_ax
2304
+ @val_axis_scaling_min = attributes["val"].to_f
2305
+ end
2306
+ end
2307
+ when "orientation"
2308
+ if attributes["val"]
2309
+ if @inside_cat_ax
2310
+ @cat_axis_orientation = attributes["val"]
2311
+ elsif @inside_val_ax
2312
+ @val_axis_orientation = attributes["val"]
2313
+ end
2314
+ end
2315
+ when "numFmt"
2316
+ if @inside_trendline_lbl && @current_trendline && attributes["formatCode"]
2317
+ nf = { format_code: attributes["formatCode"] }
2318
+ nf[:source_linked] = attributes["sourceLinked"] == "1" if attributes["sourceLinked"]
2319
+ (@current_trendline[:label] ||= {})[:num_fmt] = nf
2320
+ elsif @inside_disp_units_lbl && attributes["formatCode"]
2321
+ nf = { format_code: attributes["formatCode"] }
2322
+ nf[:source_linked] = attributes["sourceLinked"] == "1" if attributes["sourceLinked"]
2323
+ if @val_axis_disp_units.is_a?(String)
2324
+ @val_axis_disp_units = { built_in_unit: @val_axis_disp_units }
2325
+ elsif @val_axis_disp_units.nil?
2326
+ @val_axis_disp_units = {}
2327
+ end
2328
+ (@val_axis_disp_units[:label] ||= {})[:num_fmt] = nf
2329
+ elsif (@inside_cat_ax || @inside_val_ax) && attributes["formatCode"]
2330
+ nf = { format_code: attributes["formatCode"] }
2331
+ nf[:source_linked] = attributes["sourceLinked"] == "1" if attributes["sourceLinked"]
2332
+ if @inside_cat_ax
2333
+ @cat_axis_num_fmt = nf
2334
+ elsif @inside_val_ax
2335
+ @val_axis_num_fmt = nf
2336
+ end
2337
+ elsif @inside_dlbl && @current_dlbl && attributes["formatCode"]
2338
+ nf = { format_code: attributes["formatCode"] }
2339
+ nf[:source_linked] = attributes["sourceLinked"] == "1" if attributes["sourceLinked"]
2340
+ @current_dlbl[:num_fmt] = nf
2341
+ elsif @inside_dlbls && !@inside_dlbl && attributes["formatCode"]
2342
+ nf = { format_code: attributes["formatCode"] }
2343
+ nf[:source_linked] = attributes["sourceLinked"] == "1" if attributes["sourceLinked"]
2344
+ @dlbl_target[:num_fmt] = nf
2345
+ @data_labels[:num_fmt] = nf if @dlbl_target != @data_labels
2346
+ end
2347
+ when "majorTickMark"
2348
+ if attributes["val"]
2349
+ if @inside_cat_ax
2350
+ @cat_axis_major_tick_mark = attributes["val"]
2351
+ elsif @inside_val_ax
2352
+ @val_axis_major_tick_mark = attributes["val"]
2353
+ end
2354
+ end
2355
+ when "minorTickMark"
2356
+ if attributes["val"]
2357
+ if @inside_cat_ax
2358
+ @cat_axis_minor_tick_mark = attributes["val"]
2359
+ elsif @inside_val_ax
2360
+ @val_axis_minor_tick_mark = attributes["val"]
2361
+ end
2362
+ end
2363
+ when "crosses"
2364
+ if attributes["val"]
2365
+ if @inside_cat_ax
2366
+ @cat_axis_crosses = attributes["val"]
2367
+ elsif @inside_val_ax
2368
+ @val_axis_crosses = attributes["val"]
2369
+ end
2370
+ end
2371
+ when "crossesAt"
2372
+ if attributes["val"]
2373
+ if @inside_cat_ax
2374
+ @cat_axis_crosses_at = attributes["val"]&.to_f
2375
+ elsif @inside_val_ax
2376
+ @val_axis_crosses_at = attributes["val"]&.to_f
2377
+ end
2378
+ end
2379
+ when "tickLblSkip"
2380
+ @cat_axis_tick_lbl_skip = attributes["val"]&.to_i if attributes["val"] && @inside_cat_ax
2381
+ when "tickMarkSkip"
2382
+ @cat_axis_tick_mark_skip = attributes["val"]&.to_i if attributes["val"] && @inside_cat_ax
2383
+ when "auto"
2384
+ @cat_axis_auto = attributes["val"] == "1" if attributes["val"] && @inside_cat_ax
2385
+ when "lblAlgn"
2386
+ @cat_axis_lbl_algn = attributes["val"] if attributes["val"] && @inside_cat_ax
2387
+ when "lblOffset"
2388
+ @cat_axis_lbl_offset = attributes["val"]&.to_i if attributes["val"] && @inside_cat_ax
2389
+ when "noMultiLvlLbl"
2390
+ @cat_axis_no_multi_lvl_lbl = attributes["val"] == "1" if attributes["val"] && @inside_cat_ax
2391
+ when "axPos"
2392
+ if attributes["val"]
2393
+ if @inside_cat_ax
2394
+ @cat_axis_pos = attributes["val"]
2395
+ elsif @inside_val_ax
2396
+ @val_axis_pos = attributes["val"]
2397
+ end
2398
+ end
2399
+ when "crossBetween"
2400
+ @val_axis_cross_between = attributes["val"] if @inside_val_ax && attributes["val"]
2401
+ when "majorUnit"
2402
+ if @inside_cat_ax && @cat_axis_type == :date && attributes["val"]
2403
+ @cat_axis_major_unit = attributes["val"].to_f
2404
+ elsif @inside_val_ax && attributes["val"]
2405
+ @val_axis_major_unit = attributes["val"].to_f
2406
+ end
2407
+ when "minorUnit"
2408
+ if @inside_cat_ax && @cat_axis_type == :date && attributes["val"]
2409
+ @cat_axis_minor_unit = attributes["val"].to_f
2410
+ elsif @inside_val_ax && attributes["val"]
2411
+ @val_axis_minor_unit = attributes["val"].to_f
2412
+ end
2413
+ when "baseTimeUnit"
2414
+ @cat_axis_base_time_unit = attributes["val"] if @inside_cat_ax && attributes["val"]
2415
+ when "majorTimeUnit"
2416
+ @cat_axis_major_time_unit = attributes["val"] if @inside_cat_ax && attributes["val"]
2417
+ when "minorTimeUnit"
2418
+ @cat_axis_minor_time_unit = attributes["val"] if @inside_cat_ax && attributes["val"]
2419
+ when "builtInUnit"
2420
+ @val_axis_disp_units = attributes["val"] if @inside_val_ax && @inside_disp_units && attributes["val"]
2421
+ when "custUnit"
2422
+ @val_axis_disp_units = { cust_unit: attributes["val"].to_f } if @inside_val_ax && @inside_disp_units && attributes["val"]
2423
+ when "dispUnits"
2424
+ @inside_disp_units = true if @inside_val_ax
2425
+ when "dispUnitsLbl"
2426
+ @inside_disp_units_lbl = true if @inside_disp_units
2427
+ when "txPr"
2428
+ if @inside_cat_ax || @inside_val_ax || @inside_legend || @inside_legend_entry || @inside_d_table || @inside_dlbls || @inside_trendline_lbl || @inside_disp_units_lbl
2429
+ @inside_axis_tx_pr = true
2430
+ elsif !@inside_chart
2431
+ @inside_chart_space_tx_pr = true
2432
+ @inside_axis_tx_pr = true
2433
+ end
2434
+ when "bodyPr"
2435
+ if attributes["rot"]
2436
+ if @inside_axis_tx_pr
2437
+ if @inside_cat_ax
2438
+ @cat_axis_label_rotation = attributes["rot"].to_i
2439
+ elsif @inside_val_ax
2440
+ @val_axis_label_rotation = attributes["rot"].to_i
2441
+ end
2442
+ elsif @inside_ax_title
2443
+ if @inside_cat_ax
2444
+ @cat_axis_title_rotation = attributes["rot"].to_i
2445
+ elsif @inside_val_ax
2446
+ @val_axis_title_rotation = attributes["rot"].to_i
2447
+ end
2448
+ elsif @inside_title && @title_depth == 1
2449
+ @title_rotation = attributes["rot"].to_i
2450
+ end
2451
+ end
2452
+ when "defRPr"
2453
+ if @inside_axis_tx_pr
2454
+ @inside_axis_def_rpr = true
2455
+ font = {}
2456
+ font[:size] = attributes["sz"].to_i / 100.0 if attributes["sz"]
2457
+ font[:bold] = true if attributes["b"] == "1"
2458
+ font[:italic] = true if attributes["i"] == "1"
2459
+ if @inside_disp_units_lbl
2460
+ @disp_units_lbl_font = (@disp_units_lbl_font || {}).merge(font)
2461
+ elsif @inside_cat_ax
2462
+ @cat_axis_font = (@cat_axis_font || {}).merge(font)
2463
+ elsif @inside_val_ax
2464
+ @val_axis_font = (@val_axis_font || {}).merge(font)
2465
+ elsif @inside_legend_entry && @current_legend_entry
2466
+ @current_legend_entry[:font] = (@current_legend_entry[:font] || {}).merge(font)
2467
+ elsif @inside_legend
2468
+ @legend_font = (@legend_font || {}).merge(font)
2469
+ elsif @inside_d_table
2470
+ @d_table_font = (@d_table_font || {}).merge(font)
2471
+ elsif @inside_dlbl && @current_dlbl
2472
+ @current_dlbl[:font] = (@current_dlbl[:font] || {}).merge(font)
2473
+ elsif @inside_dlbls
2474
+ @dlbls_font = (@dlbls_font || {}).merge(font)
2475
+ elsif @inside_trendline_lbl && @current_trendline
2476
+ @trendline_lbl_font = (@trendline_lbl_font || {}).merge(font)
2477
+ elsif @inside_chart_space_tx_pr
2478
+ @chart_font = (@chart_font || {}).merge(font)
2479
+ end
2480
+ end
2481
+ when "tickLblPos"
2482
+ if attributes["val"]
2483
+ if @inside_cat_ax
2484
+ @cat_axis_tick_lbl_pos = attributes["val"]
2485
+ elsif @inside_val_ax
2486
+ @val_axis_tick_lbl_pos = attributes["val"]
2487
+ end
2488
+ end
2489
+ when "majorGridlines"
2490
+ @inside_gridlines = true
2491
+ if @inside_cat_ax
2492
+ @cat_axis_major_gridlines = true
2493
+ @gridlines_target = :cat_axis_major_gridlines
2494
+ elsif @inside_val_ax
2495
+ @val_axis_major_gridlines = true
2496
+ @gridlines_target = :val_axis_major_gridlines
2497
+ end
2498
+ when "minorGridlines"
2499
+ @inside_gridlines = true
2500
+ if @inside_cat_ax
2501
+ @cat_axis_minor_gridlines = true
2502
+ @gridlines_target = :cat_axis_minor_gridlines
2503
+ elsif @inside_val_ax
2504
+ @val_axis_minor_gridlines = true
2505
+ @gridlines_target = :val_axis_minor_gridlines
2506
+ end
2507
+ when "plotVisOnly"
2508
+ @plot_vis_only = attributes["val"] == "1" if attributes["val"]
2509
+ when "dispBlanksAs"
2510
+ @disp_blanks_as = attributes["val"] if attributes["val"]
2511
+ when "style"
2512
+ @style = attributes["val"]&.to_i if attributes["val"]
2513
+ when "roundedCorners"
2514
+ @rounded_corners = attributes["val"] == "1" if attributes["val"]
2515
+ when "protection"
2516
+ unless @inside_chart
2517
+ @inside_protection = true
2518
+ @protection = {}
2519
+ end
2520
+ when "chartObject"
2521
+ @protection[:chart_object] = attributes["val"] == "1" if @inside_protection && attributes["val"]
2522
+ when "data"
2523
+ @protection[:data] = attributes["val"] == "1" if @inside_protection && attributes["val"]
2524
+ when "formatting"
2525
+ @protection[:formatting] = attributes["val"] == "1" if @inside_protection && attributes["val"]
2526
+ when "selection"
2527
+ @protection[:selection] = attributes["val"] == "1" if @inside_protection && attributes["val"]
2528
+ when "userInterface"
2529
+ @protection[:user_interface] = attributes["val"] == "1" if @inside_protection && attributes["val"]
2530
+ when "showDLblsOverMax"
2531
+ @show_d_lbls_over_max = attributes["val"] == "1" if attributes["val"]
2532
+ when "dTable"
2533
+ @inside_d_table = true
2534
+ @data_table = {}
2535
+ when "showHorzBorder"
2536
+ @data_table[:show_horz_border] = attributes["val"] == "1" if @inside_d_table && @data_table && attributes["val"]
2537
+ when "showVertBorder"
2538
+ @data_table[:show_vert_border] = attributes["val"] == "1" if @inside_d_table && @data_table && attributes["val"]
2539
+ when "showOutline"
2540
+ @data_table[:show_outline] = attributes["val"] == "1" if @inside_d_table && @data_table && attributes["val"]
2541
+ when "showKeys"
2542
+ @data_table[:show_keys] = attributes["val"] == "1" if @inside_d_table && @data_table && attributes["val"]
2543
+ when "printSettings"
2544
+ unless @inside_chart
2545
+ @inside_print_settings = true
2546
+ @print_settings = {}
2547
+ end
2548
+ when "headerFooter"
2549
+ if @inside_print_settings
2550
+ @inside_ps_header_footer = true
2551
+ @print_settings[:header_footer] = {}
2552
+ end
2553
+ when "oddHeader"
2554
+ @inside_ps_odd_header = true if @inside_ps_header_footer
2555
+ @text_buffer = +""
2556
+ when "oddFooter"
2557
+ @inside_ps_odd_footer = true if @inside_ps_header_footer
2558
+ @text_buffer = +""
2559
+ when "evenHeader"
2560
+ @inside_ps_even_header = true if @inside_ps_header_footer
2561
+ @text_buffer = +""
2562
+ when "evenFooter"
2563
+ @inside_ps_even_footer = true if @inside_ps_header_footer
2564
+ @text_buffer = +""
2565
+ when "firstHeader"
2566
+ @inside_ps_first_header = true if @inside_ps_header_footer
2567
+ @text_buffer = +""
2568
+ when "firstFooter"
2569
+ @inside_ps_first_footer = true if @inside_ps_header_footer
2570
+ @text_buffer = +""
2571
+ when "pageMargins"
2572
+ if @inside_print_settings
2573
+ pm = {}
2574
+ %w[b l r t header footer].each do |a|
2575
+ pm[a.to_sym] = attributes[a].to_f if attributes[a]
2576
+ end
2577
+ @print_settings[:page_margins] = pm unless pm.empty?
2578
+ end
2579
+ when "pageSetup"
2580
+ if @inside_print_settings
2581
+ psu = {}
2582
+ psu[:paper_size] = attributes["paperSize"].to_i if attributes["paperSize"]
2583
+ psu[:first_page_number] = attributes["firstPageNumber"].to_i if attributes["firstPageNumber"]
2584
+ psu[:orientation] = attributes["orientation"] if attributes["orientation"]
2585
+ psu[:horizontal_dpi] = attributes["horizontalDpi"].to_i if attributes["horizontalDpi"]
2586
+ psu[:vertical_dpi] = attributes["verticalDpi"].to_i if attributes["verticalDpi"]
2587
+ psu[:copies] = attributes["copies"].to_i if attributes["copies"]
2588
+ @print_settings[:page_setup] = psu unless psu.empty?
2589
+ end
2590
+ end
2591
+ end
2592
+
2593
+ def characters(text)
2594
+ @text_buffer << text if @inside_t || @inside_f || @inside_separator || @inside_trendline_name || @inside_cache_v ||
2595
+ @inside_ps_odd_header || @inside_ps_odd_footer ||
2596
+ @inside_ps_even_header || @inside_ps_even_footer ||
2597
+ @inside_ps_first_header || @inside_ps_first_footer
2598
+ end
2599
+
2600
+ def end_element(_uri, local_name, qname)
2601
+ name = element_name(local_name, qname)
2602
+ case name
2603
+ when "t"
2604
+ if @inside_dlbl_tx && @current_dlbl
2605
+ @current_dlbl[:text] = (@current_dlbl[:text] || +"") << @text_buffer
2606
+ elsif @inside_trendline_lbl_tx && @current_trendline
2607
+ (@current_trendline[:label] ||= {})[:text] = (@current_trendline.dig(:label, :text) || +"") << @text_buffer
2608
+ elsif @inside_ax_title
2609
+ if @inside_cat_ax
2610
+ @cat_axis_title = @text_buffer.dup
2611
+ elsif @inside_val_ax
2612
+ @val_axis_title = @text_buffer.dup
2613
+ end
2614
+ elsif @inside_title && @title_depth == 1
2615
+ @title = @text_buffer.dup
2616
+ end
2617
+ @inside_t = false
2618
+ when "f"
2619
+ if @inside_err_bars && @current_err_bars
2620
+ if @inside_err_bars_plus
2621
+ @current_err_bars[:plus] = @text_buffer.dup
2622
+ elsif @inside_err_bars_minus
2623
+ @current_err_bars[:minus] = @text_buffer.dup
2624
+ end
2625
+ elsif @inside_ser
2626
+ if @inside_cat
2627
+ @current_ser[:cat_ref] = @text_buffer.dup
2628
+ elsif @inside_val
2629
+ @current_ser[:val_ref] = @text_buffer.dup
2630
+ elsif @inside_bubble_size
2631
+ @current_ser[:bubble_size_ref] = @text_buffer.dup
2632
+ else
2633
+ @current_ser[:name] = @text_buffer.dup
2634
+ end
2635
+ end
2636
+ @inside_f = false
2637
+ when "v"
2638
+ if @inside_cache_v
2639
+ @cache_values[@current_cache_idx] = @text_buffer.dup
2640
+ @inside_cache_v = false
2641
+ end
2642
+ when "pt"
2643
+ @inside_cache_pt = false
2644
+ when "numCache"
2645
+ if @inside_num_cache && @inside_ser && @current_ser
2646
+ if @inside_cat
2647
+ @current_ser[:cat_cache] = @cache_values.dup
2648
+ elsif @inside_val
2649
+ @current_ser[:val_cache] = @cache_values.dup
2650
+ elsif @inside_bubble_size
2651
+ @current_ser[:bubble_size_cache] = @cache_values.dup
2652
+ end
2653
+ end
2654
+ @inside_num_cache = false
2655
+ @cache_values = []
2656
+ when "strCache"
2657
+ if @inside_str_cache && @inside_ser && @current_ser
2658
+ if @inside_cat
2659
+ @current_ser[:cat_cache] = @cache_values.dup
2660
+ else
2661
+ @current_ser[:name_cache] = @cache_values.dup
2662
+ end
2663
+ end
2664
+ @inside_str_cache = false
2665
+ @cache_values = []
2666
+ when "cat", "xVal"
2667
+ @inside_cat = false
2668
+ when "val", "yVal"
2669
+ @inside_val = false
2670
+ when "bubbleSize"
2671
+ @inside_bubble_size = false
2672
+ when "dPt"
2673
+ if @inside_dpt && @current_dpt && @current_ser
2674
+ @current_ser[:data_points] ||= []
2675
+ @current_ser[:data_points] << @current_dpt
2676
+ end
2677
+ @current_dpt = nil
2678
+ @inside_dpt = false
2679
+ @inside_dpt_marker = false
2680
+ @inside_dpt_marker_sp_pr = false
2681
+ @inside_dpt_marker_solid_fill = false
2682
+ @inside_dpt_marker_ln = false
2683
+ @inside_dpt_sp_pr = false
2684
+ @inside_dpt_solid_fill = false
2685
+ @inside_dpt_ln = false
2686
+ when "trendline"
2687
+ if @inside_trendline && @current_trendline && @current_ser
2688
+ (@current_ser[:trendlines] ||= []) << @current_trendline
2689
+ @current_ser[:trendline] = @current_ser[:trendlines].first
2690
+ end
2691
+ @current_trendline = nil
2692
+ @inside_trendline = false
2693
+ @inside_trendline_name = false
2694
+ @inside_trendline_sp_pr = false
2695
+ @inside_trendline_ln = false
2696
+ @inside_trendline_solid_fill = false
2697
+ @inside_trendline_lbl = false
2698
+ @inside_trendline_lbl_layout = false
2699
+ @inside_trendline_lbl_tx = false
2700
+ @inside_trendline_lbl_sp_pr = false
2701
+ @inside_trendline_lbl_ln = false
2702
+ @inside_trendline_lbl_solid_fill = false
2703
+ @trendline_lbl_font = nil
2704
+ when "trendlineLbl"
2705
+ (@current_trendline[:label] ||= {})[:font] = @trendline_lbl_font if @trendline_lbl_font && @current_trendline
2706
+ @inside_trendline_lbl = false
2707
+ @inside_trendline_lbl_layout = false
2708
+ @inside_trendline_lbl_tx = false
2709
+ @inside_trendline_lbl_sp_pr = false
2710
+ @inside_trendline_lbl_ln = false
2711
+ @inside_trendline_lbl_solid_fill = false
2712
+ @trendline_lbl_font = nil
2713
+ when "errBars"
2714
+ if @inside_err_bars && @current_err_bars && @current_ser
2715
+ (@current_ser[:error_bars_list] ||= []) << @current_err_bars
2716
+ @current_ser[:error_bars] = @current_ser[:error_bars_list].first
2717
+ end
2718
+ @current_err_bars = nil
2719
+ @inside_err_bars = false
2720
+ @inside_err_bars_sp_pr = false
2721
+ @inside_err_bars_ln = false
2722
+ @inside_err_bars_solid_fill = false
2723
+ @inside_err_bars_plus = false
2724
+ @inside_err_bars_minus = false
2725
+ when "plus"
2726
+ @inside_err_bars_plus = false if @inside_err_bars
2727
+ when "minus"
2728
+ @inside_err_bars_minus = false if @inside_err_bars
2729
+ when "name"
2730
+ @current_trendline[:name] = @text_buffer.dup if @inside_trendline_name && @current_trendline
2731
+ @inside_trendline_name = false
2732
+ when "ser"
2733
+ @series << @current_ser if @current_ser
2734
+ @current_ser = nil
2735
+ @inside_ser = false
2736
+ @inside_ser_sp_pr = false
2737
+ @inside_ser_solid_fill = false
2738
+ @inside_ser_ln = false
2739
+ @inside_ser_marker = false
2740
+ @inside_marker_sp_pr = false
2741
+ @inside_marker_ln = false
2742
+ @inside_marker_solid_fill = false
2743
+ when "spPr"
2744
+ if @inside_band_fmt_sp_pr
2745
+ @inside_band_fmt_sp_pr = false
2746
+ @inside_band_fmt_ln = false
2747
+ @inside_band_fmt_solid_fill = false
2748
+ elsif @inside_dpt_marker_sp_pr
2749
+ @inside_dpt_marker_sp_pr = false
2750
+ @inside_dpt_marker_ln = false
2751
+ @inside_dpt_marker_solid_fill = false
2752
+ elsif @inside_dpt
2753
+ @inside_dpt_sp_pr = false
2754
+ @inside_dpt_solid_fill = false
2755
+ elsif @inside_marker_sp_pr
2756
+ @inside_marker_sp_pr = false
2757
+ @inside_marker_ln = false
2758
+ @inside_marker_solid_fill = false
2759
+ elsif @inside_leader_lines_sp_pr
2760
+ @inside_leader_lines_sp_pr = false
2761
+ @inside_leader_lines_ln = false
2762
+ @inside_leader_lines_solid_fill = false
2763
+ elsif @inside_dlbl_sp_pr
2764
+ @inside_dlbl_sp_pr = false
2765
+ @inside_dlbl_ln = false
2766
+ @inside_dlbl_solid_fill = false
2767
+ elsif @inside_dlbls_sp_pr
2768
+ @inside_dlbls_sp_pr = false
2769
+ @inside_dlbls_ln = false
2770
+ @inside_dlbls_solid_fill = false
2771
+ elsif @inside_trendline_lbl_sp_pr
2772
+ @inside_trendline_lbl_sp_pr = false
2773
+ @inside_trendline_lbl_ln = false
2774
+ @inside_trendline_lbl_solid_fill = false
2775
+ elsif @inside_trendline_sp_pr
2776
+ @inside_trendline_sp_pr = false
2777
+ @inside_trendline_ln = false
2778
+ @inside_trendline_solid_fill = false
2779
+ elsif @inside_err_bars_sp_pr
2780
+ @inside_err_bars_sp_pr = false
2781
+ @inside_err_bars_ln = false
2782
+ @inside_err_bars_solid_fill = false
2783
+ elsif @inside_drop_lines_sp_pr
2784
+ @inside_drop_lines_sp_pr = false
2785
+ @inside_drop_lines_ln = false
2786
+ @inside_drop_lines_solid_fill = false
2787
+ elsif @inside_hi_low_lines_sp_pr
2788
+ @inside_hi_low_lines_sp_pr = false
2789
+ @inside_hi_low_lines_ln = false
2790
+ @inside_hi_low_lines_solid_fill = false
2791
+ elsif @inside_ser_lines_sp_pr
2792
+ @inside_ser_lines_sp_pr = false
2793
+ @inside_ser_lines_ln = false
2794
+ @inside_ser_lines_solid_fill = false
2795
+ elsif @inside_up_down_bar_sp_pr
2796
+ @inside_up_down_bar_sp_pr = false
2797
+ @inside_up_down_bar_ln = false
2798
+ @inside_up_down_bar_solid_fill = false
2799
+ elsif @inside_ax_title_sp_pr
2800
+ @inside_ax_title_sp_pr = false
2801
+ @inside_ax_title_ln = false
2802
+ @inside_ax_title_solid_fill = false
2803
+ elsif @inside_ser
2804
+ @inside_ser_sp_pr = false
2805
+ @inside_ser_ln = false
2806
+ elsif @inside_gridlines
2807
+ @inside_gridlines_sp_pr = false
2808
+ @inside_gridlines_ln = false
2809
+ @inside_gridlines_solid_fill = false
2810
+ elsif @inside_disp_units_lbl_sp_pr
2811
+ @inside_disp_units_lbl_sp_pr = false
2812
+ @inside_disp_units_lbl_ln = false
2813
+ @inside_disp_units_lbl_solid_fill = false
2814
+ elsif @inside_cat_ax || @inside_val_ax
2815
+ @inside_ax_sp_pr = false
2816
+ @inside_ax_ln = false
2817
+ @inside_ax_solid_fill = false
2818
+ elsif @inside_wall
2819
+ @inside_wall_sp_pr = false
2820
+ @inside_wall_ln = false
2821
+ @inside_wall_solid_fill = false
2822
+ elsif @inside_legend
2823
+ @inside_legend_sp_pr = false
2824
+ @inside_legend_ln = false
2825
+ @inside_legend_solid_fill = false
2826
+ elsif @inside_d_table
2827
+ @inside_d_table_sp_pr = false
2828
+ @inside_d_table_ln = false
2829
+ @inside_d_table_solid_fill = false
2830
+ elsif @inside_title && @title_depth == 1
2831
+ @inside_title_sp_pr = false
2832
+ @inside_title_ln = false
2833
+ @inside_title_solid_fill = false
2834
+ elsif @inside_plot_area
2835
+ @inside_plot_area_sp_pr = false
2836
+ @inside_plot_area_solid_fill = false
2837
+ @inside_plot_area_ln = false
2838
+ elsif @inside_chart_space_sp_pr
2839
+ @inside_chart_space_sp_pr = false
2840
+ @inside_chart_space_ln = false
2841
+ @inside_chart_space_solid_fill = false
2842
+ end
2843
+ when "ln"
2844
+ @inside_band_fmt_ln = false if @inside_band_fmt_sp_pr
2845
+ @inside_dpt_marker_ln = false if @inside_dpt_marker_sp_pr
2846
+ @inside_dpt_ln = false if @inside_dpt
2847
+ @inside_marker_ln = false if @inside_marker_sp_pr
2848
+ @inside_leader_lines_ln = false if @inside_leader_lines_sp_pr
2849
+ @inside_dlbl_ln = false if @inside_dlbl_sp_pr
2850
+ @inside_dlbls_ln = false if @inside_dlbls_sp_pr
2851
+ @inside_trendline_lbl_ln = false if @inside_trendline_lbl_sp_pr
2852
+ @inside_trendline_ln = false if @inside_trendline_sp_pr
2853
+ @inside_err_bars_ln = false if @inside_err_bars_sp_pr
2854
+ @inside_ser_ln = false if @inside_ser
2855
+ @inside_drop_lines_ln = false if @inside_drop_lines_sp_pr
2856
+ @inside_hi_low_lines_ln = false if @inside_hi_low_lines_sp_pr
2857
+ @inside_ser_lines_ln = false if @inside_ser_lines_sp_pr
2858
+ @inside_up_down_bar_ln = false if @inside_up_down_bar_sp_pr
2859
+ @inside_ax_title_ln = false if @inside_ax_title_sp_pr
2860
+ @inside_disp_units_lbl_ln = false if @inside_disp_units_lbl_sp_pr
2861
+ @inside_gridlines_ln = false if @inside_gridlines_sp_pr
2862
+ @inside_ax_ln = false if @inside_ax_sp_pr
2863
+ @inside_wall_ln = false if @inside_wall_sp_pr
2864
+ @inside_legend_ln = false if @inside_legend_sp_pr
2865
+ @inside_d_table_ln = false if @inside_d_table_sp_pr
2866
+ @inside_title_ln = false if @inside_title_sp_pr
2867
+ @inside_plot_area_ln = false if @inside_plot_area_sp_pr
2868
+ @inside_chart_space_ln = false if @inside_chart_space_sp_pr
2869
+ when "marker"
2870
+ if @inside_dpt
2871
+ @inside_dpt_marker = false
2872
+ elsif @inside_ser
2873
+ @inside_ser_marker = false
2874
+ end
2875
+ when "rPr"
2876
+ @inside_title_rpr = false
2877
+ @inside_ax_title_rpr = false
2878
+ when "solidFill"
2879
+ if @inside_band_fmt_sp_pr
2880
+ @inside_band_fmt_solid_fill = false
2881
+ elsif @inside_dpt_marker_sp_pr
2882
+ @inside_dpt_marker_solid_fill = false
2883
+ elsif @inside_dpt
2884
+ @inside_dpt_solid_fill = false
2885
+ elsif @inside_marker_sp_pr
2886
+ @inside_marker_solid_fill = false
2887
+ elsif @inside_dlbl_sp_pr
2888
+ @inside_dlbl_solid_fill = false
2889
+ elsif @inside_trendline_lbl_sp_pr
2890
+ @inside_trendline_lbl_solid_fill = false
2891
+ elsif @inside_trendline_sp_pr
2892
+ @inside_trendline_solid_fill = false
2893
+ elsif @inside_err_bars_sp_pr
2894
+ @inside_err_bars_solid_fill = false
2895
+ elsif @inside_ser
2896
+ @inside_ser_solid_fill = false
2897
+ elsif @inside_drop_lines_sp_pr
2898
+ @inside_drop_lines_solid_fill = false
2899
+ elsif @inside_hi_low_lines_sp_pr
2900
+ @inside_hi_low_lines_solid_fill = false
2901
+ elsif @inside_ser_lines_sp_pr
2902
+ @inside_ser_lines_solid_fill = false
2903
+ elsif @inside_up_down_bar_sp_pr
2904
+ @inside_up_down_bar_solid_fill = false
2905
+ elsif @inside_ax_title_sp_pr
2906
+ @inside_ax_title_solid_fill = false
2907
+ elsif @inside_gridlines_sp_pr
2908
+ @inside_gridlines_solid_fill = false
2909
+ elsif @inside_disp_units_lbl_sp_pr
2910
+ @inside_disp_units_lbl_solid_fill = false
2911
+ elsif @inside_ax_sp_pr
2912
+ @inside_ax_solid_fill = false
2913
+ elsif @inside_wall_sp_pr
2914
+ @inside_wall_solid_fill = false
2915
+ elsif @inside_plot_area_sp_pr
2916
+ @inside_plot_area_solid_fill = false
2917
+ elsif @inside_chart_space_sp_pr
2918
+ @inside_chart_space_solid_fill = false
2919
+ end
2920
+ when "majorGridlines", "minorGridlines"
2921
+ @inside_gridlines = false
2922
+ @inside_gridlines_sp_pr = false
2923
+ @inside_gridlines_ln = false
2924
+ @inside_gridlines_solid_fill = false
2925
+ @gridlines_target = nil
2926
+ when "chart"
2927
+ @inside_chart = false
2928
+ when "plotArea"
2929
+ @inside_plot_area = false
2930
+ @inside_plot_area_layout = false
2931
+ when "title"
2932
+ @title_depth -= 1
2933
+ @inside_title = false if @title_depth.zero?
2934
+ @inside_title_layout = false
2935
+ @inside_ax_title = false
2936
+ @inside_ax_title_rpr = false
2937
+ @inside_ax_title_sp_pr = false
2938
+ @inside_ax_title_ln = false
2939
+ @inside_ax_title_solid_fill = false
2940
+ @inside_title_rpr = false
2941
+ when "legendEntry"
2942
+ if @inside_legend_entry && @current_legend_entry
2943
+ @legend[:entries] ||= []
2944
+ @legend[:entries] << @current_legend_entry
2945
+ end
2946
+ @current_legend_entry = nil
2947
+ @inside_legend_entry = false
2948
+ @inside_axis_tx_pr = false
2949
+ @inside_axis_def_rpr = false
2950
+ when "legend"
2951
+ @inside_legend = false
2952
+ @inside_legend_layout = false
2953
+ @inside_axis_tx_pr = false
2954
+ @inside_axis_def_rpr = false
2955
+ when "dLbls"
2956
+ if @dlbls_font
2957
+ dl_target = @dlbl_target || @data_labels
2958
+ dl_target[:font] = @dlbls_font
2959
+ @data_labels[:font] = @dlbls_font if dl_target != @data_labels
2960
+ end
2961
+ @inside_dlbls = false
2962
+ @dlbl_target = nil
2963
+ @inside_dlbls_sp_pr = false
2964
+ @inside_dlbls_ln = false
2965
+ @inside_dlbls_solid_fill = false
2966
+ @inside_leader_lines = false
2967
+ @inside_leader_lines_sp_pr = false
2968
+ @inside_leader_lines_ln = false
2969
+ @inside_leader_lines_solid_fill = false
2970
+ @dlbls_font = nil
2971
+ @inside_axis_tx_pr = false
2972
+ @inside_axis_def_rpr = false
2973
+ when "dLbl"
2974
+ if @inside_dlbl && @current_dlbl && @dlbl_target
2975
+ (@dlbl_target[:labels] ||= []) << @current_dlbl
2976
+ (@data_labels[:labels] ||= []) << @current_dlbl if @dlbl_target != @data_labels
2977
+ end
2978
+ @current_dlbl = nil
2979
+ @inside_dlbl = false
2980
+ @inside_dlbl_tx = false
2981
+ @inside_dlbl_sp_pr = false
2982
+ @inside_dlbl_solid_fill = false
2983
+ @inside_dlbl_ln = false
2984
+ @inside_dlbl_layout = false
2985
+ when "tx"
2986
+ @inside_dlbl_tx = false if @inside_dlbl
2987
+ when "separator"
2988
+ if @inside_separator
2989
+ if @inside_dlbl && @current_dlbl
2990
+ @current_dlbl[:separator] = @text_buffer.dup
2991
+ elsif @dlbl_target
2992
+ @dlbl_target[:separator] = @text_buffer.dup
2993
+ @data_labels[:separator] = @text_buffer.dup if @dlbl_target != @data_labels
2994
+ end
2995
+ @inside_separator = false
2996
+ end
2997
+ when "catAx", "dateAx"
2998
+ @inside_cat_ax = false
2999
+ @inside_axis_tx_pr = false
3000
+ @inside_axis_def_rpr = false
3001
+ when "valAx"
3002
+ @inside_val_ax = false
3003
+ @inside_axis_tx_pr = false
3004
+ @inside_axis_def_rpr = false
3005
+ @inside_disp_units = false
3006
+ @inside_disp_units_lbl = false
3007
+ @inside_disp_units_lbl_sp_pr = false
3008
+ @inside_disp_units_lbl_ln = false
3009
+ @inside_disp_units_lbl_solid_fill = false
3010
+ @disp_units_lbl_font = nil
3011
+ when "dispUnits"
3012
+ @inside_disp_units = false
3013
+ @inside_disp_units_lbl = false
3014
+ @inside_disp_units_lbl_sp_pr = false
3015
+ @inside_disp_units_lbl_ln = false
3016
+ @inside_disp_units_lbl_solid_fill = false
3017
+ @disp_units_lbl_font = nil
3018
+ when "dispUnitsLbl"
3019
+ if @disp_units_lbl_font
3020
+ du = @val_axis_disp_units
3021
+ du = {} unless du.is_a?(Hash)
3022
+ @val_axis_disp_units = du
3023
+ (du[:label] ||= {})[:font] = @disp_units_lbl_font
3024
+ end
3025
+ @inside_disp_units_lbl = false
3026
+ @inside_disp_units_lbl_sp_pr = false
3027
+ @inside_disp_units_lbl_ln = false
3028
+ @inside_disp_units_lbl_solid_fill = false
3029
+ @disp_units_lbl_font = nil
3030
+ when "txPr"
3031
+ @inside_axis_def_rpr = false if @inside_axis_tx_pr
3032
+ if @inside_chart_space_tx_pr
3033
+ @inside_axis_tx_pr = false
3034
+ @inside_chart_space_tx_pr = false
3035
+ end
3036
+ when "scaling"
3037
+ @inside_scaling = false
3038
+ when "view3D"
3039
+ @inside_view_3d = false
3040
+ when "floor", "sideWall", "backWall"
3041
+ @inside_wall = false
3042
+ @current_wall = nil
3043
+ @inside_wall_sp_pr = false
3044
+ @inside_wall_ln = false
3045
+ @inside_wall_solid_fill = false
3046
+ when "dTable"
3047
+ @data_table[:font] = @d_table_font if @d_table_font && @data_table
3048
+ @inside_d_table = false
3049
+ @inside_d_table_sp_pr = false
3050
+ @inside_d_table_ln = false
3051
+ @inside_d_table_solid_fill = false
3052
+ @inside_axis_tx_pr = false
3053
+ @inside_axis_def_rpr = false
3054
+ when "protection"
3055
+ @inside_protection = false
3056
+ when "printSettings"
3057
+ @inside_print_settings = false
3058
+ when "headerFooter"
3059
+ @inside_ps_header_footer = false if @inside_print_settings
3060
+ when "oddHeader"
3061
+ if @inside_ps_odd_header
3062
+ @print_settings[:header_footer][:odd_header] = @text_buffer.dup unless @text_buffer.empty?
3063
+ @inside_ps_odd_header = false
3064
+ end
3065
+ when "oddFooter"
3066
+ if @inside_ps_odd_footer
3067
+ @print_settings[:header_footer][:odd_footer] = @text_buffer.dup unless @text_buffer.empty?
3068
+ @inside_ps_odd_footer = false
3069
+ end
3070
+ when "evenHeader"
3071
+ if @inside_ps_even_header
3072
+ @print_settings[:header_footer][:even_header] = @text_buffer.dup unless @text_buffer.empty?
3073
+ @inside_ps_even_header = false
3074
+ end
3075
+ when "evenFooter"
3076
+ if @inside_ps_even_footer
3077
+ @print_settings[:header_footer][:even_footer] = @text_buffer.dup unless @text_buffer.empty?
3078
+ @inside_ps_even_footer = false
3079
+ end
3080
+ when "firstHeader"
3081
+ if @inside_ps_first_header
3082
+ @print_settings[:header_footer][:first_header] = @text_buffer.dup unless @text_buffer.empty?
3083
+ @inside_ps_first_header = false
3084
+ end
3085
+ when "firstFooter"
3086
+ if @inside_ps_first_footer
3087
+ @print_settings[:header_footer][:first_footer] = @text_buffer.dup unless @text_buffer.empty?
3088
+ @inside_ps_first_footer = false
3089
+ end
3090
+ when "upDownBars"
3091
+ @inside_up_down_bars = false
3092
+ @inside_up_bars = false
3093
+ @inside_down_bars = false
3094
+ @inside_up_down_bar_sp_pr = false
3095
+ @inside_up_down_bar_ln = false
3096
+ @inside_up_down_bar_solid_fill = false
3097
+ when "upBars"
3098
+ @inside_up_bars = false
3099
+ @inside_up_down_bar_sp_pr = false
3100
+ @inside_up_down_bar_ln = false
3101
+ @inside_up_down_bar_solid_fill = false
3102
+ when "downBars"
3103
+ @inside_down_bars = false
3104
+ @inside_up_down_bar_sp_pr = false
3105
+ @inside_up_down_bar_ln = false
3106
+ @inside_up_down_bar_solid_fill = false
3107
+ when "dropLines"
3108
+ @inside_drop_lines = false
3109
+ @inside_drop_lines_sp_pr = false
3110
+ @inside_drop_lines_ln = false
3111
+ @inside_drop_lines_solid_fill = false
3112
+ when "hiLowLines"
3113
+ @inside_hi_low_lines = false
3114
+ @inside_hi_low_lines_sp_pr = false
3115
+ @inside_hi_low_lines_ln = false
3116
+ @inside_hi_low_lines_solid_fill = false
3117
+ when "serLines"
3118
+ @inside_ser_lines = false
3119
+ @inside_ser_lines_sp_pr = false
3120
+ @inside_ser_lines_ln = false
3121
+ @inside_ser_lines_solid_fill = false
3122
+ when "custSplit"
3123
+ @inside_cust_split = false
3124
+ when "bandFmt"
3125
+ if @inside_band_fmt && @current_band_fmt
3126
+ @band_fmts << @current_band_fmt
3127
+ @current_band_fmt = nil
3128
+ @inside_band_fmt = false
3129
+ @inside_band_fmt_sp_pr = false
3130
+ @inside_band_fmt_ln = false
3131
+ @inside_band_fmt_solid_fill = false
3132
+ end
3133
+ when "bandFmts"
3134
+ @inside_band_fmts = false
3135
+ when "leaderLines"
3136
+ @inside_leader_lines = false
3137
+ @inside_leader_lines_sp_pr = false
3138
+ @inside_leader_lines_ln = false
3139
+ @inside_leader_lines_solid_fill = false
3140
+ end
3141
+ end
3142
+
3143
+ private
3144
+
3145
+ def assign_title_layout_value(key, value)
3146
+ if @inside_ax_title
3147
+ if @inside_cat_ax
3148
+ (@cat_axis_title_layout ||= {})[key] = value
3149
+ elsif @inside_val_ax
3150
+ (@val_axis_title_layout ||= {})[key] = value
3151
+ end
3152
+ elsif @inside_title && @title_depth == 1
3153
+ (@title_layout ||= {})[key] = value
3154
+ end
3155
+ end
3156
+
3157
+ def assign_chart_color(color_value)
3158
+ if @inside_axis_def_rpr
3159
+ if @inside_disp_units_lbl
3160
+ (@disp_units_lbl_font ||= {})[:color] = color_value
3161
+ elsif @inside_cat_ax
3162
+ (@cat_axis_font ||= {})[:color] = color_value
3163
+ elsif @inside_val_ax
3164
+ (@val_axis_font ||= {})[:color] = color_value
3165
+ elsif @inside_legend_entry && @current_legend_entry
3166
+ (@current_legend_entry[:font] ||= {})[:color] = color_value
3167
+ elsif @inside_legend
3168
+ (@legend_font ||= {})[:color] = color_value
3169
+ elsif @inside_d_table
3170
+ (@d_table_font ||= {})[:color] = color_value
3171
+ elsif @inside_dlbl && @current_dlbl
3172
+ (@current_dlbl[:font] ||= {})[:color] = color_value
3173
+ elsif @inside_dlbls
3174
+ (@dlbls_font ||= {})[:color] = color_value
3175
+ elsif @inside_trendline_lbl && @current_trendline
3176
+ (@trendline_lbl_font ||= {})[:color] = color_value
3177
+ elsif @inside_chart_space_tx_pr
3178
+ (@chart_font ||= {})[:color] = color_value
3179
+ end
3180
+ elsif @inside_title_rpr && @title_font
3181
+ @title_font[:color] = color_value
3182
+ elsif @inside_ax_title_rpr
3183
+ if @inside_cat_ax
3184
+ (@cat_axis_title_font ||= {})[:color] = color_value
3185
+ elsif @inside_val_ax
3186
+ (@val_axis_title_font ||= {})[:color] = color_value
3187
+ end
3188
+ elsif @inside_ax_title_sp_pr && @inside_ax_title_ln && @inside_ax_title_solid_fill
3189
+ if @inside_cat_ax
3190
+ @cat_axis_title_line_color = color_value
3191
+ elsif @inside_val_ax
3192
+ @val_axis_title_line_color = color_value
3193
+ end
3194
+ elsif @inside_ax_title_sp_pr && @inside_ax_title_solid_fill
3195
+ if @inside_cat_ax
3196
+ @cat_axis_title_fill = color_value
3197
+ elsif @inside_val_ax
3198
+ @val_axis_title_fill = color_value
3199
+ end
3200
+ elsif @inside_band_fmt_sp_pr && @inside_band_fmt_ln && @inside_band_fmt_solid_fill && @current_band_fmt
3201
+ @current_band_fmt[:line_color] = color_value
3202
+ elsif @inside_band_fmt_sp_pr && @inside_band_fmt_solid_fill && @current_band_fmt
3203
+ @current_band_fmt[:fill_color] = color_value
3204
+ elsif @inside_dpt_marker_sp_pr && @inside_dpt_marker_ln && @inside_dpt_marker_solid_fill && @current_dpt
3205
+ @current_dpt[:marker_line_color] = color_value
3206
+ elsif @inside_dpt_marker_sp_pr && @inside_dpt_marker_solid_fill && @current_dpt
3207
+ @current_dpt[:marker_fill] = color_value
3208
+ elsif @inside_dpt && @inside_dpt_sp_pr && @inside_dpt_ln && @current_dpt
3209
+ @current_dpt[:line_color] = color_value
3210
+ elsif @inside_dpt && @inside_dpt_sp_pr && @inside_dpt_solid_fill && @current_dpt
3211
+ @current_dpt[:fill_color] = color_value
3212
+ elsif @inside_marker_sp_pr && @inside_marker_ln && @inside_marker_solid_fill && @current_ser
3213
+ @current_ser[:marker_line_color] = color_value
3214
+ elsif @inside_marker_sp_pr && @inside_marker_solid_fill && @current_ser
3215
+ @current_ser[:marker_fill] = color_value
3216
+ elsif @inside_leader_lines_sp_pr && @inside_leader_lines_ln && @inside_leader_lines_solid_fill
3217
+ ll = (@dlbl_target[:leader_lines] ||= {})
3218
+ ll[:line_color] = color_value
3219
+ if @dlbl_target != @data_labels
3220
+ ll2 = (@data_labels[:leader_lines] ||= {})
3221
+ ll2[:line_color] = color_value
3222
+ end
3223
+ elsif @inside_dlbl_sp_pr && @inside_dlbl_ln && @inside_dlbl_solid_fill && @current_dlbl
3224
+ @current_dlbl[:line_color] = color_value
3225
+ elsif @inside_dlbl_sp_pr && @inside_dlbl_solid_fill && @current_dlbl
3226
+ @current_dlbl[:fill_color] = color_value
3227
+ elsif @inside_dlbls_sp_pr && @inside_dlbls_ln && @inside_dlbls_solid_fill
3228
+ dl_target = @dlbl_target || @data_labels
3229
+ dl_target[:line_color] = color_value
3230
+ @data_labels[:line_color] = color_value if dl_target != @data_labels
3231
+ elsif @inside_dlbls_sp_pr && @inside_dlbls_solid_fill
3232
+ dl_target = @dlbl_target || @data_labels
3233
+ dl_target[:fill_color] = color_value
3234
+ @data_labels[:fill_color] = color_value if dl_target != @data_labels
3235
+ elsif @inside_trendline_lbl_sp_pr && @inside_trendline_lbl_ln && @inside_trendline_lbl_solid_fill && @current_trendline
3236
+ (@current_trendline[:label] ||= {})[:line_color] = color_value
3237
+ elsif @inside_trendline_lbl_sp_pr && @inside_trendline_lbl_solid_fill && @current_trendline
3238
+ (@current_trendline[:label] ||= {})[:fill_color] = color_value
3239
+ elsif @inside_trendline_sp_pr && @inside_trendline_ln && @inside_trendline_solid_fill && @current_trendline
3240
+ @current_trendline[:line_color] = color_value
3241
+ elsif @inside_err_bars_sp_pr && @inside_err_bars_ln && @inside_err_bars_solid_fill && @current_err_bars
3242
+ @current_err_bars[:line_color] = color_value
3243
+ elsif @inside_err_bars_sp_pr && @inside_err_bars_solid_fill && @current_err_bars
3244
+ @current_err_bars[:fill_color] = color_value
3245
+ elsif @inside_ser && @inside_ser_sp_pr && @inside_ser_ln && @inside_ser_solid_fill && @current_ser
3246
+ @current_ser[:line_color] = color_value
3247
+ elsif @inside_ser && @inside_ser_sp_pr && @inside_ser_solid_fill && @current_ser
3248
+ @current_ser[:fill_color] = color_value
3249
+ elsif @inside_drop_lines_sp_pr && @inside_drop_lines_ln && @inside_drop_lines_solid_fill
3250
+ @drop_lines = {} if @drop_lines == true
3251
+ @drop_lines[:line_color] = color_value
3252
+ elsif @inside_hi_low_lines_sp_pr && @inside_hi_low_lines_ln && @inside_hi_low_lines_solid_fill
3253
+ @hi_low_lines = {} if @hi_low_lines == true
3254
+ @hi_low_lines[:line_color] = color_value
3255
+ elsif @inside_ser_lines_sp_pr && @inside_ser_lines_ln && @inside_ser_lines_solid_fill
3256
+ @ser_lines = {} if @ser_lines == true
3257
+ @ser_lines[:line_color] = color_value
3258
+ elsif @inside_up_down_bar_sp_pr && @inside_up_down_bar_ln && @inside_up_down_bar_solid_fill
3259
+ bar_key = @inside_up_bars ? :up_bars : :down_bars
3260
+ @up_down_bars[bar_key] ||= {}
3261
+ @up_down_bars[bar_key][:line_color] = color_value
3262
+ elsif @inside_up_down_bar_sp_pr && @inside_up_down_bar_solid_fill
3263
+ bar_key = @inside_up_bars ? :up_bars : :down_bars
3264
+ @up_down_bars[bar_key] ||= {}
3265
+ @up_down_bars[bar_key][:fill_color] = color_value
3266
+ elsif @inside_plot_area_sp_pr && @inside_plot_area_ln && @inside_plot_area_solid_fill
3267
+ @plot_area_line_color = color_value
3268
+ elsif @inside_plot_area_sp_pr && @inside_plot_area_solid_fill
3269
+ @plot_area_fill = color_value
3270
+ elsif @inside_gridlines_sp_pr && @inside_gridlines_ln && @inside_gridlines_solid_fill && @gridlines_target
3271
+ gl = instance_variable_get(:"@#{@gridlines_target}")
3272
+ gl = {} if gl == true
3273
+ gl[:line_color] = color_value
3274
+ instance_variable_set(:"@#{@gridlines_target}", gl)
3275
+ elsif @inside_ax_sp_pr && @inside_ax_ln && @inside_ax_solid_fill
3276
+ if @inside_cat_ax
3277
+ @cat_axis_line_color = color_value
3278
+ elsif @inside_val_ax
3279
+ @val_axis_line_color = color_value
3280
+ end
3281
+ elsif @inside_ax_sp_pr && @inside_ax_solid_fill
3282
+ if @inside_cat_ax
3283
+ @cat_axis_fill = color_value
3284
+ elsif @inside_val_ax
3285
+ @val_axis_fill = color_value
3286
+ end
3287
+ elsif @inside_disp_units_lbl_sp_pr && @inside_disp_units_lbl_ln && @inside_disp_units_lbl_solid_fill
3288
+ du = @val_axis_disp_units
3289
+ du = {} unless du.is_a?(Hash)
3290
+ @val_axis_disp_units = du
3291
+ (du[:label] ||= {})[:line_color] = color_value
3292
+ elsif @inside_disp_units_lbl_sp_pr && @inside_disp_units_lbl_solid_fill
3293
+ du = @val_axis_disp_units
3294
+ du = {} unless du.is_a?(Hash)
3295
+ @val_axis_disp_units = du
3296
+ (du[:label] ||= {})[:fill_color] = color_value
3297
+ elsif @inside_wall_sp_pr && @inside_wall_ln && @inside_wall_solid_fill && @current_wall
3298
+ @current_wall[:line_color] = color_value
3299
+ elsif @inside_wall_sp_pr && @inside_wall_solid_fill && @current_wall
3300
+ @current_wall[:fill_color] = color_value
3301
+ elsif @inside_legend_sp_pr && @inside_legend_ln && @inside_legend_solid_fill
3302
+ @legend[:line_color] = color_value
3303
+ elsif @inside_legend_sp_pr && @inside_legend_solid_fill
3304
+ @legend[:fill_color] = color_value
3305
+ elsif @inside_d_table_sp_pr && @inside_d_table_ln && @inside_d_table_solid_fill && @data_table
3306
+ @data_table[:line_color] = color_value
3307
+ elsif @inside_d_table_sp_pr && @inside_d_table_solid_fill && @data_table
3308
+ @data_table[:fill_color] = color_value
3309
+ elsif @inside_title_sp_pr && @inside_title_ln && @inside_title_solid_fill
3310
+ @title_line_color = color_value
3311
+ elsif @inside_title_sp_pr && @inside_title_solid_fill
3312
+ @title_fill_color = color_value
3313
+ elsif @inside_chart_space_sp_pr && @inside_chart_space_ln && @inside_chart_space_solid_fill
3314
+ @chart_line_color = color_value
3315
+ elsif @inside_chart_space_sp_pr && @inside_chart_space_solid_fill
3316
+ @chart_fill = color_value
3317
+ end
3318
+ end
3319
+
3320
+ def element_name(local_name, qname)
3321
+ if local_name.nil? || local_name.empty?
3322
+ qname.to_s.split(":").last
3323
+ else
3324
+ local_name
3325
+ end
3326
+ end
3327
+ end
3328
+
3329
+ # SAX2 listener for parsing comments XML.
3330
+ class CommentsListener
3331
+ include REXML::SAX2Listener
3332
+
3333
+ attr_reader :comments
3334
+
3335
+ def initialize
3336
+ @comments = []
3337
+ @authors = []
3338
+ @inside_authors = false
3339
+ @inside_author = false
3340
+ @inside_comment = false
3341
+ @inside_text = false
3342
+ @inside_r = false
3343
+ @inside_rpr = false
3344
+ @inside_t = false
3345
+ @current_comment = nil
3346
+ @text_buffer = +""
3347
+ @runs = []
3348
+ @current_font = {}
3349
+ @has_runs = false
3350
+ end
3351
+
3352
+ def start_element(_uri, local_name, qname, attributes)
3353
+ name = element_name(local_name, qname)
3354
+ case name
3355
+ when "authors"
3356
+ @inside_authors = true
3357
+ when "author"
3358
+ @inside_author = true
3359
+ @text_buffer = +""
3360
+ when "comment"
3361
+ @inside_comment = true
3362
+ @current_comment = { ref: attributes["ref"], author_id: attributes["authorId"]&.to_i }
3363
+ @current_comment[:guid] = attributes["guid"] if attributes["guid"]
3364
+ sid = attributes["shapeId"]
3365
+ @current_comment[:shape_id] = sid.to_i if sid
3366
+ when "text"
3367
+ if @inside_comment
3368
+ @inside_text = true
3369
+ @text_buffer = +""
3370
+ @runs = []
3371
+ @has_runs = false
3372
+ end
3373
+ when "r"
3374
+ if @inside_text
3375
+ @inside_r = true
3376
+ @has_runs = true
3377
+ @current_font = {}
3378
+ end
3379
+ when "rPr"
3380
+ @inside_rpr = true if @inside_r
3381
+ when "b"
3382
+ @current_font[:bold] = true if @inside_rpr
3383
+ when "i"
3384
+ @current_font[:italic] = true if @inside_rpr
3385
+ when "strike"
3386
+ @current_font[:strike] = true if @inside_rpr
3387
+ when "u"
3388
+ if @inside_rpr
3389
+ val = attributes["val"]
3390
+ @current_font[:underline] = val || true
3391
+ end
3392
+ when "vertAlign"
3393
+ @current_font[:vert_align] = attributes["val"] if @inside_rpr && attributes["val"]
3394
+ when "sz"
3395
+ @current_font[:sz] = attributes["val"]&.to_f if @inside_rpr
3396
+ when "color"
3397
+ if @inside_rpr
3398
+ if attributes["rgb"]
3399
+ @current_font[:color] = attributes["rgb"]
3400
+ elsif attributes["theme"]
3401
+ @current_font[:theme] = attributes["theme"].to_i
3402
+ @current_font[:tint] = attributes["tint"].to_f if attributes["tint"]
3403
+ elsif attributes["indexed"]
3404
+ @current_font[:indexed] = attributes["indexed"].to_i
3405
+ end
3406
+ end
3407
+ when "rFont"
3408
+ @current_font[:name] = attributes["val"] if @inside_rpr
3409
+ when "family"
3410
+ @current_font[:family] = attributes["val"]&.to_i if @inside_rpr
3411
+ when "scheme"
3412
+ @current_font[:scheme] = attributes["val"] if @inside_rpr
3413
+ when "t"
3414
+ if @inside_text
3415
+ @inside_t = true
3416
+ @text_buffer = +"" if @inside_r
3417
+ end
3418
+ end
3419
+ end
3420
+
3421
+ def characters(text)
3422
+ @text_buffer << text if @inside_author || @inside_t
3423
+ end
3424
+
3425
+ def end_element(_uri, local_name, qname)
3426
+ name = element_name(local_name, qname)
3427
+ case name
3428
+ when "authors"
3429
+ @inside_authors = false
3430
+ when "author"
3431
+ @authors << @text_buffer.dup if @inside_authors
3432
+ @inside_author = false
3433
+ when "comment"
3434
+ if @current_comment
3435
+ aid = @current_comment[:author_id]
3436
+ @current_comment[:author] = @authors[aid] if aid && aid < @authors.size
3437
+ @current_comment.delete(:author_id)
3438
+ @comments << @current_comment
3439
+ end
3440
+ @inside_comment = false
3441
+ @current_comment = nil
3442
+ when "text"
3443
+ if @current_comment && @inside_text
3444
+ if @has_runs && @runs.any? { |r| r[:font] }
3445
+ @current_comment[:text] = Xlsxrb::Elements::RichText.new(runs: @runs)
3446
+ else
3447
+ plain = @has_runs ? @runs.map { |r| r[:text] }.join : @text_buffer.dup
3448
+ @current_comment[:text] = plain
3449
+ end
3450
+ end
3451
+ @inside_text = false
3452
+ when "t"
3453
+ @inside_t = false
3454
+ when "rPr"
3455
+ @inside_rpr = false
3456
+ when "r"
3457
+ if @inside_r
3458
+ run = { text: @text_buffer.dup }
3459
+ run[:font] = @current_font.dup unless @current_font.empty?
3460
+ @runs << run
3461
+ @inside_r = false
3462
+ end
3463
+ end
3464
+ end
3465
+
3466
+ private
3467
+
3468
+ def element_name(local_name, qname)
3469
+ if local_name.nil? || local_name.empty?
3470
+ qname.to_s.split(":").last
3471
+ else
3472
+ local_name
3473
+ end
3474
+ end
3475
+ end
3476
+ end
3477
+ end
3478
+ end