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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +86 -34
- data/README.md +26 -21
- data/lib/xlsxrb/chart_builder.rb +18 -18
- data/lib/xlsxrb/dsl_helpers.rb +175 -0
- data/lib/xlsxrb/elements/cell.rb +56 -22
- data/lib/xlsxrb/elements/column.rb +27 -3
- data/lib/xlsxrb/elements/row.rb +27 -8
- data/lib/xlsxrb/elements/workbook.rb +27 -14
- data/lib/xlsxrb/elements/worksheet.rb +17 -1
- data/lib/xlsxrb/ooxml/cfb.rb +9 -6
- data/lib/xlsxrb/ooxml/crypto/agile.rb +5 -5
- data/lib/xlsxrb/ooxml/reader/listeners/core_listeners.rb +784 -0
- data/lib/xlsxrb/ooxml/reader/listeners/drawing_listeners.rb +3478 -0
- data/lib/xlsxrb/ooxml/reader/listeners/feature_listeners.rb +1796 -0
- data/lib/xlsxrb/ooxml/reader/listeners/style_listeners.rb +428 -0
- data/lib/xlsxrb/ooxml/reader/listeners.rb +4 -6446
- data/lib/xlsxrb/ooxml/shared_strings_parser.rb +3 -5
- data/lib/xlsxrb/ooxml/utils.rb +5 -6
- data/lib/xlsxrb/ooxml/workbook_writer.rb +5 -7
- data/lib/xlsxrb/ooxml/worksheet_parser.rb +172 -9
- data/lib/xlsxrb/ooxml/writer/drawing_xml.rb +1602 -0
- data/lib/xlsxrb/ooxml/writer/features_xml.rb +731 -0
- data/lib/xlsxrb/ooxml/writer/styles_xml.rb +348 -0
- data/lib/xlsxrb/ooxml/writer.rb +9 -2646
- data/lib/xlsxrb/ooxml/xml_builder.rb +31 -2
- data/lib/xlsxrb/ooxml/zip_generator.rb +26 -5
- data/lib/xlsxrb/ooxml/zip_reader.rb +364 -78
- data/lib/xlsxrb/ooxml/zip_writer.rb +32 -5
- data/lib/xlsxrb/stream_sheet.rb +94 -6
- data/lib/xlsxrb/stream_writer.rb +48 -115
- data/lib/xlsxrb/style_builder.rb +10 -1
- data/lib/xlsxrb/version.rb +1 -1
- data/lib/xlsxrb/workbook_builder.rb +1 -1
- data/lib/xlsxrb/worksheet_builder.rb +19 -81
- data/lib/xlsxrb.rb +449 -171
- data/sig/generated/xlsxrb/chart_builder.rbs +36 -36
- data/sig/generated/xlsxrb/dsl_helpers.rbs +92 -0
- data/sig/generated/xlsxrb/elements/cell.rbs +29 -0
- data/sig/generated/xlsxrb/elements/column.rbs +32 -21
- data/sig/generated/xlsxrb/elements/row.rbs +94 -14
- data/sig/generated/xlsxrb/elements/workbook.rbs +102 -10
- data/sig/generated/xlsxrb/elements/worksheet.rbs +9 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/core_listeners.rbs +173 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/drawing_listeners.rbs +400 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/feature_listeners.rbs +442 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/style_listeners.rbs +80 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners.rbs +0 -1072
- data/sig/generated/xlsxrb/ooxml/shared_strings_parser.rbs +0 -2
- data/sig/generated/xlsxrb/ooxml/utils.rbs +2 -0
- data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +5 -4
- data/sig/generated/xlsxrb/ooxml/writer/drawing_xml.rbs +79 -0
- data/sig/generated/xlsxrb/ooxml/writer/features_xml.rbs +83 -0
- data/sig/generated/xlsxrb/ooxml/writer/styles_xml.rbs +53 -0
- data/sig/generated/xlsxrb/ooxml/writer.rbs +6 -185
- data/sig/generated/xlsxrb/ooxml/xml_builder.rbs +18 -0
- data/sig/generated/xlsxrb/ooxml/zip_generator.rbs +15 -0
- data/sig/generated/xlsxrb/ooxml/zip_reader.rbs +96 -8
- data/sig/generated/xlsxrb/ooxml/zip_writer.rbs +5 -1
- data/sig/generated/xlsxrb/stream_sheet.rbs +43 -3
- data/sig/generated/xlsxrb/style_builder.rbs +7 -0
- data/sig/generated/xlsxrb.rbs +18 -11
- metadata +22 -144
- data/.devcontainer/Dockerfile +0 -65
- data/.devcontainer/devcontainer.json +0 -17
- data/.gem_rbs_collection/ast/2.4/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/ast/2.4/ast.rbs +0 -73
- data/.gem_rbs_collection/concurrent-ruby/1.1/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/concurrent-ruby/1.1/array.rbs +0 -4
- data/.gem_rbs_collection/concurrent-ruby/1.1/atomic_reference.rbs +0 -16
- data/.gem_rbs_collection/concurrent-ruby/1.1/executor.rbs +0 -96
- data/.gem_rbs_collection/concurrent-ruby/1.1/hash.rbs +0 -4
- data/.gem_rbs_collection/concurrent-ruby/1.1/map.rbs +0 -68
- data/.gem_rbs_collection/concurrent-ruby/1.1/promises.rbs +0 -249
- data/.gem_rbs_collection/concurrent-ruby/1.1/set.rbs +0 -4
- data/.gem_rbs_collection/concurrent-ruby/1.1/timer_task.rbs +0 -47
- data/.gem_rbs_collection/concurrent-ruby/1.1/utility/processor_counter.rbs +0 -5
- data/.gem_rbs_collection/csv/3.3/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/csv/3.3/csv.rbs +0 -3871
- data/.gem_rbs_collection/csv/3.3/manifest.yaml +0 -3
- data/.gem_rbs_collection/lint_roller/1.1/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/lint_roller/1.1/lint_roller.rbs +0 -48
- data/.gem_rbs_collection/listen/3.9/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/listen/3.9/listen.rbs +0 -25
- data/.gem_rbs_collection/listen/3.9/listener.rbs +0 -24
- data/.gem_rbs_collection/logger/1.7/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/logger/1.7/formatter.rbs +0 -45
- data/.gem_rbs_collection/logger/1.7/log_device.rbs +0 -100
- data/.gem_rbs_collection/logger/1.7/logger.rbs +0 -796
- data/.gem_rbs_collection/logger/1.7/manifest.yaml +0 -2
- data/.gem_rbs_collection/logger/1.7/period.rbs +0 -17
- data/.gem_rbs_collection/logger/1.7/severity.rbs +0 -34
- data/.gem_rbs_collection/nokogiri/1.11/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/nokogiri/1.11/nokogiri.rbs +0 -2332
- data/.gem_rbs_collection/nokogiri/1.11/patch.rbs +0 -4
- data/.gem_rbs_collection/parallel/1.20/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/parallel/1.20/parallel.rbs +0 -86
- data/.gem_rbs_collection/parser/3.2/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/parser/3.2/manifest.yaml +0 -7
- data/.gem_rbs_collection/parser/3.2/parser.rbs +0 -194
- data/.gem_rbs_collection/parser/3.2/polyfill.rbs +0 -4
- data/.gem_rbs_collection/rainbow/3.0/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rainbow/3.0/global.rbs +0 -7
- data/.gem_rbs_collection/rainbow/3.0/presenter.rbs +0 -209
- data/.gem_rbs_collection/rainbow/3.0/rainbow.rbs +0 -5
- data/.gem_rbs_collection/rake/13.0/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rake/13.0/manifest.yaml +0 -2
- data/.gem_rbs_collection/rake/13.0/rake.rbs +0 -39
- data/.gem_rbs_collection/regexp_parser/2.8/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/regexp_parser/2.8/regexp_parser.rbs +0 -17
- data/.gem_rbs_collection/rubocop/1.57/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubocop/1.57/rubocop.rbs +0 -208
- data/.gem_rbs_collection/rubocop-ast/1.46/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubocop-ast/1.46/rubocop-ast.rbs +0 -903
- data/.gem_rbs_collection/rubyzip/3.2/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubyzip/3.2/manifest.yaml +0 -8
- data/.gem_rbs_collection/rubyzip/3.2/zip/central_directory.rbs +0 -42
- data/.gem_rbs_collection/rubyzip/3.2/zip/compressor.rbs +0 -5
- data/.gem_rbs_collection/rubyzip/3.2/zip/constants.rbs +0 -47
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/aes_encryption.rbs +0 -30
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/decrypted_io.rbs +0 -9
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/encryption.rbs +0 -7
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/null_encryption.rbs +0 -19
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/traditional_encryption.rbs +0 -31
- data/.gem_rbs_collection/rubyzip/3.2/zip/decompressor.rbs +0 -18
- data/.gem_rbs_collection/rubyzip/3.2/zip/deflater.rbs +0 -12
- data/.gem_rbs_collection/rubyzip/3.2/zip/dirtyable.rbs +0 -11
- data/.gem_rbs_collection/rubyzip/3.2/zip/dos_time.rbs +0 -13
- data/.gem_rbs_collection/rubyzip/3.2/zip/entry.rbs +0 -95
- data/.gem_rbs_collection/rubyzip/3.2/zip/entry_set.rbs +0 -31
- data/.gem_rbs_collection/rubyzip/3.2/zip/errors.rbs +0 -58
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/aes.rbs +0 -24
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/generic.rbs +0 -17
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/ntfs.rbs +0 -23
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/old_unix.rbs +0 -22
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/universal_time.rbs +0 -30
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unix.rbs +0 -20
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unknown.rbs +0 -15
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/zip64.rbs +0 -26
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field.rbs +0 -21
- data/.gem_rbs_collection/rubyzip/3.2/zip/file.rbs +0 -131
- data/.gem_rbs_collection/rubyzip/3.2/zip/file_split.rbs +0 -14
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/dir.rbs +0 -33
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/directory_iterator.rbs +0 -21
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file.rbs +0 -63
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file_stat.rbs +0 -55
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/zip_file_name_mapper.rbs +0 -35
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem.rbs +0 -7
- data/.gem_rbs_collection/rubyzip/3.2/zip/inflater.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/input_stream.rbs +0 -22
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_input_stream.rbs +0 -29
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_output_stream.rbs +0 -17
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras.rbs +0 -13
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_compressor.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_decompressor.rbs +0 -8
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_input_stream.rbs +0 -6
- data/.gem_rbs_collection/rubyzip/3.2/zip/output_stream.rbs +0 -30
- data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_compressor.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_decompressor.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_directory.rbs +0 -5
- data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_stream.rbs +0 -15
- data/.gem_rbs_collection/rubyzip/3.2/zip/version.rbs +0 -3
- data/.gem_rbs_collection/rubyzip/3.2/zip.rbs +0 -40
- data/Rakefile +0 -567
- data/Steepfile +0 -18
- data/benchmark.rb +0 -406
- data/docs/ARCHITECTURE.md +0 -514
- data/docs/DEVELOPMENT.md +0 -133
- data/docs/PEER_LIBRARIES.md +0 -121
- data/docs/QUALITY_ASSURANCE.md +0 -25
- data/docs/SPEC_SOURCES.md +0 -48
- data/docs/assets/benchmark_results.svg +0 -144
- data/docs/assets/lsp_autocompletion.png +0 -0
- data/docs/assets/playground_preview.png +0 -0
- data/docs/coi-serviceworker.js +0 -82
- data/docs/office_thread.js +0 -77
- data/docs/preview.html +0 -719
- data/docs/visual/VisualGallery.md +0 -4672
- data/docs/wasm/ruby.wasm +0 -0
- data/docs/wasm/wasm_doc_helper.css +0 -351
- data/docs/wasm/wasm_doc_helper.js +0 -439
- data/docs/zeta.js +0 -1107
- data/rbs_collection.lock.yaml +0 -252
- data/rbs_collection.yaml +0 -19
- data/vendor/sdk_runner/Program.cs +0 -93
- data/vendor/sdk_runner/sdk_runner.csproj +0 -13
|
@@ -0,0 +1,1602 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# rbs_inline: enabled
|
|
4
|
+
|
|
5
|
+
module Xlsxrb
|
|
6
|
+
module Ooxml
|
|
7
|
+
class Writer
|
|
8
|
+
# Mixin containing DrawingML, Chart, Shape, Comment, and VML XML generation logic.
|
|
9
|
+
module DrawingXml
|
|
10
|
+
# : (untyped drawing_parts) -> untyped
|
|
11
|
+
def generate_drawing_xml(drawing_parts)
|
|
12
|
+
parts = [
|
|
13
|
+
XML_HEADER,
|
|
14
|
+
%(<xdr:wsDr xmlns:xdr="#{XDR_NS}" xmlns:a="#{A_NS}" xmlns:r="#{DOC_REL_NS}">)
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
drawing_parts.each do |dp|
|
|
18
|
+
case dp[:kind]
|
|
19
|
+
when :pic
|
|
20
|
+
img = dp[:img]
|
|
21
|
+
rid = "rId#{dp[:rid_index]}"
|
|
22
|
+
ea = img[:edit_as] || "oneCell"
|
|
23
|
+
img_pub_attr = img[:published] ? ' fPublished="1"' : ""
|
|
24
|
+
parts << %(<xdr:twoCellAnchor editAs="#{xml_escape(ea)}"#{img_pub_attr}>)
|
|
25
|
+
parts << anchor_xml("from", img[:from_col], img[:from_row], col_off: img[:from_col_off] || 0, row_off: img[:from_row_off] || 0)
|
|
26
|
+
parts << anchor_xml("to", img[:to_col], img[:to_row], col_off: img[:to_col_off] || 0, row_off: img[:to_row_off] || 0)
|
|
27
|
+
macro_attr = img[:macro] ? %( macro="#{xml_escape(img[:macro])}") : ""
|
|
28
|
+
parts << "<xdr:pic#{macro_attr}>"
|
|
29
|
+
descr_attr = img[:description] ? %( descr="#{xml_escape(img[:description])}") : ""
|
|
30
|
+
title_attr = img[:title] ? %( title="#{xml_escape(img[:title])}") : ""
|
|
31
|
+
hidden_attr = img[:hidden] ? ' hidden="1"' : ""
|
|
32
|
+
pic_lock_attrs = +""
|
|
33
|
+
pic_lock_attrs << ' noChangeAspect="1"' if img[:no_change_aspect]
|
|
34
|
+
pic_lock_attrs << ' noCrop="1"' if img[:no_crop]
|
|
35
|
+
pic_locks = pic_lock_attrs.empty? ? "<a:picLocks/>" : "<a:picLocks#{pic_lock_attrs}/>"
|
|
36
|
+
parts << %(<xdr:nvPicPr><xdr:cNvPr id="#{dp[:rid_index] + 1}" name="#{xml_escape(img[:name])}"#{descr_attr}#{title_attr}#{hidden_attr}/><xdr:cNvPicPr>#{pic_locks}</xdr:cNvPicPr></xdr:nvPicPr>)
|
|
37
|
+
src_rect_xml = if img[:src_rect]
|
|
38
|
+
sr = img[:src_rect]
|
|
39
|
+
sr_attrs = +""
|
|
40
|
+
sr_attrs << %( t="#{sr[:top]}") if sr[:top]
|
|
41
|
+
sr_attrs << %( b="#{sr[:bottom]}") if sr[:bottom]
|
|
42
|
+
sr_attrs << %( l="#{sr[:left]}") if sr[:left]
|
|
43
|
+
sr_attrs << %( r="#{sr[:right]}") if sr[:right]
|
|
44
|
+
"<a:srcRect#{sr_attrs}/>"
|
|
45
|
+
else
|
|
46
|
+
""
|
|
47
|
+
end
|
|
48
|
+
blip_xml = if img[:alpha_mod_fix]
|
|
49
|
+
%(<a:blip r:embed="#{rid}"><a:alphaModFix amt="#{img[:alpha_mod_fix]}"/></a:blip>)
|
|
50
|
+
else
|
|
51
|
+
%(<a:blip r:embed="#{rid}"/>)
|
|
52
|
+
end
|
|
53
|
+
parts << %(<xdr:blipFill>#{blip_xml}#{src_rect_xml}<a:stretch><a:fillRect/></a:stretch></xdr:blipFill>)
|
|
54
|
+
img_line_xml = if img[:line_color]
|
|
55
|
+
ln_w_attr = img[:line_width] ? %( w="#{img[:line_width].to_i}") : ""
|
|
56
|
+
%(<a:ln#{ln_w_attr}><a:solidFill>#{color_xml(img[:line_color])}</a:solidFill></a:ln>)
|
|
57
|
+
else
|
|
58
|
+
""
|
|
59
|
+
end
|
|
60
|
+
img_xfrm_xml = img[:rotation] ? %(<a:xfrm rot="#{img[:rotation].to_i}"><a:off x="0" y="0"/><a:ext cx="0" cy="0"/></a:xfrm>) : ""
|
|
61
|
+
parts << %(<xdr:spPr>#{img_xfrm_xml}<a:prstGeom prst="rect"><a:avLst/></a:prstGeom>#{img_line_xml}</xdr:spPr>)
|
|
62
|
+
parts << "</xdr:pic>"
|
|
63
|
+
parts << client_data_xml(img)
|
|
64
|
+
parts << "</xdr:twoCellAnchor>"
|
|
65
|
+
when :chart
|
|
66
|
+
chart = dp[:chart]
|
|
67
|
+
rid = "rId#{dp[:rid_index]}"
|
|
68
|
+
chart_ea_attr = chart[:edit_as] ? %( editAs="#{xml_escape(chart[:edit_as])}") : ""
|
|
69
|
+
chart_pub_attr = chart[:published] ? ' fPublished="1"' : ""
|
|
70
|
+
parts << "<xdr:twoCellAnchor#{chart_ea_attr}#{chart_pub_attr}>"
|
|
71
|
+
parts << anchor_xml("from", chart[:from_col], chart[:from_row], col_off: chart[:from_col_off] || 0, row_off: chart[:from_row_off] || 0)
|
|
72
|
+
parts << anchor_xml("to", chart[:to_col], chart[:to_row], col_off: chart[:to_col_off] || 0, row_off: chart[:to_row_off] || 0)
|
|
73
|
+
gf_macro = chart[:frame_macro] ? xml_escape(chart[:frame_macro]) : ""
|
|
74
|
+
parts << %(<xdr:graphicFrame macro="#{gf_macro}">)
|
|
75
|
+
chart_frame_name = chart[:name] || chart[:title] || "Chart"
|
|
76
|
+
chart_descr_attr = chart[:description] ? %( descr="#{xml_escape(chart[:description])}") : ""
|
|
77
|
+
chart_title_attr = chart[:frame_title] ? %( title="#{xml_escape(chart[:frame_title])}") : ""
|
|
78
|
+
chart_hidden_attr = chart[:frame_hidden] ? ' hidden="1"' : ""
|
|
79
|
+
cnv_gf_pr = if chart[:frame_no_grp]
|
|
80
|
+
'<xdr:cNvGraphicFramePr><a:graphicFrameLocks noGrp="1"/></xdr:cNvGraphicFramePr>'
|
|
81
|
+
else
|
|
82
|
+
"<xdr:cNvGraphicFramePr/>"
|
|
83
|
+
end
|
|
84
|
+
parts << %(<xdr:nvGraphicFramePr><xdr:cNvPr id="#{dp[:rid_index] + 1}" name="#{xml_escape(chart_frame_name)}"#{chart_descr_attr}#{chart_title_attr}#{chart_hidden_attr}/>#{cnv_gf_pr}</xdr:nvGraphicFramePr>)
|
|
85
|
+
parts << '<xdr:xfrm><a:off x="0" y="0"/><a:ext cx="5000000" cy="3000000"/></xdr:xfrm>'
|
|
86
|
+
parts << %(<a:graphic><a:graphicData uri="#{C_NS}"><c:chart xmlns:c="#{C_NS}" xmlns:r="#{DOC_REL_NS}" r:id="#{rid}"/></a:graphicData></a:graphic>)
|
|
87
|
+
parts << "</xdr:graphicFrame>"
|
|
88
|
+
parts << client_data_xml(chart)
|
|
89
|
+
parts << "</xdr:twoCellAnchor>"
|
|
90
|
+
when :sp
|
|
91
|
+
shape = dp[:shape]
|
|
92
|
+
shape_ea_attr = shape[:edit_as] ? %( editAs="#{xml_escape(shape[:edit_as])}") : ""
|
|
93
|
+
shape_pub_attr = shape[:published] ? ' fPublished="1"' : ""
|
|
94
|
+
parts << "<xdr:twoCellAnchor#{shape_ea_attr}#{shape_pub_attr}>"
|
|
95
|
+
parts << anchor_xml("from", shape[:from_col], shape[:from_row], col_off: shape[:from_col_off] || 0, row_off: shape[:from_row_off] || 0)
|
|
96
|
+
parts << anchor_xml("to", shape[:to_col], shape[:to_row], col_off: shape[:to_col_off] || 0, row_off: shape[:to_row_off] || 0)
|
|
97
|
+
sp_macro_attr = shape[:macro] ? %( macro="#{xml_escape(shape[:macro])}") : ""
|
|
98
|
+
sp_textlink_attr = shape[:textlink] ? %( textlink="#{xml_escape(shape[:textlink])}") : ""
|
|
99
|
+
parts << "<xdr:sp#{sp_macro_attr}#{sp_textlink_attr}>"
|
|
100
|
+
shape_descr_attr = shape[:description] ? %( descr="#{xml_escape(shape[:description])}") : ""
|
|
101
|
+
shape_title_attr = shape[:title] ? %( title="#{xml_escape(shape[:title])}") : ""
|
|
102
|
+
shape_hidden_attr = shape[:hidden] ? ' hidden="1"' : ""
|
|
103
|
+
sp_lock_attrs = +""
|
|
104
|
+
sp_lock_attrs << ' noGrp="1"' if shape[:no_grp]
|
|
105
|
+
sp_lock_attrs << ' noRot="1"' if shape[:no_rot]
|
|
106
|
+
sp_lock_attrs << ' fLocksText="1"' if shape[:f_locks_text]
|
|
107
|
+
cnv_sp_pr = if sp_lock_attrs.empty?
|
|
108
|
+
"<xdr:cNvSpPr/>"
|
|
109
|
+
else
|
|
110
|
+
"<xdr:cNvSpPr><a:spLocks#{sp_lock_attrs}/></xdr:cNvSpPr>"
|
|
111
|
+
end
|
|
112
|
+
parts << %(<xdr:nvSpPr><xdr:cNvPr id="#{dp[:id]}" name="#{xml_escape(shape[:name])}"#{shape_descr_attr}#{shape_title_attr}#{shape_hidden_attr}/>#{cnv_sp_pr}</xdr:nvSpPr>)
|
|
113
|
+
shape_fill_xml = if shape[:no_fill]
|
|
114
|
+
"<a:noFill/>"
|
|
115
|
+
elsif shape[:fill_color]
|
|
116
|
+
"<a:solidFill>#{srgb_clr_xml(shape[:fill_color], alpha: shape[:fill_alpha], transforms: shape[:fill_color_transforms])}</a:solidFill>"
|
|
117
|
+
elsif shape[:gradient_fill]
|
|
118
|
+
gf = shape[:gradient_fill]
|
|
119
|
+
gf_attrs = +""
|
|
120
|
+
gf_attrs << %( rotWithShape="#{gf[:rot_with_shape] ? 1 : 0}") unless gf[:rot_with_shape].nil?
|
|
121
|
+
gf_attrs << %( flip="#{xml_escape(gf[:flip])}") if gf[:flip]
|
|
122
|
+
gs_xml = (gf[:stops] || []).map { |gs| %(<a:gs pos="#{gs[:pos]}">#{color_xml(gs[:color])}</a:gs>) }.join
|
|
123
|
+
type_xml = if gf[:path]
|
|
124
|
+
%(<a:path path="#{xml_escape(gf[:path])}"/>)
|
|
125
|
+
elsif gf[:angle]
|
|
126
|
+
scaled_attr = gf[:scaled] ? %( scaled="1") : %( scaled="0")
|
|
127
|
+
%(<a:lin ang="#{gf[:angle]}"#{scaled_attr}/>)
|
|
128
|
+
else
|
|
129
|
+
""
|
|
130
|
+
end
|
|
131
|
+
tile_xml = gf[:tile_rect] ? %(<a:tileRect#{gf[:tile_rect].map { |k, v| %( #{k}="#{v}") }.join}/>) : ""
|
|
132
|
+
"<a:gradFill#{gf_attrs}><a:gsLst>#{gs_xml}</a:gsLst>#{type_xml}#{tile_xml}</a:gradFill>"
|
|
133
|
+
elsif shape[:pattern_fill]
|
|
134
|
+
pf = shape[:pattern_fill]
|
|
135
|
+
pf_children = +""
|
|
136
|
+
pf_children << %(<a:fgClr>#{color_xml(pf[:fg_color])}</a:fgClr>) if pf[:fg_color]
|
|
137
|
+
pf_children << %(<a:bgClr>#{color_xml(pf[:bg_color])}</a:bgClr>) if pf[:bg_color]
|
|
138
|
+
%(<a:pattFill prst="#{xml_escape(pf[:preset])}">#{pf_children}</a:pattFill>)
|
|
139
|
+
else
|
|
140
|
+
""
|
|
141
|
+
end
|
|
142
|
+
shape_line_xml = if shape[:no_line]
|
|
143
|
+
"<a:ln><a:noFill/></a:ln>"
|
|
144
|
+
elsif shape[:line_color] || shape[:line_dash] || shape[:line_custom_dash] || shape[:head_end] || shape[:tail_end] || shape[:line_cap] || shape[:line_align] || shape[:line_compound] || shape[:line_join]
|
|
145
|
+
ln_attrs = +(shape[:line_width] ? %( w="#{shape[:line_width].to_i}") : "")
|
|
146
|
+
ln_attrs << %( cap="#{xml_escape(shape[:line_cap])}") if shape[:line_cap]
|
|
147
|
+
ln_attrs << %( algn="#{xml_escape(shape[:line_align])}") if shape[:line_align]
|
|
148
|
+
ln_attrs << %( cmpd="#{xml_escape(shape[:line_compound])}") if shape[:line_compound]
|
|
149
|
+
fill_part = if shape[:line_color]
|
|
150
|
+
"<a:solidFill>#{srgb_clr_xml(shape[:line_color], alpha: shape[:line_alpha], transforms: shape[:line_color_transforms])}</a:solidFill>"
|
|
151
|
+
else
|
|
152
|
+
""
|
|
153
|
+
end
|
|
154
|
+
dash_part = if shape[:line_custom_dash]
|
|
155
|
+
ds_xml = shape[:line_custom_dash].map { |ds| %(<a:ds d="#{ds[:d]}" sp="#{ds[:sp]}"/>) }.join
|
|
156
|
+
"<a:custDash>#{ds_xml}</a:custDash>"
|
|
157
|
+
elsif shape[:line_dash]
|
|
158
|
+
%(<a:prstDash val="#{xml_escape(shape[:line_dash])}"/>)
|
|
159
|
+
else
|
|
160
|
+
""
|
|
161
|
+
end
|
|
162
|
+
head_part = build_line_end_xml("headEnd", shape[:head_end])
|
|
163
|
+
tail_part = build_line_end_xml("tailEnd", shape[:tail_end])
|
|
164
|
+
join_part = case shape[:line_join]
|
|
165
|
+
when "round" then "<a:round/>"
|
|
166
|
+
when "bevel" then "<a:bevel/>"
|
|
167
|
+
when "miter"
|
|
168
|
+
shape[:line_miter_limit] ? "<a:miter lim=\"#{shape[:line_miter_limit].to_i}\"/>" : "<a:miter/>"
|
|
169
|
+
else ""
|
|
170
|
+
end
|
|
171
|
+
%(<a:ln#{ln_attrs}>#{fill_part}#{dash_part}#{head_part}#{tail_part}#{join_part}</a:ln>)
|
|
172
|
+
else
|
|
173
|
+
""
|
|
174
|
+
end
|
|
175
|
+
shape_xfrm_xml = shape[:rotation] ? %(<a:xfrm rot="#{shape[:rotation].to_i}"><a:off x="0" y="0"/><a:ext cx="0" cy="0"/></a:xfrm>) : ""
|
|
176
|
+
av_lst_xml = if shape[:adjust_values]&.any?
|
|
177
|
+
gds = shape[:adjust_values].map { |gd| %(<a:gd name="#{xml_escape(gd[:name])}" fmla="#{xml_escape(gd[:fmla])}"/>) }
|
|
178
|
+
"<a:avLst>#{gds.join}</a:avLst>"
|
|
179
|
+
else
|
|
180
|
+
"<a:avLst/>"
|
|
181
|
+
end
|
|
182
|
+
effect_lst_xml = if shape[:outer_shadow] || shape[:inner_shadow] || shape[:glow] || shape[:soft_edge] || shape[:reflection] || shape[:blur]
|
|
183
|
+
effect_children = +""
|
|
184
|
+
if shape[:outer_shadow]
|
|
185
|
+
os = shape[:outer_shadow]
|
|
186
|
+
os_attrs = +""
|
|
187
|
+
os_attrs << %( blurRad="#{os[:blur_rad]}") if os[:blur_rad]
|
|
188
|
+
os_attrs << %( dist="#{os[:dist]}") if os[:dist]
|
|
189
|
+
os_attrs << %( dir="#{os[:dir]}") if os[:dir]
|
|
190
|
+
os_attrs << %( algn="#{xml_escape(os[:algn])}") if os[:algn]
|
|
191
|
+
os_attrs << %( rotWithShape="#{os[:rot_with_shape] ? 1 : 0}") unless os[:rot_with_shape].nil?
|
|
192
|
+
os_color = os[:color] ? srgb_clr_xml(os[:color], alpha: os[:alpha]) : ""
|
|
193
|
+
effect_children << "<a:outerShdw#{os_attrs}>#{os_color}</a:outerShdw>"
|
|
194
|
+
end
|
|
195
|
+
if shape[:glow]
|
|
196
|
+
gl = shape[:glow]
|
|
197
|
+
gl_attrs = +""
|
|
198
|
+
gl_attrs << %( rad="#{gl[:rad]}") if gl[:rad]
|
|
199
|
+
gl_color = gl[:color] ? srgb_clr_xml(gl[:color], alpha: gl[:alpha]) : ""
|
|
200
|
+
effect_children << "<a:glow#{gl_attrs}>#{gl_color}</a:glow>"
|
|
201
|
+
end
|
|
202
|
+
if shape[:inner_shadow]
|
|
203
|
+
is = shape[:inner_shadow]
|
|
204
|
+
is_attrs = +""
|
|
205
|
+
is_attrs << %( blurRad="#{is[:blur_rad]}") if is[:blur_rad]
|
|
206
|
+
is_attrs << %( dist="#{is[:dist]}") if is[:dist]
|
|
207
|
+
is_attrs << %( dir="#{is[:dir]}") if is[:dir]
|
|
208
|
+
is_color = is[:color] ? srgb_clr_xml(is[:color], alpha: is[:alpha]) : ""
|
|
209
|
+
effect_children << "<a:innerShdw#{is_attrs}>#{is_color}</a:innerShdw>"
|
|
210
|
+
end
|
|
211
|
+
if shape[:reflection]
|
|
212
|
+
rf = shape[:reflection]
|
|
213
|
+
rf_attrs = +""
|
|
214
|
+
rf_attrs << %( blurRad="#{rf[:blur_rad]}") if rf[:blur_rad]
|
|
215
|
+
rf_attrs << %( stA="#{rf[:st_a]}") if rf[:st_a]
|
|
216
|
+
rf_attrs << %( endA="#{rf[:end_a]}") if rf[:end_a]
|
|
217
|
+
rf_attrs << %( dist="#{rf[:dist]}") unless rf[:dist].nil?
|
|
218
|
+
rf_attrs << %( dir="#{rf[:dir]}") if rf[:dir]
|
|
219
|
+
rf_attrs << %( fadeDir="#{rf[:fade_dir]}") if rf[:fade_dir]
|
|
220
|
+
rf_attrs << %( sx="#{rf[:sx]}") if rf[:sx]
|
|
221
|
+
rf_attrs << %( sy="#{rf[:sy]}") if rf[:sy]
|
|
222
|
+
rf_attrs << %( kx="#{rf[:kx]}") if rf[:kx]
|
|
223
|
+
rf_attrs << %( ky="#{rf[:ky]}") if rf[:ky]
|
|
224
|
+
rf_attrs << %( algn="#{xml_escape(rf[:algn])}") if rf[:algn]
|
|
225
|
+
rf_attrs << %( rotWithShape="#{rf[:rot_with_shape] ? 1 : 0}") unless rf[:rot_with_shape].nil?
|
|
226
|
+
effect_children << "<a:reflection#{rf_attrs}/>"
|
|
227
|
+
end
|
|
228
|
+
if shape[:soft_edge]
|
|
229
|
+
se = shape[:soft_edge]
|
|
230
|
+
effect_children << %(<a:softEdge rad="#{se[:rad]}"/>)
|
|
231
|
+
end
|
|
232
|
+
if shape[:blur]
|
|
233
|
+
bl = shape[:blur]
|
|
234
|
+
bl_attrs = +""
|
|
235
|
+
bl_attrs << %( rad="#{bl[:rad]}") if bl[:rad]
|
|
236
|
+
bl_attrs << %( grow="#{bl[:grow] ? 1 : 0}") unless bl[:grow].nil?
|
|
237
|
+
effect_children << "<a:blur#{bl_attrs}/>"
|
|
238
|
+
end
|
|
239
|
+
"<a:effectLst>#{effect_children}</a:effectLst>"
|
|
240
|
+
else
|
|
241
|
+
""
|
|
242
|
+
end
|
|
243
|
+
parts << %(<xdr:spPr>#{shape_xfrm_xml}<a:prstGeom prst="#{xml_escape(shape[:preset])}">#{av_lst_xml}</a:prstGeom>#{shape_fill_xml}#{shape_line_xml}#{effect_lst_xml}</xdr:spPr>)
|
|
244
|
+
if shape[:text] || shape[:text_paragraphs]
|
|
245
|
+
body_pr_attrs = +""
|
|
246
|
+
body_pr_attrs << %( rot="#{shape[:text_rot]}") if shape[:text_rot]
|
|
247
|
+
body_pr_attrs << %( spcFirstLastPara="1") if shape[:text_spc_first_last_para]
|
|
248
|
+
body_pr_attrs << %( wrap="#{xml_escape(shape[:text_wrap])}") if shape[:text_wrap]
|
|
249
|
+
body_pr_attrs << %( anchor="#{xml_escape(shape[:text_anchor])}") if shape[:text_anchor]
|
|
250
|
+
body_pr_attrs << %( anchorCtr="1") if shape[:text_anchor_ctr]
|
|
251
|
+
body_pr_attrs << %( vertOverflow="#{xml_escape(shape[:text_vert_overflow])}") if shape[:text_vert_overflow]
|
|
252
|
+
body_pr_attrs << %( horzOverflow="#{xml_escape(shape[:text_horz_overflow])}") if shape[:text_horz_overflow]
|
|
253
|
+
body_pr_attrs << %( numCol="#{shape[:text_num_col]}") if shape[:text_num_col]
|
|
254
|
+
body_pr_attrs << %( spcCol="#{shape[:text_spc_col]}") if shape[:text_spc_col]
|
|
255
|
+
body_pr_attrs << %( rtlCol="1") if shape[:text_rtl_col]
|
|
256
|
+
body_pr_attrs << %( fromWordArt="1") if shape[:text_from_word_art]
|
|
257
|
+
body_pr_attrs << %( upright="1") if shape[:text_upright]
|
|
258
|
+
body_pr_attrs << %( compatLnSpc="1") if shape[:text_compat_ln_spc]
|
|
259
|
+
body_pr_attrs << %( forceAA="1") if shape[:text_force_aa]
|
|
260
|
+
body_pr_attrs << %( vert="#{xml_escape(shape[:text_vertical])}") if shape[:text_vertical]
|
|
261
|
+
if shape[:text_insets]
|
|
262
|
+
ins = shape[:text_insets]
|
|
263
|
+
body_pr_attrs << %( lIns="#{ins[:left]}") if ins[:left]
|
|
264
|
+
body_pr_attrs << %( tIns="#{ins[:top]}") if ins[:top]
|
|
265
|
+
body_pr_attrs << %( rIns="#{ins[:right]}") if ins[:right]
|
|
266
|
+
body_pr_attrs << %( bIns="#{ins[:bottom]}") if ins[:bottom]
|
|
267
|
+
end
|
|
268
|
+
body_pr_children = +""
|
|
269
|
+
if shape[:text_warp]
|
|
270
|
+
tw = shape[:text_warp]
|
|
271
|
+
body_pr_children << %(<a:prstTxWarp prst="#{xml_escape(tw[:preset])}"><a:avLst/></a:prstTxWarp>)
|
|
272
|
+
end
|
|
273
|
+
autofit_xml = case shape[:autofit]
|
|
274
|
+
when "none" then "<a:noAutofit/>"
|
|
275
|
+
when "shape" then "<a:spAutoFit/>"
|
|
276
|
+
when "normal" then "<a:normAutofit/>"
|
|
277
|
+
when Hash
|
|
278
|
+
af = shape[:autofit]
|
|
279
|
+
case af[:type]
|
|
280
|
+
when "normal"
|
|
281
|
+
na_attrs = +""
|
|
282
|
+
na_attrs << %( fontScale="#{af[:font_scale]}") if af[:font_scale]
|
|
283
|
+
na_attrs << %( lnSpcReduction="#{af[:ln_spc_reduction]}") if af[:ln_spc_reduction]
|
|
284
|
+
"<a:normAutofit#{na_attrs}/>"
|
|
285
|
+
when "none" then "<a:noAutofit/>"
|
|
286
|
+
when "shape" then "<a:spAutoFit/>"
|
|
287
|
+
else ""
|
|
288
|
+
end
|
|
289
|
+
else ""
|
|
290
|
+
end
|
|
291
|
+
body_pr_children << autofit_xml
|
|
292
|
+
body_pr_xml = if body_pr_children.empty?
|
|
293
|
+
"<a:bodyPr#{body_pr_attrs}/>"
|
|
294
|
+
else
|
|
295
|
+
"<a:bodyPr#{body_pr_attrs}>#{body_pr_children}</a:bodyPr>"
|
|
296
|
+
end
|
|
297
|
+
parts << "<xdr:txBody>#{body_pr_xml}<a:lstStyle/>"
|
|
298
|
+
if shape[:text_paragraphs]
|
|
299
|
+
shape[:text_paragraphs].each { |para| parts << paragraph_xml(para) }
|
|
300
|
+
elsif shape[:text]
|
|
301
|
+
parts << paragraph_xml(
|
|
302
|
+
text: shape[:text], font: shape[:text_font],
|
|
303
|
+
align: shape[:text_align], font_align: shape[:text_font_align],
|
|
304
|
+
def_tab_sz: shape[:text_def_tab_sz], rtl: shape[:text_rtl],
|
|
305
|
+
ea_ln_brk: shape[:text_ea_ln_brk], latin_ln_brk: shape[:text_latin_ln_brk],
|
|
306
|
+
hanging_punct: shape[:text_hanging_punct], level: shape[:text_level],
|
|
307
|
+
indent: shape[:text_indent], spacing: shape[:text_spacing],
|
|
308
|
+
tab_stops: shape[:text_tab_stops], bullet: shape[:text_bullet],
|
|
309
|
+
def_rpr: shape[:text_def_rpr], end_para_rpr: shape[:text_end_para_rpr]
|
|
310
|
+
)
|
|
311
|
+
end
|
|
312
|
+
parts << "</xdr:txBody>"
|
|
313
|
+
end
|
|
314
|
+
parts << "</xdr:sp>"
|
|
315
|
+
parts << client_data_xml(shape)
|
|
316
|
+
parts << "</xdr:twoCellAnchor>"
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
parts << "</xdr:wsDr>"
|
|
321
|
+
parts.join
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# : (untyped para) -> ::String
|
|
325
|
+
def paragraph_xml(para)
|
|
326
|
+
ppr_attrs = +""
|
|
327
|
+
ppr_attrs << %( algn="#{xml_escape(para[:align])}") if para[:align]
|
|
328
|
+
ppr_attrs << %( fontAlgn="#{xml_escape(para[:font_align])}") if para[:font_align]
|
|
329
|
+
ppr_attrs << %( defTabSz="#{para[:def_tab_sz]}") if para[:def_tab_sz]
|
|
330
|
+
ppr_attrs << %( rtl="1") if para[:rtl]
|
|
331
|
+
ppr_attrs << %( eaLnBrk="1") if para[:ea_ln_brk]
|
|
332
|
+
ppr_attrs << %( latinLnBrk="1") if para[:latin_ln_brk]
|
|
333
|
+
ppr_attrs << %( hangingPunct="1") if para[:hanging_punct]
|
|
334
|
+
ppr_attrs << %( lvl="#{para[:level]}") if para[:level]
|
|
335
|
+
if para[:indent]
|
|
336
|
+
ti = para[:indent]
|
|
337
|
+
ppr_attrs << %( marL="#{ti[:left]}") if ti[:left]
|
|
338
|
+
ppr_attrs << %( marR="#{ti[:right]}") if ti[:right]
|
|
339
|
+
ppr_attrs << %( indent="#{ti[:indent]}") if ti[:indent]
|
|
340
|
+
end
|
|
341
|
+
ppr_children = +""
|
|
342
|
+
if para[:spacing]
|
|
343
|
+
ts = para[:spacing]
|
|
344
|
+
if ts[:line]
|
|
345
|
+
ppr_children << %(<a:lnSpc><a:spcPts val="#{ts[:line]}"/></a:lnSpc>)
|
|
346
|
+
elsif ts[:line_pct]
|
|
347
|
+
ppr_children << %(<a:lnSpc><a:spcPct val="#{ts[:line_pct]}"/></a:lnSpc>)
|
|
348
|
+
end
|
|
349
|
+
if ts[:before]
|
|
350
|
+
ppr_children << %(<a:spcBef><a:spcPts val="#{ts[:before]}"/></a:spcBef>)
|
|
351
|
+
elsif ts[:before_pct]
|
|
352
|
+
ppr_children << %(<a:spcBef><a:spcPct val="#{ts[:before_pct]}"/></a:spcBef>)
|
|
353
|
+
end
|
|
354
|
+
if ts[:after]
|
|
355
|
+
ppr_children << %(<a:spcAft><a:spcPts val="#{ts[:after]}"/></a:spcAft>)
|
|
356
|
+
elsif ts[:after_pct]
|
|
357
|
+
ppr_children << %(<a:spcAft><a:spcPct val="#{ts[:after_pct]}"/></a:spcAft>)
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
if para[:tab_stops]&.any?
|
|
361
|
+
tabs = para[:tab_stops].map do |tab|
|
|
362
|
+
tab_attrs = %( pos="#{tab[:pos]}")
|
|
363
|
+
tab_attrs << %( algn="#{xml_escape(tab[:align])}") if tab[:align]
|
|
364
|
+
"<a:tab#{tab_attrs}/>"
|
|
365
|
+
end
|
|
366
|
+
ppr_children << "<a:tabLst>#{tabs.join}</a:tabLst>"
|
|
367
|
+
end
|
|
368
|
+
if para[:bullet]
|
|
369
|
+
bu = para[:bullet]
|
|
370
|
+
ppr_children << %(<a:buClr>#{color_xml(bu[:color])}</a:buClr>) if bu[:color]
|
|
371
|
+
ppr_children << %(<a:buSzPts val="#{bu[:size_pts]}"/>) if bu[:size_pts]
|
|
372
|
+
ppr_children << %(<a:buSzPct val="#{bu[:size_pct]}"/>) if bu[:size_pct]
|
|
373
|
+
ppr_children << %(<a:buFont typeface="#{xml_escape(bu[:font])}"/>) if bu[:font]
|
|
374
|
+
case bu[:type]
|
|
375
|
+
when "none"
|
|
376
|
+
ppr_children << "<a:buNone/>"
|
|
377
|
+
when "char"
|
|
378
|
+
ppr_children << %(<a:buChar char="#{xml_escape(bu[:char])}"/>)
|
|
379
|
+
when "auto"
|
|
380
|
+
auto_attrs = %( type="#{xml_escape(bu[:auto_type])}")
|
|
381
|
+
auto_attrs << %( startAt="#{bu[:start_at]}") if bu[:start_at]
|
|
382
|
+
ppr_children << "<a:buAutoNum#{auto_attrs}/>"
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
ppr_children << text_char_props_xml("a:defRPr", para[:def_rpr]) if para[:def_rpr]
|
|
386
|
+
ppr_xml = if ppr_attrs.empty? && ppr_children.empty?
|
|
387
|
+
""
|
|
388
|
+
elsif ppr_children.empty?
|
|
389
|
+
"<a:pPr#{ppr_attrs}/>"
|
|
390
|
+
else
|
|
391
|
+
"<a:pPr#{ppr_attrs}>#{ppr_children}</a:pPr>"
|
|
392
|
+
end
|
|
393
|
+
end_para_rpr_xml = para[:end_para_rpr] ? text_char_props_xml("a:endParaRPr", para[:end_para_rpr]) : ""
|
|
394
|
+
runs_xml = if para[:runs]
|
|
395
|
+
para[:runs].map do |run|
|
|
396
|
+
run_rpr = run[:font] ? text_char_props_xml("a:rPr", run[:font]) : ""
|
|
397
|
+
"<a:r>#{run_rpr}<a:t>#{xml_escape(run[:text] || "")}</a:t></a:r>"
|
|
398
|
+
end.join
|
|
399
|
+
else
|
|
400
|
+
rpr_xml = para[:font] ? text_char_props_xml("a:rPr", para[:font]) : ""
|
|
401
|
+
"<a:r>#{rpr_xml}<a:t>#{xml_escape(para[:text] || "")}</a:t></a:r>"
|
|
402
|
+
end
|
|
403
|
+
"<a:p>#{ppr_xml}#{runs_xml}#{end_para_rpr_xml}</a:p>"
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# : (untyped tag, untyped font) -> untyped
|
|
407
|
+
def text_char_props_xml(tag, font)
|
|
408
|
+
attrs = +""
|
|
409
|
+
attrs << %( b="1") if font[:bold]
|
|
410
|
+
attrs << %( i="1") if font[:italic]
|
|
411
|
+
attrs << %( noProof="1") if font[:no_proof]
|
|
412
|
+
attrs << %( normalizeH="1") if font[:normalize_h]
|
|
413
|
+
attrs << %( kumimoji="1") if font[:kumimoji]
|
|
414
|
+
attrs << %( sz="#{font[:size]}") if font[:size]
|
|
415
|
+
attrs << %( strike="#{xml_escape(font[:strike])}") if font[:strike]
|
|
416
|
+
attrs << %( u="#{xml_escape(font[:underline])}") if font[:underline]
|
|
417
|
+
attrs << %( baseline="#{font[:baseline]}") if font[:baseline]
|
|
418
|
+
attrs << %( spc="#{font[:spacing]}") if font[:spacing]
|
|
419
|
+
attrs << %( kern="#{font[:kern]}") if font[:kern]
|
|
420
|
+
attrs << %( cap="#{xml_escape(font[:cap])}") if font[:cap]
|
|
421
|
+
attrs << %( lang="#{xml_escape(font[:lang])}") if font[:lang]
|
|
422
|
+
attrs << %( altLang="#{xml_escape(font[:alt_lang])}") if font[:alt_lang]
|
|
423
|
+
attrs << %( dirty="1") if font[:dirty]
|
|
424
|
+
attrs << %( smtClean="1") if font[:smt_clean]
|
|
425
|
+
attrs << %( err="1") if font[:err]
|
|
426
|
+
attrs << %( bmk="#{xml_escape(font[:bmk])}") if font[:bmk]
|
|
427
|
+
children = +""
|
|
428
|
+
children << %(<a:solidFill>#{color_xml(font[:color])}</a:solidFill>) if font[:color]
|
|
429
|
+
children << %(<a:highlight>#{color_xml(font[:highlight])}</a:highlight>) if font[:highlight]
|
|
430
|
+
children << %(<a:latin typeface="#{xml_escape(font[:name])}"/>) if font[:name]
|
|
431
|
+
children << %(<a:ea typeface="#{xml_escape(font[:ea_font])}"/>) if font[:ea_font]
|
|
432
|
+
children << %(<a:cs typeface="#{xml_escape(font[:cs_font])}"/>) if font[:cs_font]
|
|
433
|
+
children << %(<a:sym typeface="#{xml_escape(font[:sym_font])}"/>) if font[:sym_font]
|
|
434
|
+
children << "<a:uFillTx/>" if font[:u_fill_tx]
|
|
435
|
+
children << "<a:uLnTx/>" if font[:u_ln_tx]
|
|
436
|
+
if font[:line_color] || font[:line_width] || font[:line_dash] || font[:line_cap] || font[:line_join]
|
|
437
|
+
ln_attrs = +(font[:line_width] ? %( w="#{font[:line_width].to_i}") : "")
|
|
438
|
+
ln_attrs << %( cap="#{xml_escape(font[:line_cap])}") if font[:line_cap]
|
|
439
|
+
ln_fill = font[:line_color] ? %(<a:solidFill>#{color_xml(font[:line_color])}</a:solidFill>) : ""
|
|
440
|
+
ln_dash = font[:line_dash] ? %(<a:prstDash val="#{xml_escape(font[:line_dash])}"/>) : ""
|
|
441
|
+
ln_join = case font[:line_join]
|
|
442
|
+
when "round" then "<a:round/>"
|
|
443
|
+
when "bevel" then "<a:bevel/>"
|
|
444
|
+
when "miter"
|
|
445
|
+
font[:line_miter_limit] ? "<a:miter lim=\"#{font[:line_miter_limit].to_i}\"/>" : "<a:miter/>"
|
|
446
|
+
else ""
|
|
447
|
+
end
|
|
448
|
+
children << "<a:ln#{ln_attrs}>#{ln_fill}#{ln_dash}#{ln_join}</a:ln>"
|
|
449
|
+
end
|
|
450
|
+
if font[:outer_shadow] || font[:inner_shadow] || font[:glow] || font[:soft_edge] || font[:reflection] || font[:blur]
|
|
451
|
+
eff = +""
|
|
452
|
+
if font[:outer_shadow]
|
|
453
|
+
os = font[:outer_shadow]
|
|
454
|
+
oa = +""
|
|
455
|
+
oa << %( blurRad="#{os[:blur_rad]}") if os[:blur_rad]
|
|
456
|
+
oa << %( dist="#{os[:dist]}") if os[:dist]
|
|
457
|
+
oa << %( dir="#{os[:dir]}") if os[:dir]
|
|
458
|
+
oa << %( algn="#{xml_escape(os[:algn])}") if os[:algn]
|
|
459
|
+
oa << %( rotWithShape="#{os[:rot_with_shape] ? 1 : 0}") unless os[:rot_with_shape].nil?
|
|
460
|
+
oc = os[:color] ? srgb_clr_xml(os[:color], alpha: os[:alpha]) : ""
|
|
461
|
+
eff << "<a:outerShdw#{oa}>#{oc}</a:outerShdw>"
|
|
462
|
+
end
|
|
463
|
+
if font[:glow]
|
|
464
|
+
gl = font[:glow]
|
|
465
|
+
ga = +(gl[:rad] ? %( rad="#{gl[:rad]}") : "")
|
|
466
|
+
gc = gl[:color] ? srgb_clr_xml(gl[:color], alpha: gl[:alpha]) : ""
|
|
467
|
+
eff << "<a:glow#{ga}>#{gc}</a:glow>"
|
|
468
|
+
end
|
|
469
|
+
if font[:inner_shadow]
|
|
470
|
+
is = font[:inner_shadow]
|
|
471
|
+
ia = +""
|
|
472
|
+
ia << %( blurRad="#{is[:blur_rad]}") if is[:blur_rad]
|
|
473
|
+
ia << %( dist="#{is[:dist]}") if is[:dist]
|
|
474
|
+
ia << %( dir="#{is[:dir]}") if is[:dir]
|
|
475
|
+
ic = is[:color] ? srgb_clr_xml(is[:color], alpha: is[:alpha]) : ""
|
|
476
|
+
eff << "<a:innerShdw#{ia}>#{ic}</a:innerShdw>"
|
|
477
|
+
end
|
|
478
|
+
if font[:reflection]
|
|
479
|
+
rf = font[:reflection]
|
|
480
|
+
ra = +""
|
|
481
|
+
ra << %( blurRad="#{rf[:blur_rad]}") if rf[:blur_rad]
|
|
482
|
+
ra << %( stA="#{rf[:st_a]}") if rf[:st_a]
|
|
483
|
+
ra << %( endA="#{rf[:end_a]}") if rf[:end_a]
|
|
484
|
+
ra << %( dist="#{rf[:dist]}") unless rf[:dist].nil?
|
|
485
|
+
ra << %( dir="#{rf[:dir]}") if rf[:dir]
|
|
486
|
+
ra << %( fadeDir="#{rf[:fade_dir]}") if rf[:fade_dir]
|
|
487
|
+
ra << %( sx="#{rf[:sx]}") if rf[:sx]
|
|
488
|
+
ra << %( sy="#{rf[:sy]}") if rf[:sy]
|
|
489
|
+
ra << %( kx="#{rf[:kx]}") if rf[:kx]
|
|
490
|
+
ra << %( ky="#{rf[:ky]}") if rf[:ky]
|
|
491
|
+
ra << %( algn="#{xml_escape(rf[:algn])}") if rf[:algn]
|
|
492
|
+
ra << %( rotWithShape="#{rf[:rot_with_shape] ? 1 : 0}") unless rf[:rot_with_shape].nil?
|
|
493
|
+
eff << "<a:reflection#{ra}/>"
|
|
494
|
+
end
|
|
495
|
+
if font[:soft_edge]
|
|
496
|
+
se = font[:soft_edge]
|
|
497
|
+
eff << %(<a:softEdge rad="#{se[:rad]}"/>)
|
|
498
|
+
end
|
|
499
|
+
if font[:blur]
|
|
500
|
+
bl = font[:blur]
|
|
501
|
+
ba = +(bl[:rad] ? %( rad="#{bl[:rad]}") : "")
|
|
502
|
+
ba << %( grow="#{bl[:grow] ? 1 : 0}") unless bl[:grow].nil?
|
|
503
|
+
eff << "<a:blur#{ba}/>"
|
|
504
|
+
end
|
|
505
|
+
children << "<a:effectLst>#{eff}</a:effectLst>"
|
|
506
|
+
end
|
|
507
|
+
if children.empty?
|
|
508
|
+
"<#{tag}#{attrs}/>"
|
|
509
|
+
else
|
|
510
|
+
"<#{tag}#{attrs}>#{children}</#{tag}>"
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
# : (untyped tag, untyped col, untyped row, ?col_off: ::Integer, ?row_off: ::Integer) -> ::String
|
|
515
|
+
def anchor_xml(tag, col, row, col_off: 0, row_off: 0)
|
|
516
|
+
"<xdr:#{tag}><xdr:col>#{col}</xdr:col><xdr:colOff>#{col_off}</xdr:colOff><xdr:row>#{row}</xdr:row><xdr:rowOff>#{row_off}</xdr:rowOff></xdr:#{tag}>"
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
# : (untyped obj) -> ::String
|
|
520
|
+
def client_data_xml(obj)
|
|
521
|
+
cd_attrs = +""
|
|
522
|
+
cd_attrs << ' fLocksWithSheet="0"' if obj[:locks_with_sheet] == false
|
|
523
|
+
cd_attrs << ' fLocksWithSheet="1"' if obj[:locks_with_sheet] == true
|
|
524
|
+
cd_attrs << ' fPrintsWithSheet="0"' if obj[:prints_with_sheet] == false
|
|
525
|
+
cd_attrs << ' fPrintsWithSheet="1"' if obj[:prints_with_sheet] == true
|
|
526
|
+
"<xdr:clientData#{cd_attrs}/>"
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
# : (untyped rels_data) -> untyped
|
|
530
|
+
def generate_drawing_rels(rels_data)
|
|
531
|
+
parts = [XML_HEADER, %(<Relationships xmlns="#{REL_NS}">)]
|
|
532
|
+
rels_data.each_with_index do |rel, i|
|
|
533
|
+
rel_type = case rel[:type]
|
|
534
|
+
when :image then "#{DOC_REL_NS}/image"
|
|
535
|
+
when :chart then "#{DOC_REL_NS}/chart"
|
|
536
|
+
end
|
|
537
|
+
parts << %(<Relationship Id="rId#{i + 1}" Type="#{rel_type}" Target="#{rel[:target]}"/>)
|
|
538
|
+
end
|
|
539
|
+
parts << "</Relationships>"
|
|
540
|
+
parts.join
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
# : (untyped title_spec, ?overlay: bool) -> untyped
|
|
544
|
+
def build_chart_title_xml(title_spec, overlay: false)
|
|
545
|
+
overlay_val = overlay ? 1 : 0
|
|
546
|
+
if title_spec.is_a?(Hash)
|
|
547
|
+
text = xml_escape(title_spec[:text].to_s)
|
|
548
|
+
rotation = title_spec[:rotation]
|
|
549
|
+
body_pr = rotation ? %(<a:bodyPr rot="#{rotation}"/>) : "<a:bodyPr/>"
|
|
550
|
+
font = title_spec[:font] || {}
|
|
551
|
+
rpr_attrs = +""
|
|
552
|
+
rpr_attrs << %( b="1") if font[:bold]
|
|
553
|
+
rpr_attrs << %( i="1") if font[:italic]
|
|
554
|
+
rpr_attrs << %( sz="#{font[:size]}") if font[:size]
|
|
555
|
+
rpr_children = +""
|
|
556
|
+
rpr_children << %(<a:solidFill>#{color_xml(font[:color])}</a:solidFill>) if font[:color]
|
|
557
|
+
rpr_children << %(<a:latin typeface="#{xml_escape(font[:name])}"/>) if font[:name]
|
|
558
|
+
rpr_xml = if rpr_children.empty?
|
|
559
|
+
"<a:rPr#{rpr_attrs}/>"
|
|
560
|
+
else
|
|
561
|
+
"<a:rPr#{rpr_attrs}>#{rpr_children}</a:rPr>"
|
|
562
|
+
end
|
|
563
|
+
layout_xml = build_title_layout_xml(title_spec[:layout])
|
|
564
|
+
sp_xml = build_title_sp_pr(title_spec)
|
|
565
|
+
"<c:title><c:tx><c:rich>#{body_pr}<a:lstStyle/><a:p><a:r>#{rpr_xml}<a:t>#{text}</a:t></a:r></a:p></c:rich></c:tx>#{layout_xml}<c:overlay val=\"#{overlay_val}\"/>#{sp_xml}</c:title>"
|
|
566
|
+
else
|
|
567
|
+
"<c:title><c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>#{xml_escape(title_spec)}</a:t></a:r></a:p></c:rich></c:tx><c:overlay val=\"#{overlay_val}\"/></c:title>"
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
# : (untyped title_spec, untyped chart, untyped prefix) -> untyped
|
|
572
|
+
def merge_flat_title_styling(title_spec, chart, prefix)
|
|
573
|
+
font_key = :"#{prefix}_font"
|
|
574
|
+
fill_key = :"#{prefix}_fill"
|
|
575
|
+
no_fill_key = :"#{prefix}_no_fill"
|
|
576
|
+
lc_key = :"#{prefix}_line_color"
|
|
577
|
+
lw_key = :"#{prefix}_line_width"
|
|
578
|
+
ld_key = :"#{prefix}_line_dash"
|
|
579
|
+
rot_key = :"#{prefix}_rotation"
|
|
580
|
+
return title_spec unless chart[font_key] || chart[fill_key] || chart[no_fill_key] ||
|
|
581
|
+
chart[lc_key] || chart[lw_key] || chart[ld_key] || chart[rot_key]
|
|
582
|
+
|
|
583
|
+
spec = title_spec.is_a?(Hash) ? title_spec : { text: title_spec }
|
|
584
|
+
spec[:font] ||= chart[font_key] if chart[font_key]
|
|
585
|
+
spec[:fill_color] ||= chart[fill_key] if chart[fill_key]
|
|
586
|
+
spec[:no_fill] = chart[no_fill_key] if spec[:no_fill].nil? && !chart[no_fill_key].nil?
|
|
587
|
+
spec[:line_color] ||= chart[lc_key] if chart[lc_key]
|
|
588
|
+
spec[:line_width] ||= chart[lw_key] if chart[lw_key]
|
|
589
|
+
spec[:line_dash] ||= chart[ld_key] if chart[ld_key]
|
|
590
|
+
spec[:rotation] ||= chart[rot_key] if chart[rot_key]
|
|
591
|
+
spec
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# : (untyped spec) -> untyped
|
|
595
|
+
def build_title_sp_pr(spec)
|
|
596
|
+
children = +""
|
|
597
|
+
children << %(<a:solidFill>#{color_xml(spec[:fill_color])}</a:solidFill>) if spec[:fill_color]
|
|
598
|
+
children << "<a:noFill/>" if spec[:no_fill]
|
|
599
|
+
if spec[:line_color] || spec[:line_width] || spec[:line_dash]
|
|
600
|
+
lw = spec[:line_width] ? %( w="#{(spec[:line_width] * 12_700).to_i}") : ""
|
|
601
|
+
lf = spec[:line_color] ? %(<a:solidFill>#{color_xml(spec[:line_color])}</a:solidFill>) : ""
|
|
602
|
+
ld = spec[:line_dash] ? %(<a:prstDash val="#{xml_escape(spec[:line_dash])}"/>) : ""
|
|
603
|
+
children << "<a:ln#{lw}>#{lf}#{ld}</a:ln>"
|
|
604
|
+
end
|
|
605
|
+
children.empty? ? "" : "<c:spPr>#{children}</c:spPr>"
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
# : (untyped layout) -> ("" | untyped)
|
|
609
|
+
def build_title_layout_xml(layout)
|
|
610
|
+
return "" unless layout.is_a?(Hash)
|
|
611
|
+
|
|
612
|
+
ml = +""
|
|
613
|
+
ml << %(<c:x val="#{layout[:x]}"/>) if layout[:x]
|
|
614
|
+
ml << %(<c:y val="#{layout[:y]}"/>) if layout[:y]
|
|
615
|
+
ml << %(<c:w val="#{layout[:w]}"/>) if layout[:w]
|
|
616
|
+
ml << %(<c:h val="#{layout[:h]}"/>) if layout[:h]
|
|
617
|
+
ml.empty? ? "" : "<c:layout><c:manualLayout>#{ml}</c:manualLayout></c:layout>"
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
# : (untyped tag, untyped spec) -> ("" | untyped)
|
|
621
|
+
def gridlines_xml(tag, spec)
|
|
622
|
+
return "" unless spec
|
|
623
|
+
|
|
624
|
+
if spec.is_a?(Hash)
|
|
625
|
+
sp_children = +""
|
|
626
|
+
if spec[:line_color] || spec[:line_width] || spec[:line_dash]
|
|
627
|
+
lw = spec[:line_width] ? %( w="#{(spec[:line_width] * 12_700).to_i}") : ""
|
|
628
|
+
lf = spec[:line_color] ? %(<a:solidFill>#{color_xml(spec[:line_color])}</a:solidFill>) : ""
|
|
629
|
+
ld = spec[:line_dash] ? %(<a:prstDash val="#{xml_escape(spec[:line_dash])}"/>) : ""
|
|
630
|
+
sp_children << "<a:ln#{lw}>#{lf}#{ld}</a:ln>"
|
|
631
|
+
end
|
|
632
|
+
sp_children.empty? ? "<c:#{tag}/>" : "<c:#{tag}><c:spPr>#{sp_children}</c:spPr></c:#{tag}>"
|
|
633
|
+
else
|
|
634
|
+
"<c:#{tag}/>"
|
|
635
|
+
end
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
# Resolves a cell reference like "Sheet1!$A$1:$A$5" to an array of values from @sheets.
|
|
639
|
+
# : (untyped ref) -> (nil | untyped)
|
|
640
|
+
def resolve_sheet_ref(ref)
|
|
641
|
+
return nil unless ref
|
|
642
|
+
|
|
643
|
+
m = if ref.start_with?("'")
|
|
644
|
+
ref.match(/\A'([^']+)'!(.+)\z/)
|
|
645
|
+
|
|
646
|
+
else
|
|
647
|
+
ref.match(/\A([^!]+)!(.+)\z/)
|
|
648
|
+
|
|
649
|
+
end
|
|
650
|
+
return nil unless m
|
|
651
|
+
|
|
652
|
+
sheet_name = m[1]
|
|
653
|
+
range_part = m[2]
|
|
654
|
+
return nil unless @sheets.key?(sheet_name)
|
|
655
|
+
|
|
656
|
+
range_part = range_part.delete("$")
|
|
657
|
+
cells = if range_part.include?(":")
|
|
658
|
+
start_cell, end_cell = range_part.split(":", 2)
|
|
659
|
+
enumerate_cell_range(start_cell, end_cell)
|
|
660
|
+
else
|
|
661
|
+
[range_part]
|
|
662
|
+
end
|
|
663
|
+
cells.map { |addr| @sheets[sheet_name][addr] }
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
# : (untyped start_cell, untyped end_cell) -> (::Array[untyped] | untyped)
|
|
667
|
+
def enumerate_cell_range(start_cell, end_cell)
|
|
668
|
+
sc = start_cell.match(/\A([A-Z]+)(\d+)\z/)
|
|
669
|
+
ec = end_cell.match(/\A([A-Z]+)(\d+)\z/)
|
|
670
|
+
return [start_cell] unless sc && ec
|
|
671
|
+
|
|
672
|
+
start_col = column_letter_to_index(sc[1])
|
|
673
|
+
start_row = sc[2].to_i
|
|
674
|
+
end_col = column_letter_to_index(ec[1])
|
|
675
|
+
end_row = ec[2].to_i
|
|
676
|
+
cells = []
|
|
677
|
+
(start_row..end_row).each do |row|
|
|
678
|
+
(start_col..end_col).each do |col|
|
|
679
|
+
cells << "#{index_to_column_letter(col)}#{row}"
|
|
680
|
+
end
|
|
681
|
+
end
|
|
682
|
+
cells
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
# : (untyped ref) -> ("" | ::String)
|
|
686
|
+
def num_cache_xml(ref)
|
|
687
|
+
values = resolve_sheet_ref(ref)
|
|
688
|
+
return "" unless values
|
|
689
|
+
|
|
690
|
+
pts = values.each_with_index.filter_map do |v, i|
|
|
691
|
+
next unless v
|
|
692
|
+
|
|
693
|
+
%(<c:pt idx="#{i}"><c:v>#{xml_escape(v.to_s)}</c:v></c:pt>)
|
|
694
|
+
end
|
|
695
|
+
%(<c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="#{values.size}"/>#{pts.join}</c:numCache>)
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
# : (untyped ref) -> ("" | ::String)
|
|
699
|
+
def str_cache_xml(ref)
|
|
700
|
+
values = resolve_sheet_ref(ref)
|
|
701
|
+
return "" unless values
|
|
702
|
+
|
|
703
|
+
pts = values.each_with_index.filter_map do |v, i|
|
|
704
|
+
next unless v
|
|
705
|
+
|
|
706
|
+
%(<c:pt idx="#{i}"><c:v>#{xml_escape(v.to_s)}</c:v></c:pt>)
|
|
707
|
+
end
|
|
708
|
+
%(<c:strCache><c:ptCount val="#{values.size}"/>#{pts.join}</c:strCache>)
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
# : (untyped color, ?alpha: untyped?, ?transforms: untyped?) -> untyped
|
|
712
|
+
def color_xml(color, alpha: nil, transforms: nil)
|
|
713
|
+
if color.is_a?(Hash) && color[:scheme]
|
|
714
|
+
tag = "schemeClr"
|
|
715
|
+
val = xml_escape(color[:scheme])
|
|
716
|
+
transforms = color[:transforms] || transforms
|
|
717
|
+
else
|
|
718
|
+
tag = "srgbClr"
|
|
719
|
+
val = xml_escape(normalize_drawing_rgb(color))
|
|
720
|
+
end
|
|
721
|
+
children = +""
|
|
722
|
+
children << %(<a:alpha val="#{alpha}"/>) if alpha
|
|
723
|
+
(transforms || []).each do |t|
|
|
724
|
+
children << %(<a:#{xml_escape(t[:type])} val="#{t[:val]}"/>)
|
|
725
|
+
end
|
|
726
|
+
if children.empty?
|
|
727
|
+
%(<a:#{tag} val="#{val}"/>)
|
|
728
|
+
else
|
|
729
|
+
%(<a:#{tag} val="#{val}">#{children}</a:#{tag}>)
|
|
730
|
+
end
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
# : (untyped color) -> untyped
|
|
734
|
+
def normalize_drawing_rgb(color)
|
|
735
|
+
str = color.to_s
|
|
736
|
+
hex = str.delete_prefix("#")
|
|
737
|
+
|
|
738
|
+
# Common shorthand (e.g. #f00 -> FF0000)
|
|
739
|
+
return hex.chars.map { |c| c * 2 }.join.upcase if hex.match?(/\A[0-9A-Fa-f]{3}\z/)
|
|
740
|
+
|
|
741
|
+
# ARGB -> RGB for DrawingML srgbClr (expects RRGGBB)
|
|
742
|
+
return hex[-6, 6].upcase if hex.match?(/\A[0-9A-Fa-f]{8}\z/)
|
|
743
|
+
|
|
744
|
+
return hex.upcase if hex.match?(/\A[0-9A-Fa-f]{6}\z/)
|
|
745
|
+
|
|
746
|
+
str
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
alias srgb_clr_xml color_xml
|
|
750
|
+
|
|
751
|
+
# : (untyped rotation, untyped font) -> ("" | ::String)
|
|
752
|
+
def build_axis_txpr(rotation, font)
|
|
753
|
+
return "" unless rotation || font
|
|
754
|
+
|
|
755
|
+
body_attrs = rotation ? %( rot="#{rotation}") : ""
|
|
756
|
+
rpr_attrs = +""
|
|
757
|
+
rpr_children = +""
|
|
758
|
+
if font
|
|
759
|
+
rpr_attrs << %( sz="#{(font[:size] * 100).to_i}") if font[:size]
|
|
760
|
+
rpr_attrs << %( b="1") if font[:bold]
|
|
761
|
+
rpr_attrs << %( i="1") if font[:italic]
|
|
762
|
+
rpr_children << %(<a:solidFill>#{color_xml(font[:color])}</a:solidFill>) if font[:color]
|
|
763
|
+
rpr_children << %(<a:latin typeface="#{xml_escape(font[:name])}"/>) if font[:name]
|
|
764
|
+
end
|
|
765
|
+
def_rpr = if rpr_children.empty?
|
|
766
|
+
"<a:defRPr#{rpr_attrs}/>"
|
|
767
|
+
else
|
|
768
|
+
"<a:defRPr#{rpr_attrs}>#{rpr_children}</a:defRPr>"
|
|
769
|
+
end
|
|
770
|
+
%(<c:txPr><a:bodyPr#{body_attrs}/><a:lstStyle/><a:p><a:pPr>#{def_rpr}</a:pPr><a:endParaRPr/></a:p></c:txPr>)
|
|
771
|
+
end
|
|
772
|
+
|
|
773
|
+
# : (untyped chart) -> untyped
|
|
774
|
+
def generate_chart_xml(chart)
|
|
775
|
+
chart_type = CHART_TYPE_MAP[chart[:type]] || "barChart"
|
|
776
|
+
no_axes = NO_AXIS_CHARTS.include?(chart_type)
|
|
777
|
+
parts = [
|
|
778
|
+
XML_HEADER,
|
|
779
|
+
%(<c:chartSpace xmlns:c="#{C_NS}" xmlns:a="#{A_NS}" xmlns:r="#{DOC_REL_NS}">)
|
|
780
|
+
]
|
|
781
|
+
rc = chart[:rounded_corners]
|
|
782
|
+
parts << %(<c:roundedCorners val="#{rc ? 1 : 0}"/>) unless rc.nil?
|
|
783
|
+
parts << %(<c:style val="#{chart[:style]}"/>) if chart[:style]
|
|
784
|
+
if (prot = chart[:protection])
|
|
785
|
+
parts << "<c:protection>"
|
|
786
|
+
parts << %(<c:chartObject val="#{prot[:chart_object] ? 1 : 0}"/>) unless prot[:chart_object].nil?
|
|
787
|
+
parts << %(<c:data val="#{prot[:data] ? 1 : 0}"/>) unless prot[:data].nil?
|
|
788
|
+
parts << %(<c:formatting val="#{prot[:formatting] ? 1 : 0}"/>) unless prot[:formatting].nil?
|
|
789
|
+
parts << %(<c:selection val="#{prot[:selection] ? 1 : 0}"/>) unless prot[:selection].nil?
|
|
790
|
+
parts << %(<c:userInterface val="#{prot[:user_interface] ? 1 : 0}"/>) unless prot[:user_interface].nil?
|
|
791
|
+
parts << "</c:protection>"
|
|
792
|
+
end
|
|
793
|
+
parts << "<c:chart>"
|
|
794
|
+
|
|
795
|
+
if chart[:title]
|
|
796
|
+
title_spec = chart[:title]
|
|
797
|
+
if chart[:title_font] || chart[:title_fill_color] || chart[:title_no_fill] ||
|
|
798
|
+
chart[:title_line_color] || chart[:title_line_width] || chart[:title_line_dash] ||
|
|
799
|
+
chart[:title_rotation]
|
|
800
|
+
title_spec = { text: title_spec } unless title_spec.is_a?(Hash)
|
|
801
|
+
title_spec[:font] ||= chart[:title_font] if chart[:title_font]
|
|
802
|
+
title_spec[:fill_color] ||= chart[:title_fill_color] if chart[:title_fill_color]
|
|
803
|
+
title_spec[:no_fill] = chart[:title_no_fill] if title_spec[:no_fill].nil? && !chart[:title_no_fill].nil?
|
|
804
|
+
title_spec[:line_color] ||= chart[:title_line_color] if chart[:title_line_color]
|
|
805
|
+
title_spec[:line_width] ||= chart[:title_line_width] if chart[:title_line_width]
|
|
806
|
+
title_spec[:line_dash] ||= chart[:title_line_dash] if chart[:title_line_dash]
|
|
807
|
+
title_spec[:rotation] ||= chart[:title_rotation] if chart[:title_rotation]
|
|
808
|
+
end
|
|
809
|
+
parts << build_chart_title_xml(title_spec, overlay: chart[:title_overlay])
|
|
810
|
+
end
|
|
811
|
+
atd = chart[:auto_title_deleted]
|
|
812
|
+
parts << %(<c:autoTitleDeleted val="#{atd ? 1 : 0}"/>) unless atd.nil?
|
|
813
|
+
|
|
814
|
+
if (v3d = chart[:view_3d])
|
|
815
|
+
parts << "<c:view3D>"
|
|
816
|
+
parts << %(<c:rotX val="#{v3d[:rot_x]}"/>) if v3d[:rot_x]
|
|
817
|
+
parts << %(<c:hPercent val="#{v3d[:h_percent]}"/>) if v3d[:h_percent]
|
|
818
|
+
parts << %(<c:rotY val="#{v3d[:rot_y]}"/>) if v3d[:rot_y]
|
|
819
|
+
parts << %(<c:depthPercent val="#{v3d[:depth_percent]}"/>) if v3d[:depth_percent]
|
|
820
|
+
r_ang = v3d[:r_ang_ax]
|
|
821
|
+
parts << %(<c:rAngAx val="#{r_ang ? 1 : 0}"/>) unless r_ang.nil?
|
|
822
|
+
parts << %(<c:perspective val="#{v3d[:perspective]}"/>) if v3d[:perspective]
|
|
823
|
+
parts << "</c:view3D>"
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
%i[floor side_wall back_wall].each do |wall_key|
|
|
827
|
+
next unless (wall = chart[wall_key])
|
|
828
|
+
|
|
829
|
+
tag = { floor: "floor", side_wall: "sideWall", back_wall: "backWall" }[wall_key]
|
|
830
|
+
parts << "<c:#{tag}><c:spPr>"
|
|
831
|
+
parts << "<a:solidFill>#{srgb_clr_xml(wall[:fill_color])}</a:solidFill>" if wall[:fill_color]
|
|
832
|
+
parts << "<a:noFill/>" if wall[:no_fill]
|
|
833
|
+
if wall[:line_color] || wall[:line_dash]
|
|
834
|
+
w_attr = wall[:line_width] ? %( w="#{wall[:line_width]}") : ""
|
|
835
|
+
w_fill = wall[:line_color] ? %(<a:solidFill>#{srgb_clr_xml(wall[:line_color])}</a:solidFill>) : ""
|
|
836
|
+
w_dash = wall[:line_dash] ? %(<a:prstDash val="#{xml_escape(wall[:line_dash])}"/>) : ""
|
|
837
|
+
parts << "<a:ln#{w_attr}>#{w_fill}#{w_dash}</a:ln>"
|
|
838
|
+
end
|
|
839
|
+
parts << "</c:spPr></c:#{tag}>"
|
|
840
|
+
end
|
|
841
|
+
|
|
842
|
+
parts << "<c:plotArea>"
|
|
843
|
+
pa_layout = chart[:plot_area_layout]
|
|
844
|
+
if pa_layout.is_a?(Hash)
|
|
845
|
+
ml_parts = +""
|
|
846
|
+
ml_parts << %(<c:layoutTarget val="#{xml_escape(pa_layout[:target])}"/>) if pa_layout[:target]
|
|
847
|
+
ml_parts << %(<c:xMode val="#{xml_escape(pa_layout[:x_mode])}"/>) if pa_layout[:x_mode]
|
|
848
|
+
ml_parts << %(<c:yMode val="#{xml_escape(pa_layout[:y_mode])}"/>) if pa_layout[:y_mode]
|
|
849
|
+
ml_parts << %(<c:wMode val="#{xml_escape(pa_layout[:w_mode])}"/>) if pa_layout[:w_mode]
|
|
850
|
+
ml_parts << %(<c:hMode val="#{xml_escape(pa_layout[:h_mode])}"/>) if pa_layout[:h_mode]
|
|
851
|
+
ml_parts << %(<c:x val="#{pa_layout[:x]}"/>) if pa_layout[:x]
|
|
852
|
+
ml_parts << %(<c:y val="#{pa_layout[:y]}"/>) if pa_layout[:y]
|
|
853
|
+
ml_parts << %(<c:w val="#{pa_layout[:w]}"/>) if pa_layout[:w]
|
|
854
|
+
ml_parts << %(<c:h val="#{pa_layout[:h]}"/>) if pa_layout[:h]
|
|
855
|
+
parts << if ml_parts.empty?
|
|
856
|
+
"<c:layout/>"
|
|
857
|
+
else
|
|
858
|
+
"<c:layout><c:manualLayout>#{ml_parts}</c:manualLayout></c:layout>"
|
|
859
|
+
end
|
|
860
|
+
else
|
|
861
|
+
parts << "<c:layout/>"
|
|
862
|
+
end
|
|
863
|
+
parts << "<c:#{chart_type}>"
|
|
864
|
+
parts << %(<c:ofPieType val="#{xml_escape(chart[:of_pie_type] || "pie")}"/>) if chart_type == "ofPieChart"
|
|
865
|
+
if %w[barChart bar3DChart].include?(chart_type)
|
|
866
|
+
bd = chart[:bar_dir] || "col"
|
|
867
|
+
gr = chart[:grouping] || "clustered"
|
|
868
|
+
parts << %(<c:barDir val="#{bd}"/><c:grouping val="#{gr}"/>)
|
|
869
|
+
elsif GROUPING_CHARTS.include?(chart_type)
|
|
870
|
+
parts << %(<c:grouping val="#{chart[:grouping] || "standard"}"/>)
|
|
871
|
+
end
|
|
872
|
+
parts << %(<c:scatterStyle val="#{chart[:scatter_style] || "lineMarker"}"/>) if chart_type == "scatterChart"
|
|
873
|
+
parts << %(<c:radarStyle val="#{chart[:radar_style] || "standard"}"/>) if chart_type == "radarChart"
|
|
874
|
+
vc = chart[:vary_colors]
|
|
875
|
+
parts << %(<c:varyColors val="#{vc ? 1 : 0}"/>) unless vc.nil?
|
|
876
|
+
wf = chart[:wireframe]
|
|
877
|
+
parts << %(<c:wireframe val="#{wf ? 1 : 0}"/>) unless wf.nil?
|
|
878
|
+
|
|
879
|
+
all_series = chart[:series] || []
|
|
880
|
+
all_series.each_with_index do |ser, idx|
|
|
881
|
+
ser_order = ser[:order] || idx
|
|
882
|
+
parts << "<c:ser><c:idx val=\"#{idx}\"/><c:order val=\"#{ser_order}\"/>"
|
|
883
|
+
parts << "<c:tx><c:strRef><c:f>#{xml_escape(ser[:name])}</c:f>#{str_cache_xml(ser[:name])}</c:strRef></c:tx>" if ser[:name]
|
|
884
|
+
iin = ser[:invert_if_negative]
|
|
885
|
+
parts << %(<c:invertIfNegative val="#{iin ? 1 : 0}"/>) unless iin.nil?
|
|
886
|
+
parts << %(<c:explosion val="#{ser[:explosion]}"/>) if ser[:explosion]
|
|
887
|
+
ser[:data_points]&.each do |dp|
|
|
888
|
+
parts << "<c:dPt><c:idx val=\"#{dp[:idx]}\"/>"
|
|
889
|
+
dp_iin = dp[:invert_if_negative]
|
|
890
|
+
parts << %(<c:invertIfNegative val="#{dp_iin ? 1 : 0}"/>) unless dp_iin.nil?
|
|
891
|
+
if dp[:marker_symbol] || dp[:marker_size] || dp[:marker_fill] || dp[:marker_no_fill] || dp[:marker_line_color] || dp[:marker_line_dash] || dp[:marker_no_line]
|
|
892
|
+
parts << "<c:marker>"
|
|
893
|
+
parts << %(<c:symbol val="#{xml_escape(dp[:marker_symbol])}"/>) if dp[:marker_symbol]
|
|
894
|
+
parts << %(<c:size val="#{dp[:marker_size]}"/>) if dp[:marker_size]
|
|
895
|
+
if dp[:marker_fill] || dp[:marker_no_fill] || dp[:marker_line_color] || dp[:marker_line_dash] || dp[:marker_no_line]
|
|
896
|
+
mk_sp = +""
|
|
897
|
+
mk_sp << "<a:noFill/>" if dp[:marker_no_fill]
|
|
898
|
+
mk_sp << %(<a:solidFill>#{color_xml(dp[:marker_fill])}</a:solidFill>) if dp[:marker_fill]
|
|
899
|
+
if dp[:marker_no_line]
|
|
900
|
+
mk_sp << "<a:ln><a:noFill/></a:ln>"
|
|
901
|
+
elsif dp[:marker_line_color] || dp[:marker_line_dash]
|
|
902
|
+
mk_ln_w = dp[:marker_line_width] ? %( w="#{(dp[:marker_line_width] * 12_700).to_i}") : ""
|
|
903
|
+
mk_ln_f = dp[:marker_line_color] ? %(<a:solidFill>#{color_xml(dp[:marker_line_color])}</a:solidFill>) : ""
|
|
904
|
+
mk_ln_d = dp[:marker_line_dash] ? %(<a:prstDash val="#{xml_escape(dp[:marker_line_dash])}"/>) : ""
|
|
905
|
+
mk_sp << "<a:ln#{mk_ln_w}>#{mk_ln_f}#{mk_ln_d}</a:ln>"
|
|
906
|
+
end
|
|
907
|
+
parts << "<c:spPr>#{mk_sp}</c:spPr>"
|
|
908
|
+
end
|
|
909
|
+
parts << "</c:marker>"
|
|
910
|
+
end
|
|
911
|
+
dp_b3d = dp[:bubble_3d]
|
|
912
|
+
parts << %(<c:bubble3D val="#{dp_b3d ? 1 : 0}"/>) unless dp_b3d.nil?
|
|
913
|
+
parts << %(<c:explosion val="#{dp[:explosion]}"/>) if dp[:explosion]
|
|
914
|
+
dp_sp_children = +""
|
|
915
|
+
dp_sp_children << %(<a:solidFill>#{color_xml(dp[:fill_color])}</a:solidFill>) if dp[:fill_color]
|
|
916
|
+
dp_sp_children << "<a:noFill/>" if dp[:no_fill]
|
|
917
|
+
if dp[:no_line]
|
|
918
|
+
dp_sp_children << "<a:ln><a:noFill/></a:ln>"
|
|
919
|
+
elsif dp[:line_color] || dp[:line_width] || dp[:line_dash]
|
|
920
|
+
dp_ln_attrs = dp[:line_width] ? %( w="#{(dp[:line_width] * 12_700).to_i}") : ""
|
|
921
|
+
dp_ln_fill = dp[:line_color] ? %(<a:solidFill>#{color_xml(dp[:line_color])}</a:solidFill>) : ""
|
|
922
|
+
dp_ln_dash = dp[:line_dash] ? %(<a:prstDash val="#{xml_escape(dp[:line_dash])}"/>) : ""
|
|
923
|
+
dp_sp_children << "<a:ln#{dp_ln_attrs}>#{dp_ln_fill}#{dp_ln_dash}</a:ln>"
|
|
924
|
+
end
|
|
925
|
+
parts << "<c:spPr>#{dp_sp_children}</c:spPr>" unless dp_sp_children.empty?
|
|
926
|
+
parts << "</c:dPt>"
|
|
927
|
+
end
|
|
928
|
+
if ser[:fill_color] || ser[:no_fill] || ser[:line_color] || ser[:no_line] || ser[:line_width] || ser[:line_cap] || ser[:line_join] || ser[:line_dash]
|
|
929
|
+
parts << "<c:spPr>"
|
|
930
|
+
parts << %(<a:solidFill>#{color_xml(ser[:fill_color])}</a:solidFill>) if ser[:fill_color]
|
|
931
|
+
parts << "<a:noFill/>" if ser[:no_fill]
|
|
932
|
+
if ser[:line_color] || ser[:no_line] || ser[:line_width] || ser[:line_cap] || ser[:line_join] || ser[:line_dash]
|
|
933
|
+
lw = ser[:line_width] ? %( w="#{(ser[:line_width] * 12_700).to_i}") : ""
|
|
934
|
+
lc = ser[:line_cap] ? %( cap="#{xml_escape(ser[:line_cap])}") : ""
|
|
935
|
+
parts << "<a:ln#{lw}#{lc}>"
|
|
936
|
+
parts << "<a:noFill/>" if ser[:no_line]
|
|
937
|
+
parts << %(<a:solidFill>#{color_xml(ser[:line_color])}</a:solidFill>) if ser[:line_color]
|
|
938
|
+
parts << %(<a:prstDash val="#{xml_escape(ser[:line_dash])}"/>) if ser[:line_dash]
|
|
939
|
+
case ser[:line_join]
|
|
940
|
+
when "round" then parts << "<a:round/>"
|
|
941
|
+
when "bevel" then parts << "<a:bevel/>"
|
|
942
|
+
when "miter"
|
|
943
|
+
parts << (ser[:line_miter_limit] ? "<a:miter lim=\"#{ser[:line_miter_limit].to_i}\"/>" : "<a:miter/>")
|
|
944
|
+
end
|
|
945
|
+
parts << "</a:ln>"
|
|
946
|
+
end
|
|
947
|
+
parts << "</c:spPr>"
|
|
948
|
+
end
|
|
949
|
+
if ser[:marker_symbol] || ser[:marker_size] || ser[:marker_fill] || ser[:marker_no_fill] || ser[:marker_line_color] || ser[:marker_line_dash] || ser[:marker_no_line]
|
|
950
|
+
parts << "<c:marker>"
|
|
951
|
+
parts << %(<c:symbol val="#{xml_escape(ser[:marker_symbol])}"/>) if ser[:marker_symbol]
|
|
952
|
+
parts << %(<c:size val="#{ser[:marker_size]}"/>) if ser[:marker_size]
|
|
953
|
+
if ser[:marker_fill] || ser[:marker_no_fill] || ser[:marker_line_color] || ser[:marker_line_dash] || ser[:marker_no_line]
|
|
954
|
+
parts << "<c:spPr>"
|
|
955
|
+
parts << "<a:noFill/>" if ser[:marker_no_fill]
|
|
956
|
+
parts << %(<a:solidFill>#{color_xml(ser[:marker_fill])}</a:solidFill>) if ser[:marker_fill]
|
|
957
|
+
if ser[:marker_no_line]
|
|
958
|
+
parts << "<a:ln><a:noFill/></a:ln>"
|
|
959
|
+
elsif ser[:marker_line_color] || ser[:marker_line_dash]
|
|
960
|
+
mk_ln_w = ser[:marker_line_width] ? %( w="#{(ser[:marker_line_width] * 12_700).to_i}") : ""
|
|
961
|
+
mk_ln_f = ser[:marker_line_color] ? %(<a:solidFill>#{color_xml(ser[:marker_line_color])}</a:solidFill>) : ""
|
|
962
|
+
mk_ln_d = ser[:marker_line_dash] ? %(<a:prstDash val="#{xml_escape(ser[:marker_line_dash])}"/>) : ""
|
|
963
|
+
parts << "<a:ln#{mk_ln_w}>#{mk_ln_f}#{mk_ln_d}</a:ln>"
|
|
964
|
+
end
|
|
965
|
+
parts << "</c:spPr>"
|
|
966
|
+
end
|
|
967
|
+
parts << "</c:marker>"
|
|
968
|
+
end
|
|
969
|
+
if (dl = ser[:data_labels] || chart[:data_labels])
|
|
970
|
+
parts << "<c:dLbls>"
|
|
971
|
+
dl[:labels]&.each do |lbl|
|
|
972
|
+
parts << "<c:dLbl>"
|
|
973
|
+
parts << %(<c:idx val="#{lbl[:idx]}"/>)
|
|
974
|
+
if lbl[:delete]
|
|
975
|
+
parts << '<c:delete val="1"/>'
|
|
976
|
+
else
|
|
977
|
+
if (lbl_layout = lbl[:layout])
|
|
978
|
+
ml = +""
|
|
979
|
+
ml << %(<c:x val="#{lbl_layout[:x]}"/>) if lbl_layout[:x]
|
|
980
|
+
ml << %(<c:y val="#{lbl_layout[:y]}"/>) if lbl_layout[:y]
|
|
981
|
+
parts << (ml.empty? ? "<c:layout/>" : "<c:layout><c:manualLayout>#{ml}</c:manualLayout></c:layout>")
|
|
982
|
+
end
|
|
983
|
+
parts << "<c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>#{xml_escape(lbl[:text])}</a:t></a:r></a:p></c:rich></c:tx>" if lbl[:text]
|
|
984
|
+
if lbl[:num_fmt]
|
|
985
|
+
lnf = lbl[:num_fmt]
|
|
986
|
+
lnf_src = lnf[:source_linked] ? ' sourceLinked="1"' : ""
|
|
987
|
+
parts << %(<c:numFmt formatCode="#{xml_escape(lnf[:format_code])}"#{lnf_src}/>)
|
|
988
|
+
end
|
|
989
|
+
lbl_sp = +""
|
|
990
|
+
lbl_sp << %(<a:solidFill>#{color_xml(lbl[:fill_color])}</a:solidFill>) if lbl[:fill_color]
|
|
991
|
+
lbl_sp << "<a:noFill/>" if lbl[:no_fill]
|
|
992
|
+
if lbl[:line_color] || lbl[:line_width] || lbl[:line_dash]
|
|
993
|
+
lbl_lw = lbl[:line_width] ? %( w="#{(lbl[:line_width] * 12_700).to_i}") : ""
|
|
994
|
+
lbl_lf = lbl[:line_color] ? %(<a:solidFill>#{color_xml(lbl[:line_color])}</a:solidFill>) : ""
|
|
995
|
+
lbl_ld = lbl[:line_dash] ? %(<a:prstDash val="#{xml_escape(lbl[:line_dash])}"/>) : ""
|
|
996
|
+
lbl_sp << "<a:ln#{lbl_lw}>#{lbl_lf}#{lbl_ld}</a:ln>"
|
|
997
|
+
end
|
|
998
|
+
parts << "<c:spPr>#{lbl_sp}</c:spPr>" unless lbl_sp.empty?
|
|
999
|
+
parts << build_axis_txpr(nil, lbl[:font]) if lbl[:font]
|
|
1000
|
+
parts << %(<c:dLblPos val="#{lbl[:position]}"/>) if lbl[:position]
|
|
1001
|
+
parts << "<c:showLegendKey val=\"#{lbl[:show_legend_key] ? 1 : 0}\"/>" unless lbl[:show_legend_key].nil?
|
|
1002
|
+
parts << "<c:showVal val=\"#{lbl[:show_val] ? 1 : 0}\"/>" unless lbl[:show_val].nil?
|
|
1003
|
+
parts << "<c:showCatName val=\"#{lbl[:show_cat_name] ? 1 : 0}\"/>" unless lbl[:show_cat_name].nil?
|
|
1004
|
+
parts << "<c:showSerName val=\"#{lbl[:show_ser_name] ? 1 : 0}\"/>" unless lbl[:show_ser_name].nil?
|
|
1005
|
+
parts << "<c:showPercent val=\"#{lbl[:show_percent] ? 1 : 0}\"/>" unless lbl[:show_percent].nil?
|
|
1006
|
+
parts << "<c:showBubbleSize val=\"#{lbl[:show_bubble_size] ? 1 : 0}\"/>" unless lbl[:show_bubble_size].nil?
|
|
1007
|
+
parts << "<c:separator>#{xml_escape(lbl[:separator])}</c:separator>" if lbl[:separator]
|
|
1008
|
+
end
|
|
1009
|
+
parts << "</c:dLbl>"
|
|
1010
|
+
end
|
|
1011
|
+
if dl[:num_fmt]
|
|
1012
|
+
nf = dl[:num_fmt]
|
|
1013
|
+
nf_src = nf[:source_linked] ? ' sourceLinked="1"' : ""
|
|
1014
|
+
parts << %(<c:numFmt formatCode="#{xml_escape(nf[:format_code])}"#{nf_src}/>)
|
|
1015
|
+
end
|
|
1016
|
+
dl_sp_children = +""
|
|
1017
|
+
dl_sp_children << %(<a:solidFill>#{color_xml(dl[:fill_color])}</a:solidFill>) if dl[:fill_color]
|
|
1018
|
+
dl_sp_children << "<a:noFill/>" if dl[:no_fill]
|
|
1019
|
+
if dl[:line_color] || dl[:line_width] || dl[:line_dash]
|
|
1020
|
+
dl_lw = dl[:line_width] ? %( w="#{(dl[:line_width] * 12_700).to_i}") : ""
|
|
1021
|
+
dl_lf = dl[:line_color] ? %(<a:solidFill>#{color_xml(dl[:line_color])}</a:solidFill>) : ""
|
|
1022
|
+
dl_ld = dl[:line_dash] ? %(<a:prstDash val="#{xml_escape(dl[:line_dash])}"/>) : ""
|
|
1023
|
+
dl_sp_children << "<a:ln#{dl_lw}>#{dl_lf}#{dl_ld}</a:ln>"
|
|
1024
|
+
end
|
|
1025
|
+
parts << "<c:spPr>#{dl_sp_children}</c:spPr>" unless dl_sp_children.empty?
|
|
1026
|
+
parts << build_axis_txpr(nil, dl[:font]) if dl[:font]
|
|
1027
|
+
parts << %(<c:dLblPos val="#{dl[:position]}"/>) if dl[:position]
|
|
1028
|
+
parts << "<c:showLegendKey val=\"#{dl[:show_legend_key] ? 1 : 0}\"/>" unless dl[:show_legend_key].nil?
|
|
1029
|
+
parts << "<c:showVal val=\"#{dl[:show_val] ? 1 : 0}\"/>" unless dl[:show_val].nil?
|
|
1030
|
+
parts << "<c:showCatName val=\"#{dl[:show_cat_name] ? 1 : 0}\"/>" unless dl[:show_cat_name].nil?
|
|
1031
|
+
parts << "<c:showSerName val=\"#{dl[:show_ser_name] ? 1 : 0}\"/>" unless dl[:show_ser_name].nil?
|
|
1032
|
+
parts << "<c:showPercent val=\"#{dl[:show_percent] ? 1 : 0}\"/>" unless dl[:show_percent].nil?
|
|
1033
|
+
parts << "<c:showBubbleSize val=\"#{dl[:show_bubble_size] ? 1 : 0}\"/>" unless dl[:show_bubble_size].nil?
|
|
1034
|
+
parts << "<c:separator>#{xml_escape(dl[:separator])}</c:separator>" if dl[:separator]
|
|
1035
|
+
sl = dl[:show_leader_lines]
|
|
1036
|
+
parts << "<c:showLeaderLines val=\"#{sl ? 1 : 0}\"/>" unless sl.nil?
|
|
1037
|
+
if dl[:leader_lines]
|
|
1038
|
+
ll_spec = dl[:leader_lines]
|
|
1039
|
+
if ll_spec.is_a?(Hash)
|
|
1040
|
+
ll_sp = +""
|
|
1041
|
+
if ll_spec[:line_color] || ll_spec[:line_width] || ll_spec[:line_dash]
|
|
1042
|
+
ll_lw = ll_spec[:line_width] ? %( w="#{(ll_spec[:line_width] * 12_700).to_i}") : ""
|
|
1043
|
+
ll_lf = ll_spec[:line_color] ? %(<a:solidFill>#{color_xml(ll_spec[:line_color])}</a:solidFill>) : ""
|
|
1044
|
+
ll_ld = ll_spec[:line_dash] ? %(<a:prstDash val="#{xml_escape(ll_spec[:line_dash])}"/>) : ""
|
|
1045
|
+
ll_sp << "<a:ln#{ll_lw}>#{ll_lf}#{ll_ld}</a:ln>"
|
|
1046
|
+
end
|
|
1047
|
+
parts << (ll_sp.empty? ? "<c:leaderLines/>" : "<c:leaderLines><c:spPr>#{ll_sp}</c:spPr></c:leaderLines>")
|
|
1048
|
+
else
|
|
1049
|
+
parts << "<c:leaderLines/>"
|
|
1050
|
+
end
|
|
1051
|
+
end
|
|
1052
|
+
parts << "</c:dLbls>"
|
|
1053
|
+
end
|
|
1054
|
+
trendline_list = ser[:trendlines] || (ser[:trendline] ? [ser[:trendline]] : [])
|
|
1055
|
+
trendline_list.each do |tl|
|
|
1056
|
+
parts << "<c:trendline>"
|
|
1057
|
+
parts << "<c:name>#{xml_escape(tl[:name])}</c:name>" if tl[:name]
|
|
1058
|
+
tl_sp_children = +""
|
|
1059
|
+
if tl[:line_color] || tl[:line_width] || tl[:line_dash]
|
|
1060
|
+
tl_ln_w = tl[:line_width] ? %( w="#{(tl[:line_width] * 12_700).to_i}") : ""
|
|
1061
|
+
tl_ln_f = tl[:line_color] ? %(<a:solidFill>#{color_xml(tl[:line_color])}</a:solidFill>) : ""
|
|
1062
|
+
tl_ln_d = tl[:line_dash] ? %(<a:prstDash val="#{xml_escape(tl[:line_dash])}"/>) : ""
|
|
1063
|
+
tl_sp_children << "<a:ln#{tl_ln_w}>#{tl_ln_f}#{tl_ln_d}</a:ln>"
|
|
1064
|
+
end
|
|
1065
|
+
parts << "<c:spPr>#{tl_sp_children}</c:spPr>" unless tl_sp_children.empty?
|
|
1066
|
+
parts << %(<c:trendlineType val="#{xml_escape(tl[:type] || "linear")}"/>)
|
|
1067
|
+
parts << %(<c:order val="#{tl[:order]}"/>) if tl[:order]
|
|
1068
|
+
parts << %(<c:period val="#{tl[:period]}"/>) if tl[:period]
|
|
1069
|
+
parts << %(<c:forward val="#{tl[:forward]}"/>) if tl[:forward]
|
|
1070
|
+
parts << %(<c:backward val="#{tl[:backward]}"/>) if tl[:backward]
|
|
1071
|
+
parts << %(<c:intercept val="#{tl[:intercept]}"/>) if tl[:intercept]
|
|
1072
|
+
parts << %(<c:dispRSqr val="#{tl[:disp_r_sqr] ? 1 : 0}"/>) unless tl[:disp_r_sqr].nil?
|
|
1073
|
+
parts << %(<c:dispEq val="#{tl[:disp_eq] ? 1 : 0}"/>) unless tl[:disp_eq].nil?
|
|
1074
|
+
if tl[:label]
|
|
1075
|
+
lbl = tl[:label]
|
|
1076
|
+
parts << "<c:trendlineLbl>"
|
|
1077
|
+
parts << build_title_layout_xml(lbl[:layout])
|
|
1078
|
+
parts << "<c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>#{xml_escape(lbl[:text])}</a:t></a:r></a:p></c:rich></c:tx>" if lbl[:text]
|
|
1079
|
+
if lbl[:num_fmt]
|
|
1080
|
+
nf = lbl[:num_fmt]
|
|
1081
|
+
nf_src = nf.is_a?(Hash) ? nf : { format_code: nf }
|
|
1082
|
+
nf_linked = if nf_src.key?(:source_linked)
|
|
1083
|
+
nf_src[:source_linked] ? 1 : 0
|
|
1084
|
+
else
|
|
1085
|
+
0
|
|
1086
|
+
end
|
|
1087
|
+
parts << %(<c:numFmt formatCode="#{xml_escape(nf_src[:format_code])}" sourceLinked="#{nf_linked}"/>)
|
|
1088
|
+
end
|
|
1089
|
+
tll_sp = +""
|
|
1090
|
+
tll_sp << %(<a:solidFill>#{color_xml(lbl[:fill_color])}</a:solidFill>) if lbl[:fill_color]
|
|
1091
|
+
tll_sp << "<a:noFill/>" if lbl[:no_fill]
|
|
1092
|
+
if lbl[:line_color] || lbl[:line_width] || lbl[:line_dash]
|
|
1093
|
+
tll_lw = lbl[:line_width] ? %( w="#{(lbl[:line_width] * 12_700).to_i}") : ""
|
|
1094
|
+
tll_lf = lbl[:line_color] ? %(<a:solidFill>#{color_xml(lbl[:line_color])}</a:solidFill>) : ""
|
|
1095
|
+
tll_ld = lbl[:line_dash] ? %(<a:prstDash val="#{xml_escape(lbl[:line_dash])}"/>) : ""
|
|
1096
|
+
tll_sp << "<a:ln#{tll_lw}>#{tll_lf}#{tll_ld}</a:ln>"
|
|
1097
|
+
end
|
|
1098
|
+
parts << "<c:spPr>#{tll_sp}</c:spPr>" unless tll_sp.empty?
|
|
1099
|
+
parts << build_axis_txpr(nil, lbl[:font]) if lbl[:font]
|
|
1100
|
+
parts << "</c:trendlineLbl>"
|
|
1101
|
+
end
|
|
1102
|
+
parts << "</c:trendline>"
|
|
1103
|
+
end
|
|
1104
|
+
err_bars_list = ser[:error_bars_list] || (ser[:error_bars] ? [ser[:error_bars]] : [])
|
|
1105
|
+
err_bars_list.each do |eb|
|
|
1106
|
+
parts << "<c:errBars>"
|
|
1107
|
+
parts << %(<c:errDir val="#{xml_escape(eb[:direction])}"/>) if eb[:direction]
|
|
1108
|
+
parts << %(<c:errBarType val="#{xml_escape(eb[:bar_type] || "both")}"/>)
|
|
1109
|
+
parts << %(<c:errValType val="#{xml_escape(eb[:val_type] || "fixedVal")}"/>)
|
|
1110
|
+
parts << %(<c:noEndCap val="#{eb[:no_end_cap] ? 1 : 0}"/>) unless eb[:no_end_cap].nil?
|
|
1111
|
+
parts << "<c:plus><c:numRef><c:f>#{xml_escape(eb[:plus])}</c:f></c:numRef></c:plus>" if eb[:plus]
|
|
1112
|
+
parts << "<c:minus><c:numRef><c:f>#{xml_escape(eb[:minus])}</c:f></c:numRef></c:minus>" if eb[:minus]
|
|
1113
|
+
parts << %(<c:val val="#{eb[:val]}"/>) if eb[:val]
|
|
1114
|
+
eb_sp_children = +""
|
|
1115
|
+
eb_sp_children << %(<a:solidFill>#{color_xml(eb[:fill_color])}</a:solidFill>) if eb[:fill_color]
|
|
1116
|
+
eb_sp_children << "<a:noFill/>" if eb[:no_fill]
|
|
1117
|
+
if eb[:line_color] || eb[:line_width] || eb[:line_dash]
|
|
1118
|
+
eb_ln_w = eb[:line_width] ? %( w="#{(eb[:line_width] * 12_700).to_i}") : ""
|
|
1119
|
+
eb_ln_f = eb[:line_color] ? %(<a:solidFill>#{color_xml(eb[:line_color])}</a:solidFill>) : ""
|
|
1120
|
+
eb_ln_d = eb[:line_dash] ? %(<a:prstDash val="#{xml_escape(eb[:line_dash])}"/>) : ""
|
|
1121
|
+
eb_sp_children << "<a:ln#{eb_ln_w}>#{eb_ln_f}#{eb_ln_d}</a:ln>"
|
|
1122
|
+
end
|
|
1123
|
+
parts << "<c:spPr>#{eb_sp_children}</c:spPr>" unless eb_sp_children.empty?
|
|
1124
|
+
parts << "</c:errBars>"
|
|
1125
|
+
end
|
|
1126
|
+
uses_xy = %w[scatterChart bubbleChart].include?(chart_type)
|
|
1127
|
+
cat_tag = uses_xy ? "xVal" : "cat"
|
|
1128
|
+
val_tag = uses_xy ? "yVal" : "val"
|
|
1129
|
+
if ser[:cat_ref]
|
|
1130
|
+
parts << if uses_xy || ser[:cat_ref_type] == :num
|
|
1131
|
+
"<c:#{cat_tag}><c:numRef><c:f>#{xml_escape(ser[:cat_ref])}</c:f>#{num_cache_xml(ser[:cat_ref])}</c:numRef></c:#{cat_tag}>"
|
|
1132
|
+
else
|
|
1133
|
+
"<c:#{cat_tag}><c:strRef><c:f>#{xml_escape(ser[:cat_ref])}</c:f>#{str_cache_xml(ser[:cat_ref])}</c:strRef></c:#{cat_tag}>"
|
|
1134
|
+
end
|
|
1135
|
+
end
|
|
1136
|
+
parts << "<c:#{val_tag}><c:numRef><c:f>#{xml_escape(ser[:val_ref])}</c:f>#{num_cache_xml(ser[:val_ref])}</c:numRef></c:#{val_tag}>" if ser[:val_ref]
|
|
1137
|
+
parts << "<c:bubbleSize><c:numRef><c:f>#{xml_escape(ser[:bubble_size_ref])}</c:f>#{num_cache_xml(ser[:bubble_size_ref])}</c:numRef></c:bubbleSize>" if ser[:bubble_size_ref]
|
|
1138
|
+
parts << %(<c:smooth val="#{ser[:smooth] ? 1 : 0}"/>) unless ser[:smooth].nil?
|
|
1139
|
+
parts << %(<c:shape val="#{xml_escape(ser[:shape])}"/>) if ser[:shape]
|
|
1140
|
+
parts << "</c:ser>"
|
|
1141
|
+
end
|
|
1142
|
+
|
|
1143
|
+
if chart[:band_fmts]
|
|
1144
|
+
parts << "<c:bandFmts>"
|
|
1145
|
+
chart[:band_fmts].each do |bf|
|
|
1146
|
+
parts << "<c:bandFmt><c:idx val=\"#{bf[:idx]}\"/>"
|
|
1147
|
+
bf_sp = +""
|
|
1148
|
+
bf_sp << %(<a:solidFill>#{color_xml(bf[:fill_color])}</a:solidFill>) if bf[:fill_color]
|
|
1149
|
+
bf_sp << "<a:noFill/>" if bf[:no_fill]
|
|
1150
|
+
if bf[:line_color] || bf[:line_width] || bf[:line_dash]
|
|
1151
|
+
bf_lw = bf[:line_width] ? %( w="#{(bf[:line_width] * 12_700).to_i}") : ""
|
|
1152
|
+
bf_lf = bf[:line_color] ? %(<a:solidFill>#{color_xml(bf[:line_color])}</a:solidFill>) : ""
|
|
1153
|
+
bf_ld = bf[:line_dash] ? %(<a:prstDash val="#{xml_escape(bf[:line_dash])}"/>) : ""
|
|
1154
|
+
bf_sp << "<a:ln#{bf_lw}>#{bf_lf}#{bf_ld}</a:ln>"
|
|
1155
|
+
end
|
|
1156
|
+
parts << "<c:spPr>#{bf_sp}</c:spPr>" unless bf_sp.empty?
|
|
1157
|
+
parts << "</c:bandFmt>"
|
|
1158
|
+
end
|
|
1159
|
+
parts << "</c:bandFmts>"
|
|
1160
|
+
end
|
|
1161
|
+
|
|
1162
|
+
parts << %(<c:gapWidth val="#{chart[:gap_width]}"/>) if chart[:gap_width]
|
|
1163
|
+
parts << %(<c:splitType val="#{xml_escape(chart[:split_type])}"/>) if chart[:split_type]
|
|
1164
|
+
parts << %(<c:splitPos val="#{chart[:split_pos]}"/>) if chart[:split_pos]
|
|
1165
|
+
if chart[:cust_split]&.any?
|
|
1166
|
+
parts << "<c:custSplit>"
|
|
1167
|
+
chart[:cust_split].each { |idx| parts << %(<c:secondPiePt val="#{idx}"/>) }
|
|
1168
|
+
parts << "</c:custSplit>"
|
|
1169
|
+
end
|
|
1170
|
+
parts << %(<c:secondPieSize val="#{chart[:second_pie_size]}"/>) if chart[:second_pie_size]
|
|
1171
|
+
parts << %(<c:gapDepth val="#{chart[:gap_depth]}"/>) if chart[:gap_depth]
|
|
1172
|
+
parts << %(<c:overlap val="#{chart[:overlap]}"/>) if chart[:overlap]
|
|
1173
|
+
parts << %(<c:shape val="#{chart[:bar_shape]}"/>) if chart[:bar_shape]
|
|
1174
|
+
b3d = chart[:bubble_3d]
|
|
1175
|
+
parts << %(<c:bubble3D val="#{b3d ? 1 : 0}"/>) unless b3d.nil?
|
|
1176
|
+
parts << %(<c:bubbleScale val="#{chart[:bubble_scale]}"/>) if chart[:bubble_scale]
|
|
1177
|
+
snb = chart[:show_neg_bubbles]
|
|
1178
|
+
parts << %(<c:showNegBubbles val="#{snb ? 1 : 0}"/>) unless snb.nil?
|
|
1179
|
+
parts << %(<c:sizeRepresents val="#{chart[:size_represents]}"/>) if chart[:size_represents]
|
|
1180
|
+
parts << %(<c:firstSliceAng val="#{chart[:first_slice_ang]}"/>) if chart[:first_slice_ang]
|
|
1181
|
+
parts << %(<c:holeSize val="#{chart[:hole_size]}"/>) if chart[:hole_size]
|
|
1182
|
+
if chart[:ser_lines]
|
|
1183
|
+
sl_spec = chart[:ser_lines]
|
|
1184
|
+
if sl_spec.is_a?(Hash)
|
|
1185
|
+
sl_sp = +""
|
|
1186
|
+
if sl_spec[:line_color] || sl_spec[:line_width] || sl_spec[:line_dash]
|
|
1187
|
+
sl_lw = sl_spec[:line_width] ? %( w="#{(sl_spec[:line_width] * 12_700).to_i}") : ""
|
|
1188
|
+
sl_lf = sl_spec[:line_color] ? %(<a:solidFill>#{color_xml(sl_spec[:line_color])}</a:solidFill>) : ""
|
|
1189
|
+
sl_ld = sl_spec[:line_dash] ? %(<a:prstDash val="#{xml_escape(sl_spec[:line_dash])}"/>) : ""
|
|
1190
|
+
sl_sp << "<a:ln#{sl_lw}>#{sl_lf}#{sl_ld}</a:ln>"
|
|
1191
|
+
end
|
|
1192
|
+
parts << (sl_sp.empty? ? "<c:serLines/>" : "<c:serLines><c:spPr>#{sl_sp}</c:spPr></c:serLines>")
|
|
1193
|
+
else
|
|
1194
|
+
parts << "<c:serLines/>"
|
|
1195
|
+
end
|
|
1196
|
+
end
|
|
1197
|
+
if chart[:drop_lines]
|
|
1198
|
+
dl_spec = chart[:drop_lines]
|
|
1199
|
+
if dl_spec.is_a?(Hash)
|
|
1200
|
+
dl_sp = +""
|
|
1201
|
+
if dl_spec[:line_color] || dl_spec[:line_width] || dl_spec[:line_dash]
|
|
1202
|
+
dl_lw = dl_spec[:line_width] ? %( w="#{(dl_spec[:line_width] * 12_700).to_i}") : ""
|
|
1203
|
+
dl_lf = dl_spec[:line_color] ? %(<a:solidFill>#{color_xml(dl_spec[:line_color])}</a:solidFill>) : ""
|
|
1204
|
+
dl_ld = dl_spec[:line_dash] ? %(<a:prstDash val="#{xml_escape(dl_spec[:line_dash])}"/>) : ""
|
|
1205
|
+
dl_sp << "<a:ln#{dl_lw}>#{dl_lf}#{dl_ld}</a:ln>"
|
|
1206
|
+
end
|
|
1207
|
+
parts << (dl_sp.empty? ? "<c:dropLines/>" : "<c:dropLines><c:spPr>#{dl_sp}</c:spPr></c:dropLines>")
|
|
1208
|
+
else
|
|
1209
|
+
parts << "<c:dropLines/>"
|
|
1210
|
+
end
|
|
1211
|
+
end
|
|
1212
|
+
if chart[:hi_low_lines]
|
|
1213
|
+
hl_spec = chart[:hi_low_lines]
|
|
1214
|
+
if hl_spec.is_a?(Hash)
|
|
1215
|
+
hl_sp = +""
|
|
1216
|
+
if hl_spec[:line_color] || hl_spec[:line_width] || hl_spec[:line_dash]
|
|
1217
|
+
hl_lw = hl_spec[:line_width] ? %( w="#{(hl_spec[:line_width] * 12_700).to_i}") : ""
|
|
1218
|
+
hl_lf = hl_spec[:line_color] ? %(<a:solidFill>#{color_xml(hl_spec[:line_color])}</a:solidFill>) : ""
|
|
1219
|
+
hl_ld = hl_spec[:line_dash] ? %(<a:prstDash val="#{xml_escape(hl_spec[:line_dash])}"/>) : ""
|
|
1220
|
+
hl_sp << "<a:ln#{hl_lw}>#{hl_lf}#{hl_ld}</a:ln>"
|
|
1221
|
+
end
|
|
1222
|
+
parts << (hl_sp.empty? ? "<c:hiLowLines/>" : "<c:hiLowLines><c:spPr>#{hl_sp}</c:spPr></c:hiLowLines>")
|
|
1223
|
+
else
|
|
1224
|
+
parts << "<c:hiLowLines/>"
|
|
1225
|
+
end
|
|
1226
|
+
end
|
|
1227
|
+
if chart[:up_down_bars]
|
|
1228
|
+
udb = chart[:up_down_bars]
|
|
1229
|
+
parts << "<c:upDownBars>"
|
|
1230
|
+
parts << %(<c:gapWidth val="#{udb[:gap_width]}"/>) if udb.is_a?(Hash) && udb[:gap_width]
|
|
1231
|
+
%i[up_bars down_bars].each do |bar_key|
|
|
1232
|
+
tag = bar_key == :up_bars ? "upBars" : "downBars"
|
|
1233
|
+
bar = udb.is_a?(Hash) ? udb[bar_key] : nil
|
|
1234
|
+
if bar
|
|
1235
|
+
bar_sp = +""
|
|
1236
|
+
bar_sp << %(<a:solidFill>#{color_xml(bar[:fill_color])}</a:solidFill>) if bar[:fill_color]
|
|
1237
|
+
bar_sp << "<a:noFill/>" if bar[:no_fill]
|
|
1238
|
+
if bar[:line_color] || bar[:line_width] || bar[:line_dash]
|
|
1239
|
+
b_lw = bar[:line_width] ? %( w="#{(bar[:line_width] * 12_700).to_i}") : ""
|
|
1240
|
+
b_lf = bar[:line_color] ? %(<a:solidFill>#{color_xml(bar[:line_color])}</a:solidFill>) : ""
|
|
1241
|
+
b_ld = bar[:line_dash] ? %(<a:prstDash val="#{xml_escape(bar[:line_dash])}"/>) : ""
|
|
1242
|
+
bar_sp << "<a:ln#{b_lw}>#{b_lf}#{b_ld}</a:ln>"
|
|
1243
|
+
end
|
|
1244
|
+
parts << (bar_sp.empty? ? "<c:#{tag}/>" : "<c:#{tag}><c:spPr>#{bar_sp}</c:spPr></c:#{tag}>")
|
|
1245
|
+
else
|
|
1246
|
+
parts << "<c:#{tag}/>"
|
|
1247
|
+
end
|
|
1248
|
+
end
|
|
1249
|
+
parts << "</c:upDownBars>"
|
|
1250
|
+
end
|
|
1251
|
+
mk = chart[:marker]
|
|
1252
|
+
parts << %(<c:marker val="#{mk ? 1 : 0}"/>) unless mk.nil?
|
|
1253
|
+
sm = chart[:smooth]
|
|
1254
|
+
parts << %(<c:smooth val="#{sm ? 1 : 0}"/>) unless sm.nil?
|
|
1255
|
+
three_axes = THREE_AXIS_CHARTS.include?(chart_type)
|
|
1256
|
+
unless no_axes
|
|
1257
|
+
parts << '<c:axId val="1"/><c:axId val="2"/>'
|
|
1258
|
+
parts << '<c:axId val="3"/>' if three_axes
|
|
1259
|
+
end
|
|
1260
|
+
parts << "</c:#{chart_type}>"
|
|
1261
|
+
|
|
1262
|
+
unless no_axes
|
|
1263
|
+
cat_del = chart[:cat_axis_delete] ? 1 : 0
|
|
1264
|
+
cat_orient = chart[:cat_axis_orientation] || "minMax"
|
|
1265
|
+
cat_ax_tag = chart[:cat_axis_type] == :date ? "dateAx" : "catAx"
|
|
1266
|
+
parts << %(<c:#{cat_ax_tag}><c:axId val="1"/><c:scaling>)
|
|
1267
|
+
parts << %(<c:logBase val="#{chart[:cat_axis_log_base]}"/>) if chart[:cat_axis_log_base]
|
|
1268
|
+
parts << %(<c:orientation val="#{cat_orient}"/>)
|
|
1269
|
+
parts << %(<c:max val="#{chart[:cat_axis_scaling_max]}"/>) if chart[:cat_axis_scaling_max]
|
|
1270
|
+
parts << %(<c:min val="#{chart[:cat_axis_scaling_min]}"/>) if chart[:cat_axis_scaling_min]
|
|
1271
|
+
parts << %(</c:scaling><c:delete val="#{cat_del}"/><c:axPos val="#{chart[:cat_axis_pos] || "b"}"/>)
|
|
1272
|
+
parts << gridlines_xml("majorGridlines", chart[:cat_axis_major_gridlines])
|
|
1273
|
+
parts << gridlines_xml("minorGridlines", chart[:cat_axis_minor_gridlines])
|
|
1274
|
+
if chart[:cat_axis_title]
|
|
1275
|
+
cat_title_spec = merge_flat_title_styling(chart[:cat_axis_title], chart, :cat_axis_title)
|
|
1276
|
+
parts << build_chart_title_xml(cat_title_spec)
|
|
1277
|
+
end
|
|
1278
|
+
if (cnf = chart[:cat_axis_num_fmt])
|
|
1279
|
+
sl = cnf[:source_linked] ? 1 : 0
|
|
1280
|
+
parts << %(<c:numFmt formatCode="#{xml_escape(cnf[:format_code])}" sourceLinked="#{sl}"/>)
|
|
1281
|
+
end
|
|
1282
|
+
parts << %(<c:majorTickMark val="#{chart[:cat_axis_major_tick_mark]}"/>) if chart[:cat_axis_major_tick_mark]
|
|
1283
|
+
parts << %(<c:minorTickMark val="#{chart[:cat_axis_minor_tick_mark]}"/>) if chart[:cat_axis_minor_tick_mark]
|
|
1284
|
+
parts << %(<c:tickLblPos val="#{chart[:cat_axis_tick_lbl_pos]}"/>) if chart[:cat_axis_tick_lbl_pos]
|
|
1285
|
+
if chart[:cat_axis_line_color] || chart[:cat_axis_fill] || chart[:cat_axis_no_fill] || chart[:cat_axis_line_dash]
|
|
1286
|
+
parts << "<c:spPr>"
|
|
1287
|
+
parts << "<a:noFill/>" if chart[:cat_axis_no_fill]
|
|
1288
|
+
parts << %(<a:solidFill>#{color_xml(chart[:cat_axis_fill])}</a:solidFill>) if chart[:cat_axis_fill]
|
|
1289
|
+
if chart[:cat_axis_line_color] || chart[:cat_axis_line_dash]
|
|
1290
|
+
w_attr = chart[:cat_axis_line_width] ? %( w="#{chart[:cat_axis_line_width]}") : ""
|
|
1291
|
+
ca_ln_f = chart[:cat_axis_line_color] ? %(<a:solidFill>#{srgb_clr_xml(chart[:cat_axis_line_color])}</a:solidFill>) : ""
|
|
1292
|
+
ca_ln_d = chart[:cat_axis_line_dash] ? %(<a:prstDash val="#{xml_escape(chart[:cat_axis_line_dash])}"/>) : ""
|
|
1293
|
+
parts << "<a:ln#{w_attr}>#{ca_ln_f}#{ca_ln_d}</a:ln>"
|
|
1294
|
+
end
|
|
1295
|
+
parts << "</c:spPr>"
|
|
1296
|
+
end
|
|
1297
|
+
parts << build_axis_txpr(chart[:cat_axis_label_rotation], chart[:cat_axis_font])
|
|
1298
|
+
parts << '<c:crossAx val="2"/>'
|
|
1299
|
+
parts << %(<c:crosses val="#{chart[:cat_axis_crosses]}"/>) if chart[:cat_axis_crosses]
|
|
1300
|
+
parts << %(<c:crossesAt val="#{chart[:cat_axis_crosses_at]}"/>) if !chart[:cat_axis_crosses] && chart[:cat_axis_crosses_at]
|
|
1301
|
+
ca_auto = chart[:cat_axis_auto]
|
|
1302
|
+
parts << %(<c:auto val="#{ca_auto ? 1 : 0}"/>) unless ca_auto.nil?
|
|
1303
|
+
parts << %(<c:lblAlgn val="#{xml_escape(chart[:cat_axis_lbl_algn])}"/>) if chart[:cat_axis_lbl_algn]
|
|
1304
|
+
parts << %(<c:lblOffset val="#{chart[:cat_axis_lbl_offset]}"/>) if chart[:cat_axis_lbl_offset]
|
|
1305
|
+
parts << %(<c:tickLblSkip val="#{chart[:cat_axis_tick_lbl_skip]}"/>) if chart[:cat_axis_tick_lbl_skip]
|
|
1306
|
+
parts << %(<c:tickMarkSkip val="#{chart[:cat_axis_tick_mark_skip]}"/>) if chart[:cat_axis_tick_mark_skip]
|
|
1307
|
+
nml = chart[:cat_axis_no_multi_lvl_lbl]
|
|
1308
|
+
parts << %(<c:noMultiLvlLbl val="#{nml ? 1 : 0}"/>) unless nml.nil?
|
|
1309
|
+
if cat_ax_tag == "dateAx"
|
|
1310
|
+
parts << %(<c:baseTimeUnit val="#{chart[:cat_axis_base_time_unit]}"/>) if chart[:cat_axis_base_time_unit]
|
|
1311
|
+
parts << %(<c:majorUnit val="#{chart[:cat_axis_major_unit]}"/>) if chart[:cat_axis_major_unit]
|
|
1312
|
+
parts << %(<c:majorTimeUnit val="#{chart[:cat_axis_major_time_unit]}"/>) if chart[:cat_axis_major_time_unit]
|
|
1313
|
+
parts << %(<c:minorUnit val="#{chart[:cat_axis_minor_unit]}"/>) if chart[:cat_axis_minor_unit]
|
|
1314
|
+
parts << %(<c:minorTimeUnit val="#{chart[:cat_axis_minor_time_unit]}"/>) if chart[:cat_axis_minor_time_unit]
|
|
1315
|
+
end
|
|
1316
|
+
parts << "</c:#{cat_ax_tag}>"
|
|
1317
|
+
val_del = chart[:val_axis_delete] ? 1 : 0
|
|
1318
|
+
val_orient = chart[:val_axis_orientation] || "minMax"
|
|
1319
|
+
parts << %(<c:valAx><c:axId val="2"/><c:scaling>)
|
|
1320
|
+
parts << %(<c:logBase val="#{chart[:val_axis_log_base]}"/>) if chart[:val_axis_log_base]
|
|
1321
|
+
parts << %(<c:orientation val="#{val_orient}"/>)
|
|
1322
|
+
parts << %(<c:max val="#{chart[:val_axis_scaling_max]}"/>) if chart[:val_axis_scaling_max]
|
|
1323
|
+
parts << %(<c:min val="#{chart[:val_axis_scaling_min]}"/>) if chart[:val_axis_scaling_min]
|
|
1324
|
+
parts << %(</c:scaling><c:delete val="#{val_del}"/><c:axPos val="#{chart[:val_axis_pos] || "l"}"/>)
|
|
1325
|
+
parts << gridlines_xml("majorGridlines", chart[:val_axis_major_gridlines])
|
|
1326
|
+
parts << gridlines_xml("minorGridlines", chart[:val_axis_minor_gridlines])
|
|
1327
|
+
if chart[:val_axis_title]
|
|
1328
|
+
val_title_spec = merge_flat_title_styling(chart[:val_axis_title], chart, :val_axis_title)
|
|
1329
|
+
parts << build_chart_title_xml(val_title_spec)
|
|
1330
|
+
end
|
|
1331
|
+
if (vnf = chart[:val_axis_num_fmt])
|
|
1332
|
+
sl = vnf[:source_linked] ? 1 : 0
|
|
1333
|
+
parts << %(<c:numFmt formatCode="#{xml_escape(vnf[:format_code])}" sourceLinked="#{sl}"/>)
|
|
1334
|
+
end
|
|
1335
|
+
parts << %(<c:majorTickMark val="#{chart[:val_axis_major_tick_mark]}"/>) if chart[:val_axis_major_tick_mark]
|
|
1336
|
+
parts << %(<c:minorTickMark val="#{chart[:val_axis_minor_tick_mark]}"/>) if chart[:val_axis_minor_tick_mark]
|
|
1337
|
+
parts << %(<c:tickLblPos val="#{chart[:val_axis_tick_lbl_pos]}"/>) if chart[:val_axis_tick_lbl_pos]
|
|
1338
|
+
if chart[:val_axis_line_color] || chart[:val_axis_fill] || chart[:val_axis_no_fill] || chart[:val_axis_line_dash]
|
|
1339
|
+
parts << "<c:spPr>"
|
|
1340
|
+
parts << "<a:noFill/>" if chart[:val_axis_no_fill]
|
|
1341
|
+
parts << %(<a:solidFill>#{color_xml(chart[:val_axis_fill])}</a:solidFill>) if chart[:val_axis_fill]
|
|
1342
|
+
if chart[:val_axis_line_color] || chart[:val_axis_line_dash]
|
|
1343
|
+
w_attr = chart[:val_axis_line_width] ? %( w="#{chart[:val_axis_line_width]}") : ""
|
|
1344
|
+
va_ln_f = chart[:val_axis_line_color] ? %(<a:solidFill>#{srgb_clr_xml(chart[:val_axis_line_color])}</a:solidFill>) : ""
|
|
1345
|
+
va_ln_d = chart[:val_axis_line_dash] ? %(<a:prstDash val="#{xml_escape(chart[:val_axis_line_dash])}"/>) : ""
|
|
1346
|
+
parts << "<a:ln#{w_attr}>#{va_ln_f}#{va_ln_d}</a:ln>"
|
|
1347
|
+
end
|
|
1348
|
+
parts << "</c:spPr>"
|
|
1349
|
+
end
|
|
1350
|
+
parts << build_axis_txpr(chart[:val_axis_label_rotation], chart[:val_axis_font])
|
|
1351
|
+
parts << '<c:crossAx val="1"/>'
|
|
1352
|
+
parts << %(<c:crosses val="#{chart[:val_axis_crosses]}"/>) if chart[:val_axis_crosses]
|
|
1353
|
+
parts << %(<c:crossesAt val="#{chart[:val_axis_crosses_at]}"/>) if !chart[:val_axis_crosses] && chart[:val_axis_crosses_at]
|
|
1354
|
+
parts << %(<c:crossBetween val="#{chart[:val_axis_cross_between]}"/>) if chart[:val_axis_cross_between]
|
|
1355
|
+
parts << %(<c:majorUnit val="#{chart[:val_axis_major_unit]}"/>) if chart[:val_axis_major_unit]
|
|
1356
|
+
parts << %(<c:minorUnit val="#{chart[:val_axis_minor_unit]}"/>) if chart[:val_axis_minor_unit]
|
|
1357
|
+
if chart[:val_axis_disp_units]
|
|
1358
|
+
du = chart[:val_axis_disp_units]
|
|
1359
|
+
if du.is_a?(Hash)
|
|
1360
|
+
parts << "<c:dispUnits>"
|
|
1361
|
+
parts << %(<c:builtInUnit val="#{xml_escape(du[:built_in_unit])}"/>) if du[:built_in_unit]
|
|
1362
|
+
parts << %(<c:custUnit val="#{du[:cust_unit]}"/>) if du[:cust_unit]
|
|
1363
|
+
if du[:label]
|
|
1364
|
+
dul = du[:label]
|
|
1365
|
+
parts << "<c:dispUnitsLbl>"
|
|
1366
|
+
if dul[:num_fmt]
|
|
1367
|
+
nf = dul[:num_fmt]
|
|
1368
|
+
nf_src = nf.is_a?(Hash) ? nf : { format_code: nf }
|
|
1369
|
+
nf_linked = if nf_src.key?(:source_linked)
|
|
1370
|
+
nf_src[:source_linked] ? 1 : 0
|
|
1371
|
+
else
|
|
1372
|
+
0
|
|
1373
|
+
end
|
|
1374
|
+
parts << %(<c:numFmt formatCode="#{xml_escape(nf_src[:format_code])}" sourceLinked="#{nf_linked}"/>)
|
|
1375
|
+
end
|
|
1376
|
+
dul_sp = +""
|
|
1377
|
+
dul_sp << %(<a:solidFill>#{color_xml(dul[:fill_color])}</a:solidFill>) if dul[:fill_color]
|
|
1378
|
+
dul_sp << "<a:noFill/>" if dul[:no_fill]
|
|
1379
|
+
if dul[:line_color] || dul[:line_width] || dul[:line_dash]
|
|
1380
|
+
dul_lw = dul[:line_width] ? %( w="#{(dul[:line_width] * 12_700).to_i}") : ""
|
|
1381
|
+
dul_lf = dul[:line_color] ? %(<a:solidFill>#{color_xml(dul[:line_color])}</a:solidFill>) : ""
|
|
1382
|
+
dul_ld = dul[:line_dash] ? %(<a:prstDash val="#{xml_escape(dul[:line_dash])}"/>) : ""
|
|
1383
|
+
dul_sp << "<a:ln#{dul_lw}>#{dul_lf}#{dul_ld}</a:ln>"
|
|
1384
|
+
end
|
|
1385
|
+
parts << "<c:spPr>#{dul_sp}</c:spPr>" unless dul_sp.empty?
|
|
1386
|
+
parts << build_axis_txpr(nil, dul[:font]) if dul[:font]
|
|
1387
|
+
parts << "</c:dispUnitsLbl>"
|
|
1388
|
+
end
|
|
1389
|
+
parts << "</c:dispUnits>"
|
|
1390
|
+
else
|
|
1391
|
+
parts << %(<c:dispUnits><c:builtInUnit val="#{xml_escape(du.to_s)}"/></c:dispUnits>)
|
|
1392
|
+
end
|
|
1393
|
+
end
|
|
1394
|
+
parts << "</c:valAx>"
|
|
1395
|
+
|
|
1396
|
+
if three_axes
|
|
1397
|
+
parts << '<c:serAx><c:axId val="3"/><c:scaling><c:orientation val="minMax"/></c:scaling>'
|
|
1398
|
+
parts << '<c:delete val="0"/><c:axPos val="b"/><c:crossAx val="2"/></c:serAx>'
|
|
1399
|
+
end
|
|
1400
|
+
end
|
|
1401
|
+
|
|
1402
|
+
if chart[:data_table]
|
|
1403
|
+
dt = chart[:data_table]
|
|
1404
|
+
parts << "<c:dTable>"
|
|
1405
|
+
parts << %(<c:showHorzBorder val="#{dt[:show_horz_border] ? 1 : 0}"/>) unless dt[:show_horz_border].nil?
|
|
1406
|
+
parts << %(<c:showVertBorder val="#{dt[:show_vert_border] ? 1 : 0}"/>) unless dt[:show_vert_border].nil?
|
|
1407
|
+
parts << %(<c:showOutline val="#{dt[:show_outline] ? 1 : 0}"/>) unless dt[:show_outline].nil?
|
|
1408
|
+
parts << %(<c:showKeys val="#{dt[:show_keys] ? 1 : 0}"/>) unless dt[:show_keys].nil?
|
|
1409
|
+
dt_sp_children = +""
|
|
1410
|
+
dt_sp_children << %(<a:solidFill>#{color_xml(dt[:fill_color])}</a:solidFill>) if dt[:fill_color]
|
|
1411
|
+
dt_sp_children << "<a:noFill/>" if dt[:no_fill]
|
|
1412
|
+
if dt[:line_color] || dt[:line_width] || dt[:line_dash]
|
|
1413
|
+
dt_ln_w = dt[:line_width] ? %( w="#{(dt[:line_width] * 12_700).to_i}") : ""
|
|
1414
|
+
dt_ln_f = dt[:line_color] ? %(<a:solidFill>#{color_xml(dt[:line_color])}</a:solidFill>) : ""
|
|
1415
|
+
dt_ln_d = dt[:line_dash] ? %(<a:prstDash val="#{xml_escape(dt[:line_dash])}"/>) : ""
|
|
1416
|
+
dt_sp_children << "<a:ln#{dt_ln_w}>#{dt_ln_f}#{dt_ln_d}</a:ln>"
|
|
1417
|
+
end
|
|
1418
|
+
parts << "<c:spPr>#{dt_sp_children}</c:spPr>" unless dt_sp_children.empty?
|
|
1419
|
+
parts << build_axis_txpr(nil, dt[:font]) if dt[:font]
|
|
1420
|
+
parts << "</c:dTable>"
|
|
1421
|
+
end
|
|
1422
|
+
|
|
1423
|
+
if chart[:plot_area_fill] || chart[:plot_area_no_fill] || chart[:plot_area_line_color] || chart[:plot_area_line_dash]
|
|
1424
|
+
pa_sp = +""
|
|
1425
|
+
pa_sp << %(<a:solidFill>#{color_xml(chart[:plot_area_fill])}</a:solidFill>) if chart[:plot_area_fill]
|
|
1426
|
+
pa_sp << "<a:noFill/>" if chart[:plot_area_no_fill]
|
|
1427
|
+
if chart[:plot_area_line_color] || chart[:plot_area_line_dash]
|
|
1428
|
+
pa_ln_w = chart[:plot_area_line_width] ? %( w="#{chart[:plot_area_line_width].to_i}") : ""
|
|
1429
|
+
pa_ln_f = chart[:plot_area_line_color] ? %(<a:solidFill>#{color_xml(chart[:plot_area_line_color])}</a:solidFill>) : ""
|
|
1430
|
+
pa_ln_d = chart[:plot_area_line_dash] ? %(<a:prstDash val="#{xml_escape(chart[:plot_area_line_dash])}"/>) : ""
|
|
1431
|
+
pa_sp << "<a:ln#{pa_ln_w}>#{pa_ln_f}#{pa_ln_d}</a:ln>"
|
|
1432
|
+
end
|
|
1433
|
+
parts << "<c:spPr>#{pa_sp}</c:spPr>"
|
|
1434
|
+
end
|
|
1435
|
+
|
|
1436
|
+
parts << "</c:plotArea>"
|
|
1437
|
+
legend_pos = chart.dig(:legend, :position) || "r"
|
|
1438
|
+
legend_overlay = chart.dig(:legend, :overlay)
|
|
1439
|
+
parts << %(<c:legend><c:legendPos val="#{legend_pos}"/>)
|
|
1440
|
+
legend_entries = chart.dig(:legend, :entries)
|
|
1441
|
+
legend_entries&.each do |entry|
|
|
1442
|
+
parts << %(<c:legendEntry><c:idx val="#{entry[:idx]}"/>)
|
|
1443
|
+
if !entry[:delete].nil?
|
|
1444
|
+
parts << %(<c:delete val="#{entry[:delete] ? 1 : 0}"/>)
|
|
1445
|
+
elsif entry[:font]
|
|
1446
|
+
parts << build_axis_txpr(nil, entry[:font])
|
|
1447
|
+
end
|
|
1448
|
+
parts << "</c:legendEntry>"
|
|
1449
|
+
end
|
|
1450
|
+
legend_layout = chart.dig(:legend, :layout)
|
|
1451
|
+
if legend_layout.is_a?(Hash)
|
|
1452
|
+
ml_parts = +""
|
|
1453
|
+
ml_parts << %(<c:layoutTarget val="#{xml_escape(legend_layout[:target])}"/>) if legend_layout[:target]
|
|
1454
|
+
ml_parts << %(<c:xMode val="#{xml_escape(legend_layout[:x_mode])}"/>) if legend_layout[:x_mode]
|
|
1455
|
+
ml_parts << %(<c:yMode val="#{xml_escape(legend_layout[:y_mode])}"/>) if legend_layout[:y_mode]
|
|
1456
|
+
ml_parts << %(<c:wMode val="#{xml_escape(legend_layout[:w_mode])}"/>) if legend_layout[:w_mode]
|
|
1457
|
+
ml_parts << %(<c:hMode val="#{xml_escape(legend_layout[:h_mode])}"/>) if legend_layout[:h_mode]
|
|
1458
|
+
ml_parts << %(<c:x val="#{legend_layout[:x]}"/>) if legend_layout[:x]
|
|
1459
|
+
ml_parts << %(<c:y val="#{legend_layout[:y]}"/>) if legend_layout[:y]
|
|
1460
|
+
ml_parts << %(<c:w val="#{legend_layout[:w]}"/>) if legend_layout[:w]
|
|
1461
|
+
ml_parts << %(<c:h val="#{legend_layout[:h]}"/>) if legend_layout[:h]
|
|
1462
|
+
parts << "<c:layout><c:manualLayout>#{ml_parts}</c:manualLayout></c:layout>" unless ml_parts.empty?
|
|
1463
|
+
end
|
|
1464
|
+
parts << %(<c:overlay val="#{legend_overlay ? 1 : 0}"/>) unless legend_overlay.nil?
|
|
1465
|
+
leg_sp_children = +""
|
|
1466
|
+
leg_fill = chart.dig(:legend, :fill_color)
|
|
1467
|
+
leg_sp_children << %(<a:solidFill>#{color_xml(leg_fill)}</a:solidFill>) if leg_fill
|
|
1468
|
+
leg_no_fill = chart.dig(:legend, :no_fill)
|
|
1469
|
+
leg_sp_children << "<a:noFill/>" if leg_no_fill
|
|
1470
|
+
leg_lc = chart.dig(:legend, :line_color)
|
|
1471
|
+
leg_lw = chart.dig(:legend, :line_width)
|
|
1472
|
+
leg_ld = chart.dig(:legend, :line_dash)
|
|
1473
|
+
if leg_lc || leg_lw || leg_ld
|
|
1474
|
+
lw_attr = leg_lw ? %( w="#{(leg_lw * 12_700).to_i}") : ""
|
|
1475
|
+
lf = leg_lc ? %(<a:solidFill>#{color_xml(leg_lc)}</a:solidFill>) : ""
|
|
1476
|
+
ld = leg_ld ? %(<a:prstDash val="#{xml_escape(leg_ld)}"/>) : ""
|
|
1477
|
+
leg_sp_children << "<a:ln#{lw_attr}>#{lf}#{ld}</a:ln>"
|
|
1478
|
+
end
|
|
1479
|
+
parts << "<c:spPr>#{leg_sp_children}</c:spPr>" unless leg_sp_children.empty?
|
|
1480
|
+
if (lfont = chart.dig(:legend, :font))
|
|
1481
|
+
parts << build_axis_txpr(nil, lfont)
|
|
1482
|
+
end
|
|
1483
|
+
parts << %(</c:legend>)
|
|
1484
|
+
pvo = chart[:plot_vis_only]
|
|
1485
|
+
parts << %(<c:plotVisOnly val="#{pvo ? 1 : 0}"/>) unless pvo.nil?
|
|
1486
|
+
parts << %(<c:dispBlanksAs val="#{chart[:disp_blanks_as]}"/>) if chart[:disp_blanks_as]
|
|
1487
|
+
sdom = chart[:show_d_lbls_over_max]
|
|
1488
|
+
parts << %(<c:showDLblsOverMax val="#{sdom ? 1 : 0}"/>) unless sdom.nil?
|
|
1489
|
+
parts << "</c:chart>"
|
|
1490
|
+
if chart[:chart_fill] || chart[:chart_no_fill] || chart[:chart_line_color] || chart[:chart_line_dash]
|
|
1491
|
+
cs_sp = +""
|
|
1492
|
+
cs_sp << %(<a:solidFill>#{color_xml(chart[:chart_fill])}</a:solidFill>) if chart[:chart_fill]
|
|
1493
|
+
cs_sp << "<a:noFill/>" if chart[:chart_no_fill]
|
|
1494
|
+
if chart[:chart_line_color] || chart[:chart_line_dash]
|
|
1495
|
+
cs_lw = chart[:chart_line_width] ? %( w="#{(chart[:chart_line_width] * 12_700).to_i}") : ""
|
|
1496
|
+
cs_ln_f = chart[:chart_line_color] ? %(<a:solidFill>#{color_xml(chart[:chart_line_color])}</a:solidFill>) : ""
|
|
1497
|
+
cs_ln_d = chart[:chart_line_dash] ? %(<a:prstDash val="#{xml_escape(chart[:chart_line_dash])}"/>) : ""
|
|
1498
|
+
cs_sp << "<a:ln#{cs_lw}>#{cs_ln_f}#{cs_ln_d}</a:ln>"
|
|
1499
|
+
end
|
|
1500
|
+
parts << "<c:spPr>#{cs_sp}</c:spPr>"
|
|
1501
|
+
end
|
|
1502
|
+
parts << build_axis_txpr(nil, chart[:chart_font]) if chart[:chart_font]
|
|
1503
|
+
if (ps = chart[:print_settings])
|
|
1504
|
+
parts << "<c:printSettings>"
|
|
1505
|
+
if (hf = ps[:header_footer])
|
|
1506
|
+
parts << "<c:headerFooter>"
|
|
1507
|
+
parts << "<c:oddHeader>#{xml_escape(hf[:odd_header])}</c:oddHeader>" if hf[:odd_header]
|
|
1508
|
+
parts << "<c:oddFooter>#{xml_escape(hf[:odd_footer])}</c:oddFooter>" if hf[:odd_footer]
|
|
1509
|
+
parts << "<c:evenHeader>#{xml_escape(hf[:even_header])}</c:evenHeader>" if hf[:even_header]
|
|
1510
|
+
parts << "<c:evenFooter>#{xml_escape(hf[:even_footer])}</c:evenFooter>" if hf[:even_footer]
|
|
1511
|
+
parts << "<c:firstHeader>#{xml_escape(hf[:first_header])}</c:firstHeader>" if hf[:first_header]
|
|
1512
|
+
parts << "<c:firstFooter>#{xml_escape(hf[:first_footer])}</c:firstFooter>" if hf[:first_footer]
|
|
1513
|
+
parts << "</c:headerFooter>"
|
|
1514
|
+
end
|
|
1515
|
+
if (pm = ps[:page_margins])
|
|
1516
|
+
pm_attrs = %w[b l r t header footer].filter_map { |a| pm[a.to_sym] ? %( #{a}="#{pm[a.to_sym]}") : nil }.join
|
|
1517
|
+
parts << "<c:pageMargins#{pm_attrs}/>"
|
|
1518
|
+
end
|
|
1519
|
+
if (psu = ps[:page_setup])
|
|
1520
|
+
psu_parts = +""
|
|
1521
|
+
psu_parts << %( paperSize="#{psu[:paper_size]}") if psu[:paper_size]
|
|
1522
|
+
psu_parts << %( firstPageNumber="#{psu[:first_page_number]}") if psu[:first_page_number]
|
|
1523
|
+
psu_parts << %( orientation="#{xml_escape(psu[:orientation])}") if psu[:orientation]
|
|
1524
|
+
psu_parts << %( horizontalDpi="#{psu[:horizontal_dpi]}") if psu[:horizontal_dpi]
|
|
1525
|
+
psu_parts << %( verticalDpi="#{psu[:vertical_dpi]}") if psu[:vertical_dpi]
|
|
1526
|
+
psu_parts << %( copies="#{psu[:copies]}") if psu[:copies]
|
|
1527
|
+
parts << "<c:pageSetup#{psu_parts}/>"
|
|
1528
|
+
end
|
|
1529
|
+
parts << "</c:printSettings>"
|
|
1530
|
+
end
|
|
1531
|
+
parts << "</c:chartSpace>"
|
|
1532
|
+
parts.join
|
|
1533
|
+
end
|
|
1534
|
+
|
|
1535
|
+
# : (untyped sheet_comments) -> untyped
|
|
1536
|
+
def generate_comments_xml(sheet_comments)
|
|
1537
|
+
authors = sheet_comments.map { |c| c[:author] }.uniq
|
|
1538
|
+
parts = [
|
|
1539
|
+
XML_HEADER,
|
|
1540
|
+
%(<comments xmlns="#{SSML_NS}">),
|
|
1541
|
+
"<authors>"
|
|
1542
|
+
]
|
|
1543
|
+
authors.each { |a| parts << "<author>#{xml_escape(a)}</author>" }
|
|
1544
|
+
parts << "</authors><commentList>"
|
|
1545
|
+
sheet_comments.each do |c|
|
|
1546
|
+
aid = authors.index(c[:author]) || 0
|
|
1547
|
+
text_xml = if c[:text].is_a?(Xlsxrb::Elements::RichText)
|
|
1548
|
+
rich_text_xml(c[:text])
|
|
1549
|
+
else
|
|
1550
|
+
"<r><t>#{xml_escape(c[:text])}</t></r>"
|
|
1551
|
+
end
|
|
1552
|
+
comment_attrs = %(ref="#{c[:ref]}" authorId="#{aid}")
|
|
1553
|
+
comment_attrs << %( guid="#{c[:guid]}") if c[:guid]
|
|
1554
|
+
comment_attrs << %( shapeId="#{c[:shape_id]}") if c[:shape_id]
|
|
1555
|
+
parts << "<comment #{comment_attrs}><text>#{text_xml}</text></comment>"
|
|
1556
|
+
end
|
|
1557
|
+
parts << "</commentList></comments>"
|
|
1558
|
+
parts.join
|
|
1559
|
+
end
|
|
1560
|
+
|
|
1561
|
+
# : (untyped sheet_comments) -> untyped
|
|
1562
|
+
def generate_vml_drawing_xml(sheet_comments)
|
|
1563
|
+
parts = [
|
|
1564
|
+
'<xml xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel">',
|
|
1565
|
+
'<o:shapelayout v:ext="edit"><o:idmap v:ext="edit" data="1"/></o:shapelayout>',
|
|
1566
|
+
'<v:shapetype id="_x0000_t202" coordsize="21600,21600" o:spt="202" path="m,l,21600r21600,l21600,xe">',
|
|
1567
|
+
'<v:stroke joinstyle="miter"/><v:path gradientshapeok="t" o:connecttype="rect"/>',
|
|
1568
|
+
"</v:shapetype>"
|
|
1569
|
+
]
|
|
1570
|
+
sheet_comments.each_with_index do |c, idx|
|
|
1571
|
+
col, row = cell_to_col_row(c[:ref])
|
|
1572
|
+
shape_id = 1025 + idx
|
|
1573
|
+
parts << %(<v:shape id="_x0000_s#{shape_id}" type="#_x0000_t202" style="position:absolute;margin-left:59.25pt;margin-top:1.5pt;width:108pt;height:59.25pt;z-index:#{idx + 1};visibility:hidden" fillcolor="#ffffe1" o:insetmode="auto">)
|
|
1574
|
+
parts << '<v:fill color2="#ffffe1"/>'
|
|
1575
|
+
parts << '<v:shadow on="t" color="black" obscured="t"/>'
|
|
1576
|
+
parts << '<v:path o:connecttype="none"/>'
|
|
1577
|
+
parts << '<v:textbox style="mso-direction-alt:auto"><div style="text-align:left"></div></v:textbox>'
|
|
1578
|
+
parts << '<x:ClientData ObjectType="Note">'
|
|
1579
|
+
parts << "<x:MoveWithCells/><x:SizeWithCells/>"
|
|
1580
|
+
parts << "<x:Anchor>#{col + 1}, 15, #{row}, 10, #{col + 3}, 15, #{row + 4}, 4</x:Anchor>"
|
|
1581
|
+
parts << "<x:AutoFill>False</x:AutoFill>"
|
|
1582
|
+
parts << "<x:Row>#{row}</x:Row>"
|
|
1583
|
+
parts << "<x:Column>#{col}</x:Column>"
|
|
1584
|
+
parts << "</x:ClientData></v:shape>"
|
|
1585
|
+
end
|
|
1586
|
+
parts << "</xml>"
|
|
1587
|
+
parts.join
|
|
1588
|
+
end
|
|
1589
|
+
|
|
1590
|
+
# : (untyped cell_ref) -> (::Array[0] | ::Array[untyped])
|
|
1591
|
+
def cell_to_col_row(cell_ref)
|
|
1592
|
+
m = cell_ref.match(/\A([A-Z]+)(\d+)\z/)
|
|
1593
|
+
return [0, 0] unless m
|
|
1594
|
+
|
|
1595
|
+
col = column_letter_to_index(m[1]) - 1
|
|
1596
|
+
row = m[2].to_i - 1
|
|
1597
|
+
[col, row]
|
|
1598
|
+
end
|
|
1599
|
+
end
|
|
1600
|
+
end
|
|
1601
|
+
end
|
|
1602
|
+
end
|