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
data/lib/xlsxrb/ooxml/writer.rb
CHANGED
|
@@ -3,11 +3,19 @@
|
|
|
3
3
|
# rbs_inline: enabled
|
|
4
4
|
|
|
5
5
|
require_relative "zip_generator"
|
|
6
|
+
require_relative "writer/drawing_xml"
|
|
7
|
+
require_relative "writer/features_xml"
|
|
8
|
+
require_relative "writer/styles_xml"
|
|
9
|
+
require_relative "../dsl_helpers"
|
|
6
10
|
|
|
7
11
|
module Xlsxrb
|
|
8
12
|
module Ooxml
|
|
9
13
|
# Writes spreadsheet data into an XLSX file.
|
|
10
14
|
class Writer
|
|
15
|
+
include DrawingXml
|
|
16
|
+
include FeaturesXml
|
|
17
|
+
include StylesXml
|
|
18
|
+
|
|
11
19
|
XML_HEADER = %(<?xml version="1.0" encoding="UTF-8"?>)
|
|
12
20
|
CT_NS = "http://schemas.openxmlformats.org/package/2006/content-types"
|
|
13
21
|
REL_NS = "http://schemas.openxmlformats.org/package/2006/relationships"
|
|
@@ -2992,2651 +3000,6 @@ module Xlsxrb
|
|
|
2992
3000
|
parts.join
|
|
2993
3001
|
end
|
|
2994
3002
|
|
|
2995
|
-
# : (untyped drawing_parts) -> untyped
|
|
2996
|
-
def generate_drawing_xml(drawing_parts)
|
|
2997
|
-
parts = [
|
|
2998
|
-
XML_HEADER,
|
|
2999
|
-
%(<xdr:wsDr xmlns:xdr="#{XDR_NS}" xmlns:a="#{A_NS}" xmlns:r="#{DOC_REL_NS}">)
|
|
3000
|
-
]
|
|
3001
|
-
|
|
3002
|
-
drawing_parts.each do |dp|
|
|
3003
|
-
case dp[:kind]
|
|
3004
|
-
when :pic
|
|
3005
|
-
img = dp[:img]
|
|
3006
|
-
rid = "rId#{dp[:rid_index]}"
|
|
3007
|
-
ea = img[:edit_as] || "oneCell"
|
|
3008
|
-
img_pub_attr = img[:published] ? ' fPublished="1"' : ""
|
|
3009
|
-
parts << %(<xdr:twoCellAnchor editAs="#{xml_escape(ea)}"#{img_pub_attr}>)
|
|
3010
|
-
parts << anchor_xml("from", img[:from_col], img[:from_row], col_off: img[:from_col_off] || 0, row_off: img[:from_row_off] || 0)
|
|
3011
|
-
parts << anchor_xml("to", img[:to_col], img[:to_row], col_off: img[:to_col_off] || 0, row_off: img[:to_row_off] || 0)
|
|
3012
|
-
macro_attr = img[:macro] ? %( macro="#{xml_escape(img[:macro])}") : ""
|
|
3013
|
-
parts << "<xdr:pic#{macro_attr}>"
|
|
3014
|
-
descr_attr = img[:description] ? %( descr="#{xml_escape(img[:description])}") : ""
|
|
3015
|
-
title_attr = img[:title] ? %( title="#{xml_escape(img[:title])}") : ""
|
|
3016
|
-
hidden_attr = img[:hidden] ? ' hidden="1"' : ""
|
|
3017
|
-
pic_lock_attrs = +""
|
|
3018
|
-
pic_lock_attrs << ' noChangeAspect="1"' if img[:no_change_aspect]
|
|
3019
|
-
pic_lock_attrs << ' noCrop="1"' if img[:no_crop]
|
|
3020
|
-
pic_locks = pic_lock_attrs.empty? ? "<a:picLocks/>" : "<a:picLocks#{pic_lock_attrs}/>"
|
|
3021
|
-
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>)
|
|
3022
|
-
src_rect_xml = if img[:src_rect]
|
|
3023
|
-
sr = img[:src_rect]
|
|
3024
|
-
sr_attrs = +""
|
|
3025
|
-
sr_attrs << %( t="#{sr[:top]}") if sr[:top]
|
|
3026
|
-
sr_attrs << %( b="#{sr[:bottom]}") if sr[:bottom]
|
|
3027
|
-
sr_attrs << %( l="#{sr[:left]}") if sr[:left]
|
|
3028
|
-
sr_attrs << %( r="#{sr[:right]}") if sr[:right]
|
|
3029
|
-
"<a:srcRect#{sr_attrs}/>"
|
|
3030
|
-
else
|
|
3031
|
-
""
|
|
3032
|
-
end
|
|
3033
|
-
blip_xml = if img[:alpha_mod_fix]
|
|
3034
|
-
%(<a:blip r:embed="#{rid}"><a:alphaModFix amt="#{img[:alpha_mod_fix]}"/></a:blip>)
|
|
3035
|
-
else
|
|
3036
|
-
%(<a:blip r:embed="#{rid}"/>)
|
|
3037
|
-
end
|
|
3038
|
-
parts << %(<xdr:blipFill>#{blip_xml}#{src_rect_xml}<a:stretch><a:fillRect/></a:stretch></xdr:blipFill>)
|
|
3039
|
-
img_line_xml = if img[:line_color]
|
|
3040
|
-
ln_w_attr = img[:line_width] ? %( w="#{img[:line_width].to_i}") : ""
|
|
3041
|
-
%(<a:ln#{ln_w_attr}><a:solidFill>#{color_xml(img[:line_color])}</a:solidFill></a:ln>)
|
|
3042
|
-
else
|
|
3043
|
-
""
|
|
3044
|
-
end
|
|
3045
|
-
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>) : ""
|
|
3046
|
-
parts << %(<xdr:spPr>#{img_xfrm_xml}<a:prstGeom prst="rect"><a:avLst/></a:prstGeom>#{img_line_xml}</xdr:spPr>)
|
|
3047
|
-
parts << "</xdr:pic>"
|
|
3048
|
-
parts << client_data_xml(img)
|
|
3049
|
-
parts << "</xdr:twoCellAnchor>"
|
|
3050
|
-
when :chart
|
|
3051
|
-
chart = dp[:chart]
|
|
3052
|
-
rid = "rId#{dp[:rid_index]}"
|
|
3053
|
-
chart_ea_attr = chart[:edit_as] ? %( editAs="#{xml_escape(chart[:edit_as])}") : ""
|
|
3054
|
-
chart_pub_attr = chart[:published] ? ' fPublished="1"' : ""
|
|
3055
|
-
parts << "<xdr:twoCellAnchor#{chart_ea_attr}#{chart_pub_attr}>"
|
|
3056
|
-
parts << anchor_xml("from", chart[:from_col], chart[:from_row], col_off: chart[:from_col_off] || 0, row_off: chart[:from_row_off] || 0)
|
|
3057
|
-
parts << anchor_xml("to", chart[:to_col], chart[:to_row], col_off: chart[:to_col_off] || 0, row_off: chart[:to_row_off] || 0)
|
|
3058
|
-
gf_macro = chart[:frame_macro] ? xml_escape(chart[:frame_macro]) : ""
|
|
3059
|
-
parts << %(<xdr:graphicFrame macro="#{gf_macro}">)
|
|
3060
|
-
chart_frame_name = chart[:name] || chart[:title] || "Chart"
|
|
3061
|
-
chart_descr_attr = chart[:description] ? %( descr="#{xml_escape(chart[:description])}") : ""
|
|
3062
|
-
chart_title_attr = chart[:frame_title] ? %( title="#{xml_escape(chart[:frame_title])}") : ""
|
|
3063
|
-
chart_hidden_attr = chart[:frame_hidden] ? ' hidden="1"' : ""
|
|
3064
|
-
cnv_gf_pr = if chart[:frame_no_grp]
|
|
3065
|
-
'<xdr:cNvGraphicFramePr><a:graphicFrameLocks noGrp="1"/></xdr:cNvGraphicFramePr>'
|
|
3066
|
-
else
|
|
3067
|
-
"<xdr:cNvGraphicFramePr/>"
|
|
3068
|
-
end
|
|
3069
|
-
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>)
|
|
3070
|
-
parts << '<xdr:xfrm><a:off x="0" y="0"/><a:ext cx="5000000" cy="3000000"/></xdr:xfrm>'
|
|
3071
|
-
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>)
|
|
3072
|
-
parts << "</xdr:graphicFrame>"
|
|
3073
|
-
parts << client_data_xml(chart)
|
|
3074
|
-
parts << "</xdr:twoCellAnchor>"
|
|
3075
|
-
when :sp
|
|
3076
|
-
shape = dp[:shape]
|
|
3077
|
-
shape_ea_attr = shape[:edit_as] ? %( editAs="#{xml_escape(shape[:edit_as])}") : ""
|
|
3078
|
-
shape_pub_attr = shape[:published] ? ' fPublished="1"' : ""
|
|
3079
|
-
parts << "<xdr:twoCellAnchor#{shape_ea_attr}#{shape_pub_attr}>"
|
|
3080
|
-
parts << anchor_xml("from", shape[:from_col], shape[:from_row], col_off: shape[:from_col_off] || 0, row_off: shape[:from_row_off] || 0)
|
|
3081
|
-
parts << anchor_xml("to", shape[:to_col], shape[:to_row], col_off: shape[:to_col_off] || 0, row_off: shape[:to_row_off] || 0)
|
|
3082
|
-
sp_macro_attr = shape[:macro] ? %( macro="#{xml_escape(shape[:macro])}") : ""
|
|
3083
|
-
sp_textlink_attr = shape[:textlink] ? %( textlink="#{xml_escape(shape[:textlink])}") : ""
|
|
3084
|
-
parts << "<xdr:sp#{sp_macro_attr}#{sp_textlink_attr}>"
|
|
3085
|
-
shape_descr_attr = shape[:description] ? %( descr="#{xml_escape(shape[:description])}") : ""
|
|
3086
|
-
shape_title_attr = shape[:title] ? %( title="#{xml_escape(shape[:title])}") : ""
|
|
3087
|
-
shape_hidden_attr = shape[:hidden] ? ' hidden="1"' : ""
|
|
3088
|
-
sp_lock_attrs = +""
|
|
3089
|
-
sp_lock_attrs << ' noGrp="1"' if shape[:no_grp]
|
|
3090
|
-
sp_lock_attrs << ' noRot="1"' if shape[:no_rot]
|
|
3091
|
-
sp_lock_attrs << ' fLocksText="1"' if shape[:f_locks_text]
|
|
3092
|
-
cnv_sp_pr = if sp_lock_attrs.empty?
|
|
3093
|
-
"<xdr:cNvSpPr/>"
|
|
3094
|
-
else
|
|
3095
|
-
"<xdr:cNvSpPr><a:spLocks#{sp_lock_attrs}/></xdr:cNvSpPr>"
|
|
3096
|
-
end
|
|
3097
|
-
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>)
|
|
3098
|
-
shape_fill_xml = if shape[:no_fill]
|
|
3099
|
-
"<a:noFill/>"
|
|
3100
|
-
elsif shape[:fill_color]
|
|
3101
|
-
"<a:solidFill>#{srgb_clr_xml(shape[:fill_color], alpha: shape[:fill_alpha], transforms: shape[:fill_color_transforms])}</a:solidFill>"
|
|
3102
|
-
elsif shape[:gradient_fill]
|
|
3103
|
-
gf = shape[:gradient_fill]
|
|
3104
|
-
gf_attrs = +""
|
|
3105
|
-
gf_attrs << %( rotWithShape="#{gf[:rot_with_shape] ? 1 : 0}") unless gf[:rot_with_shape].nil?
|
|
3106
|
-
gf_attrs << %( flip="#{xml_escape(gf[:flip])}") if gf[:flip]
|
|
3107
|
-
gs_xml = (gf[:stops] || []).map { |gs| %(<a:gs pos="#{gs[:pos]}">#{color_xml(gs[:color])}</a:gs>) }.join
|
|
3108
|
-
type_xml = if gf[:path]
|
|
3109
|
-
%(<a:path path="#{xml_escape(gf[:path])}"/>)
|
|
3110
|
-
elsif gf[:angle]
|
|
3111
|
-
scaled_attr = gf[:scaled] ? %( scaled="1") : %( scaled="0")
|
|
3112
|
-
%(<a:lin ang="#{gf[:angle]}"#{scaled_attr}/>)
|
|
3113
|
-
else
|
|
3114
|
-
""
|
|
3115
|
-
end
|
|
3116
|
-
tile_xml = gf[:tile_rect] ? %(<a:tileRect#{gf[:tile_rect].map { |k, v| %( #{k}="#{v}") }.join}/>) : ""
|
|
3117
|
-
"<a:gradFill#{gf_attrs}><a:gsLst>#{gs_xml}</a:gsLst>#{type_xml}#{tile_xml}</a:gradFill>"
|
|
3118
|
-
elsif shape[:pattern_fill]
|
|
3119
|
-
pf = shape[:pattern_fill]
|
|
3120
|
-
pf_children = +""
|
|
3121
|
-
pf_children << %(<a:fgClr>#{color_xml(pf[:fg_color])}</a:fgClr>) if pf[:fg_color]
|
|
3122
|
-
pf_children << %(<a:bgClr>#{color_xml(pf[:bg_color])}</a:bgClr>) if pf[:bg_color]
|
|
3123
|
-
%(<a:pattFill prst="#{xml_escape(pf[:preset])}">#{pf_children}</a:pattFill>)
|
|
3124
|
-
else
|
|
3125
|
-
""
|
|
3126
|
-
end
|
|
3127
|
-
shape_line_xml = if shape[:no_line]
|
|
3128
|
-
"<a:ln><a:noFill/></a:ln>"
|
|
3129
|
-
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]
|
|
3130
|
-
ln_attrs = +(shape[:line_width] ? %( w="#{shape[:line_width].to_i}") : "")
|
|
3131
|
-
ln_attrs << %( cap="#{xml_escape(shape[:line_cap])}") if shape[:line_cap]
|
|
3132
|
-
ln_attrs << %( algn="#{xml_escape(shape[:line_align])}") if shape[:line_align]
|
|
3133
|
-
ln_attrs << %( cmpd="#{xml_escape(shape[:line_compound])}") if shape[:line_compound]
|
|
3134
|
-
fill_part = if shape[:line_color]
|
|
3135
|
-
"<a:solidFill>#{srgb_clr_xml(shape[:line_color], alpha: shape[:line_alpha], transforms: shape[:line_color_transforms])}</a:solidFill>"
|
|
3136
|
-
else
|
|
3137
|
-
""
|
|
3138
|
-
end
|
|
3139
|
-
dash_part = if shape[:line_custom_dash]
|
|
3140
|
-
ds_xml = shape[:line_custom_dash].map { |ds| %(<a:ds d="#{ds[:d]}" sp="#{ds[:sp]}"/>) }.join
|
|
3141
|
-
"<a:custDash>#{ds_xml}</a:custDash>"
|
|
3142
|
-
elsif shape[:line_dash]
|
|
3143
|
-
%(<a:prstDash val="#{xml_escape(shape[:line_dash])}"/>)
|
|
3144
|
-
else
|
|
3145
|
-
""
|
|
3146
|
-
end
|
|
3147
|
-
head_part = build_line_end_xml("headEnd", shape[:head_end])
|
|
3148
|
-
tail_part = build_line_end_xml("tailEnd", shape[:tail_end])
|
|
3149
|
-
join_part = case shape[:line_join]
|
|
3150
|
-
when "round" then "<a:round/>"
|
|
3151
|
-
when "bevel" then "<a:bevel/>"
|
|
3152
|
-
when "miter"
|
|
3153
|
-
shape[:line_miter_limit] ? "<a:miter lim=\"#{shape[:line_miter_limit].to_i}\"/>" : "<a:miter/>"
|
|
3154
|
-
else ""
|
|
3155
|
-
end
|
|
3156
|
-
%(<a:ln#{ln_attrs}>#{fill_part}#{dash_part}#{head_part}#{tail_part}#{join_part}</a:ln>)
|
|
3157
|
-
else
|
|
3158
|
-
""
|
|
3159
|
-
end
|
|
3160
|
-
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>) : ""
|
|
3161
|
-
av_lst_xml = if shape[:adjust_values]&.any?
|
|
3162
|
-
gds = shape[:adjust_values].map { |gd| %(<a:gd name="#{xml_escape(gd[:name])}" fmla="#{xml_escape(gd[:fmla])}"/>) }
|
|
3163
|
-
"<a:avLst>#{gds.join}</a:avLst>"
|
|
3164
|
-
else
|
|
3165
|
-
"<a:avLst/>"
|
|
3166
|
-
end
|
|
3167
|
-
effect_lst_xml = if shape[:outer_shadow] || shape[:inner_shadow] || shape[:glow] || shape[:soft_edge] || shape[:reflection] || shape[:blur]
|
|
3168
|
-
effect_children = +""
|
|
3169
|
-
if shape[:outer_shadow]
|
|
3170
|
-
os = shape[:outer_shadow]
|
|
3171
|
-
os_attrs = +""
|
|
3172
|
-
os_attrs << %( blurRad="#{os[:blur_rad]}") if os[:blur_rad]
|
|
3173
|
-
os_attrs << %( dist="#{os[:dist]}") if os[:dist]
|
|
3174
|
-
os_attrs << %( dir="#{os[:dir]}") if os[:dir]
|
|
3175
|
-
os_attrs << %( algn="#{xml_escape(os[:algn])}") if os[:algn]
|
|
3176
|
-
os_attrs << %( rotWithShape="#{os[:rot_with_shape] ? 1 : 0}") unless os[:rot_with_shape].nil?
|
|
3177
|
-
os_color = os[:color] ? srgb_clr_xml(os[:color], alpha: os[:alpha]) : ""
|
|
3178
|
-
effect_children << "<a:outerShdw#{os_attrs}>#{os_color}</a:outerShdw>"
|
|
3179
|
-
end
|
|
3180
|
-
if shape[:glow]
|
|
3181
|
-
gl = shape[:glow]
|
|
3182
|
-
gl_attrs = +""
|
|
3183
|
-
gl_attrs << %( rad="#{gl[:rad]}") if gl[:rad]
|
|
3184
|
-
gl_color = gl[:color] ? srgb_clr_xml(gl[:color], alpha: gl[:alpha]) : ""
|
|
3185
|
-
effect_children << "<a:glow#{gl_attrs}>#{gl_color}</a:glow>"
|
|
3186
|
-
end
|
|
3187
|
-
if shape[:inner_shadow]
|
|
3188
|
-
is = shape[:inner_shadow]
|
|
3189
|
-
is_attrs = +""
|
|
3190
|
-
is_attrs << %( blurRad="#{is[:blur_rad]}") if is[:blur_rad]
|
|
3191
|
-
is_attrs << %( dist="#{is[:dist]}") if is[:dist]
|
|
3192
|
-
is_attrs << %( dir="#{is[:dir]}") if is[:dir]
|
|
3193
|
-
is_color = is[:color] ? srgb_clr_xml(is[:color], alpha: is[:alpha]) : ""
|
|
3194
|
-
effect_children << "<a:innerShdw#{is_attrs}>#{is_color}</a:innerShdw>"
|
|
3195
|
-
end
|
|
3196
|
-
if shape[:reflection]
|
|
3197
|
-
rf = shape[:reflection]
|
|
3198
|
-
rf_attrs = +""
|
|
3199
|
-
rf_attrs << %( blurRad="#{rf[:blur_rad]}") if rf[:blur_rad]
|
|
3200
|
-
rf_attrs << %( stA="#{rf[:st_a]}") if rf[:st_a]
|
|
3201
|
-
rf_attrs << %( endA="#{rf[:end_a]}") if rf[:end_a]
|
|
3202
|
-
rf_attrs << %( dist="#{rf[:dist]}") unless rf[:dist].nil?
|
|
3203
|
-
rf_attrs << %( dir="#{rf[:dir]}") if rf[:dir]
|
|
3204
|
-
rf_attrs << %( fadeDir="#{rf[:fade_dir]}") if rf[:fade_dir]
|
|
3205
|
-
rf_attrs << %( sx="#{rf[:sx]}") if rf[:sx]
|
|
3206
|
-
rf_attrs << %( sy="#{rf[:sy]}") if rf[:sy]
|
|
3207
|
-
rf_attrs << %( kx="#{rf[:kx]}") if rf[:kx]
|
|
3208
|
-
rf_attrs << %( ky="#{rf[:ky]}") if rf[:ky]
|
|
3209
|
-
rf_attrs << %( algn="#{xml_escape(rf[:algn])}") if rf[:algn]
|
|
3210
|
-
rf_attrs << %( rotWithShape="#{rf[:rot_with_shape] ? 1 : 0}") unless rf[:rot_with_shape].nil?
|
|
3211
|
-
effect_children << "<a:reflection#{rf_attrs}/>"
|
|
3212
|
-
end
|
|
3213
|
-
if shape[:soft_edge]
|
|
3214
|
-
se = shape[:soft_edge]
|
|
3215
|
-
effect_children << %(<a:softEdge rad="#{se[:rad]}"/>)
|
|
3216
|
-
end
|
|
3217
|
-
if shape[:blur]
|
|
3218
|
-
bl = shape[:blur]
|
|
3219
|
-
bl_attrs = +""
|
|
3220
|
-
bl_attrs << %( rad="#{bl[:rad]}") if bl[:rad]
|
|
3221
|
-
bl_attrs << %( grow="#{bl[:grow] ? 1 : 0}") unless bl[:grow].nil?
|
|
3222
|
-
effect_children << "<a:blur#{bl_attrs}/>"
|
|
3223
|
-
end
|
|
3224
|
-
"<a:effectLst>#{effect_children}</a:effectLst>"
|
|
3225
|
-
else
|
|
3226
|
-
""
|
|
3227
|
-
end
|
|
3228
|
-
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>)
|
|
3229
|
-
if shape[:text] || shape[:text_paragraphs]
|
|
3230
|
-
body_pr_attrs = +""
|
|
3231
|
-
body_pr_attrs << %( rot="#{shape[:text_rot]}") if shape[:text_rot]
|
|
3232
|
-
body_pr_attrs << %( spcFirstLastPara="1") if shape[:text_spc_first_last_para]
|
|
3233
|
-
body_pr_attrs << %( wrap="#{xml_escape(shape[:text_wrap])}") if shape[:text_wrap]
|
|
3234
|
-
body_pr_attrs << %( anchor="#{xml_escape(shape[:text_anchor])}") if shape[:text_anchor]
|
|
3235
|
-
body_pr_attrs << %( anchorCtr="1") if shape[:text_anchor_ctr]
|
|
3236
|
-
body_pr_attrs << %( vertOverflow="#{xml_escape(shape[:text_vert_overflow])}") if shape[:text_vert_overflow]
|
|
3237
|
-
body_pr_attrs << %( horzOverflow="#{xml_escape(shape[:text_horz_overflow])}") if shape[:text_horz_overflow]
|
|
3238
|
-
body_pr_attrs << %( numCol="#{shape[:text_num_col]}") if shape[:text_num_col]
|
|
3239
|
-
body_pr_attrs << %( spcCol="#{shape[:text_spc_col]}") if shape[:text_spc_col]
|
|
3240
|
-
body_pr_attrs << %( rtlCol="1") if shape[:text_rtl_col]
|
|
3241
|
-
body_pr_attrs << %( fromWordArt="1") if shape[:text_from_word_art]
|
|
3242
|
-
body_pr_attrs << %( upright="1") if shape[:text_upright]
|
|
3243
|
-
body_pr_attrs << %( compatLnSpc="1") if shape[:text_compat_ln_spc]
|
|
3244
|
-
body_pr_attrs << %( forceAA="1") if shape[:text_force_aa]
|
|
3245
|
-
body_pr_attrs << %( vert="#{xml_escape(shape[:text_vertical])}") if shape[:text_vertical]
|
|
3246
|
-
if shape[:text_insets]
|
|
3247
|
-
ins = shape[:text_insets]
|
|
3248
|
-
body_pr_attrs << %( lIns="#{ins[:left]}") if ins[:left]
|
|
3249
|
-
body_pr_attrs << %( tIns="#{ins[:top]}") if ins[:top]
|
|
3250
|
-
body_pr_attrs << %( rIns="#{ins[:right]}") if ins[:right]
|
|
3251
|
-
body_pr_attrs << %( bIns="#{ins[:bottom]}") if ins[:bottom]
|
|
3252
|
-
end
|
|
3253
|
-
body_pr_children = +""
|
|
3254
|
-
if shape[:text_warp]
|
|
3255
|
-
tw = shape[:text_warp]
|
|
3256
|
-
body_pr_children << %(<a:prstTxWarp prst="#{xml_escape(tw[:preset])}"><a:avLst/></a:prstTxWarp>)
|
|
3257
|
-
end
|
|
3258
|
-
autofit_xml = case shape[:autofit]
|
|
3259
|
-
when "none" then "<a:noAutofit/>"
|
|
3260
|
-
when "shape" then "<a:spAutoFit/>"
|
|
3261
|
-
when "normal" then "<a:normAutofit/>"
|
|
3262
|
-
when Hash
|
|
3263
|
-
af = shape[:autofit]
|
|
3264
|
-
case af[:type]
|
|
3265
|
-
when "normal"
|
|
3266
|
-
na_attrs = +""
|
|
3267
|
-
na_attrs << %( fontScale="#{af[:font_scale]}") if af[:font_scale]
|
|
3268
|
-
na_attrs << %( lnSpcReduction="#{af[:ln_spc_reduction]}") if af[:ln_spc_reduction]
|
|
3269
|
-
"<a:normAutofit#{na_attrs}/>"
|
|
3270
|
-
when "none" then "<a:noAutofit/>"
|
|
3271
|
-
when "shape" then "<a:spAutoFit/>"
|
|
3272
|
-
else ""
|
|
3273
|
-
end
|
|
3274
|
-
else ""
|
|
3275
|
-
end
|
|
3276
|
-
body_pr_children << autofit_xml
|
|
3277
|
-
body_pr_xml = if body_pr_children.empty?
|
|
3278
|
-
"<a:bodyPr#{body_pr_attrs}/>"
|
|
3279
|
-
else
|
|
3280
|
-
"<a:bodyPr#{body_pr_attrs}>#{body_pr_children}</a:bodyPr>"
|
|
3281
|
-
end
|
|
3282
|
-
parts << "<xdr:txBody>#{body_pr_xml}<a:lstStyle/>"
|
|
3283
|
-
if shape[:text_paragraphs]
|
|
3284
|
-
shape[:text_paragraphs].each { |para| parts << paragraph_xml(para) }
|
|
3285
|
-
elsif shape[:text]
|
|
3286
|
-
parts << paragraph_xml(
|
|
3287
|
-
text: shape[:text], font: shape[:text_font],
|
|
3288
|
-
align: shape[:text_align], font_align: shape[:text_font_align],
|
|
3289
|
-
def_tab_sz: shape[:text_def_tab_sz], rtl: shape[:text_rtl],
|
|
3290
|
-
ea_ln_brk: shape[:text_ea_ln_brk], latin_ln_brk: shape[:text_latin_ln_brk],
|
|
3291
|
-
hanging_punct: shape[:text_hanging_punct], level: shape[:text_level],
|
|
3292
|
-
indent: shape[:text_indent], spacing: shape[:text_spacing],
|
|
3293
|
-
tab_stops: shape[:text_tab_stops], bullet: shape[:text_bullet],
|
|
3294
|
-
def_rpr: shape[:text_def_rpr], end_para_rpr: shape[:text_end_para_rpr]
|
|
3295
|
-
)
|
|
3296
|
-
end
|
|
3297
|
-
parts << "</xdr:txBody>"
|
|
3298
|
-
end
|
|
3299
|
-
parts << "</xdr:sp>"
|
|
3300
|
-
parts << client_data_xml(shape)
|
|
3301
|
-
parts << "</xdr:twoCellAnchor>"
|
|
3302
|
-
end
|
|
3303
|
-
end
|
|
3304
|
-
|
|
3305
|
-
parts << "</xdr:wsDr>"
|
|
3306
|
-
parts.join
|
|
3307
|
-
end
|
|
3308
|
-
|
|
3309
|
-
# : (untyped para) -> ::String
|
|
3310
|
-
def paragraph_xml(para)
|
|
3311
|
-
ppr_attrs = +""
|
|
3312
|
-
ppr_attrs << %( algn="#{xml_escape(para[:align])}") if para[:align]
|
|
3313
|
-
ppr_attrs << %( fontAlgn="#{xml_escape(para[:font_align])}") if para[:font_align]
|
|
3314
|
-
ppr_attrs << %( defTabSz="#{para[:def_tab_sz]}") if para[:def_tab_sz]
|
|
3315
|
-
ppr_attrs << %( rtl="1") if para[:rtl]
|
|
3316
|
-
ppr_attrs << %( eaLnBrk="1") if para[:ea_ln_brk]
|
|
3317
|
-
ppr_attrs << %( latinLnBrk="1") if para[:latin_ln_brk]
|
|
3318
|
-
ppr_attrs << %( hangingPunct="1") if para[:hanging_punct]
|
|
3319
|
-
ppr_attrs << %( lvl="#{para[:level]}") if para[:level]
|
|
3320
|
-
if para[:indent]
|
|
3321
|
-
ti = para[:indent]
|
|
3322
|
-
ppr_attrs << %( marL="#{ti[:left]}") if ti[:left]
|
|
3323
|
-
ppr_attrs << %( marR="#{ti[:right]}") if ti[:right]
|
|
3324
|
-
ppr_attrs << %( indent="#{ti[:indent]}") if ti[:indent]
|
|
3325
|
-
end
|
|
3326
|
-
ppr_children = +""
|
|
3327
|
-
if para[:spacing]
|
|
3328
|
-
ts = para[:spacing]
|
|
3329
|
-
if ts[:line]
|
|
3330
|
-
ppr_children << %(<a:lnSpc><a:spcPts val="#{ts[:line]}"/></a:lnSpc>)
|
|
3331
|
-
elsif ts[:line_pct]
|
|
3332
|
-
ppr_children << %(<a:lnSpc><a:spcPct val="#{ts[:line_pct]}"/></a:lnSpc>)
|
|
3333
|
-
end
|
|
3334
|
-
if ts[:before]
|
|
3335
|
-
ppr_children << %(<a:spcBef><a:spcPts val="#{ts[:before]}"/></a:spcBef>)
|
|
3336
|
-
elsif ts[:before_pct]
|
|
3337
|
-
ppr_children << %(<a:spcBef><a:spcPct val="#{ts[:before_pct]}"/></a:spcBef>)
|
|
3338
|
-
end
|
|
3339
|
-
if ts[:after]
|
|
3340
|
-
ppr_children << %(<a:spcAft><a:spcPts val="#{ts[:after]}"/></a:spcAft>)
|
|
3341
|
-
elsif ts[:after_pct]
|
|
3342
|
-
ppr_children << %(<a:spcAft><a:spcPct val="#{ts[:after_pct]}"/></a:spcAft>)
|
|
3343
|
-
end
|
|
3344
|
-
end
|
|
3345
|
-
if para[:tab_stops]&.any?
|
|
3346
|
-
tabs = para[:tab_stops].map do |tab|
|
|
3347
|
-
tab_attrs = %( pos="#{tab[:pos]}")
|
|
3348
|
-
tab_attrs << %( algn="#{xml_escape(tab[:align])}") if tab[:align]
|
|
3349
|
-
"<a:tab#{tab_attrs}/>"
|
|
3350
|
-
end
|
|
3351
|
-
ppr_children << "<a:tabLst>#{tabs.join}</a:tabLst>"
|
|
3352
|
-
end
|
|
3353
|
-
if para[:bullet]
|
|
3354
|
-
bu = para[:bullet]
|
|
3355
|
-
ppr_children << %(<a:buClr>#{color_xml(bu[:color])}</a:buClr>) if bu[:color]
|
|
3356
|
-
ppr_children << %(<a:buSzPts val="#{bu[:size_pts]}"/>) if bu[:size_pts]
|
|
3357
|
-
ppr_children << %(<a:buSzPct val="#{bu[:size_pct]}"/>) if bu[:size_pct]
|
|
3358
|
-
ppr_children << %(<a:buFont typeface="#{xml_escape(bu[:font])}"/>) if bu[:font]
|
|
3359
|
-
case bu[:type]
|
|
3360
|
-
when "none"
|
|
3361
|
-
ppr_children << "<a:buNone/>"
|
|
3362
|
-
when "char"
|
|
3363
|
-
ppr_children << %(<a:buChar char="#{xml_escape(bu[:char])}"/>)
|
|
3364
|
-
when "auto"
|
|
3365
|
-
auto_attrs = %( type="#{xml_escape(bu[:auto_type])}")
|
|
3366
|
-
auto_attrs << %( startAt="#{bu[:start_at]}") if bu[:start_at]
|
|
3367
|
-
ppr_children << "<a:buAutoNum#{auto_attrs}/>"
|
|
3368
|
-
end
|
|
3369
|
-
end
|
|
3370
|
-
ppr_children << text_char_props_xml("a:defRPr", para[:def_rpr]) if para[:def_rpr]
|
|
3371
|
-
ppr_xml = if ppr_attrs.empty? && ppr_children.empty?
|
|
3372
|
-
""
|
|
3373
|
-
elsif ppr_children.empty?
|
|
3374
|
-
"<a:pPr#{ppr_attrs}/>"
|
|
3375
|
-
else
|
|
3376
|
-
"<a:pPr#{ppr_attrs}>#{ppr_children}</a:pPr>"
|
|
3377
|
-
end
|
|
3378
|
-
end_para_rpr_xml = para[:end_para_rpr] ? text_char_props_xml("a:endParaRPr", para[:end_para_rpr]) : ""
|
|
3379
|
-
runs_xml = if para[:runs]
|
|
3380
|
-
para[:runs].map do |run|
|
|
3381
|
-
run_rpr = run[:font] ? text_char_props_xml("a:rPr", run[:font]) : ""
|
|
3382
|
-
"<a:r>#{run_rpr}<a:t>#{xml_escape(run[:text] || "")}</a:t></a:r>"
|
|
3383
|
-
end.join
|
|
3384
|
-
else
|
|
3385
|
-
rpr_xml = para[:font] ? text_char_props_xml("a:rPr", para[:font]) : ""
|
|
3386
|
-
"<a:r>#{rpr_xml}<a:t>#{xml_escape(para[:text] || "")}</a:t></a:r>"
|
|
3387
|
-
end
|
|
3388
|
-
"<a:p>#{ppr_xml}#{runs_xml}#{end_para_rpr_xml}</a:p>"
|
|
3389
|
-
end
|
|
3390
|
-
|
|
3391
|
-
# : (untyped tag, untyped font) -> untyped
|
|
3392
|
-
def text_char_props_xml(tag, font)
|
|
3393
|
-
attrs = +""
|
|
3394
|
-
attrs << %( b="1") if font[:bold]
|
|
3395
|
-
attrs << %( i="1") if font[:italic]
|
|
3396
|
-
attrs << %( noProof="1") if font[:no_proof]
|
|
3397
|
-
attrs << %( normalizeH="1") if font[:normalize_h]
|
|
3398
|
-
attrs << %( kumimoji="1") if font[:kumimoji]
|
|
3399
|
-
attrs << %( sz="#{font[:size]}") if font[:size]
|
|
3400
|
-
attrs << %( strike="#{xml_escape(font[:strike])}") if font[:strike]
|
|
3401
|
-
attrs << %( u="#{xml_escape(font[:underline])}") if font[:underline]
|
|
3402
|
-
attrs << %( baseline="#{font[:baseline]}") if font[:baseline]
|
|
3403
|
-
attrs << %( spc="#{font[:spacing]}") if font[:spacing]
|
|
3404
|
-
attrs << %( kern="#{font[:kern]}") if font[:kern]
|
|
3405
|
-
attrs << %( cap="#{xml_escape(font[:cap])}") if font[:cap]
|
|
3406
|
-
attrs << %( lang="#{xml_escape(font[:lang])}") if font[:lang]
|
|
3407
|
-
attrs << %( altLang="#{xml_escape(font[:alt_lang])}") if font[:alt_lang]
|
|
3408
|
-
attrs << %( dirty="1") if font[:dirty]
|
|
3409
|
-
attrs << %( smtClean="1") if font[:smt_clean]
|
|
3410
|
-
attrs << %( err="1") if font[:err]
|
|
3411
|
-
attrs << %( bmk="#{xml_escape(font[:bmk])}") if font[:bmk]
|
|
3412
|
-
children = +""
|
|
3413
|
-
children << %(<a:solidFill>#{color_xml(font[:color])}</a:solidFill>) if font[:color]
|
|
3414
|
-
children << %(<a:highlight>#{color_xml(font[:highlight])}</a:highlight>) if font[:highlight]
|
|
3415
|
-
children << %(<a:latin typeface="#{xml_escape(font[:name])}"/>) if font[:name]
|
|
3416
|
-
children << %(<a:ea typeface="#{xml_escape(font[:ea_font])}"/>) if font[:ea_font]
|
|
3417
|
-
children << %(<a:cs typeface="#{xml_escape(font[:cs_font])}"/>) if font[:cs_font]
|
|
3418
|
-
children << %(<a:sym typeface="#{xml_escape(font[:sym_font])}"/>) if font[:sym_font]
|
|
3419
|
-
children << "<a:uFillTx/>" if font[:u_fill_tx]
|
|
3420
|
-
children << "<a:uLnTx/>" if font[:u_ln_tx]
|
|
3421
|
-
if font[:line_color] || font[:line_width] || font[:line_dash] || font[:line_cap] || font[:line_join]
|
|
3422
|
-
ln_attrs = +(font[:line_width] ? %( w="#{font[:line_width].to_i}") : "")
|
|
3423
|
-
ln_attrs << %( cap="#{xml_escape(font[:line_cap])}") if font[:line_cap]
|
|
3424
|
-
ln_fill = font[:line_color] ? %(<a:solidFill>#{color_xml(font[:line_color])}</a:solidFill>) : ""
|
|
3425
|
-
ln_dash = font[:line_dash] ? %(<a:prstDash val="#{xml_escape(font[:line_dash])}"/>) : ""
|
|
3426
|
-
ln_join = case font[:line_join]
|
|
3427
|
-
when "round" then "<a:round/>"
|
|
3428
|
-
when "bevel" then "<a:bevel/>"
|
|
3429
|
-
when "miter"
|
|
3430
|
-
font[:line_miter_limit] ? "<a:miter lim=\"#{font[:line_miter_limit].to_i}\"/>" : "<a:miter/>"
|
|
3431
|
-
else ""
|
|
3432
|
-
end
|
|
3433
|
-
children << "<a:ln#{ln_attrs}>#{ln_fill}#{ln_dash}#{ln_join}</a:ln>"
|
|
3434
|
-
end
|
|
3435
|
-
if font[:outer_shadow] || font[:inner_shadow] || font[:glow] || font[:soft_edge] || font[:reflection] || font[:blur]
|
|
3436
|
-
eff = +""
|
|
3437
|
-
if font[:outer_shadow]
|
|
3438
|
-
os = font[:outer_shadow]
|
|
3439
|
-
oa = +""
|
|
3440
|
-
oa << %( blurRad="#{os[:blur_rad]}") if os[:blur_rad]
|
|
3441
|
-
oa << %( dist="#{os[:dist]}") if os[:dist]
|
|
3442
|
-
oa << %( dir="#{os[:dir]}") if os[:dir]
|
|
3443
|
-
oa << %( algn="#{xml_escape(os[:algn])}") if os[:algn]
|
|
3444
|
-
oa << %( rotWithShape="#{os[:rot_with_shape] ? 1 : 0}") unless os[:rot_with_shape].nil?
|
|
3445
|
-
oc = os[:color] ? srgb_clr_xml(os[:color], alpha: os[:alpha]) : ""
|
|
3446
|
-
eff << "<a:outerShdw#{oa}>#{oc}</a:outerShdw>"
|
|
3447
|
-
end
|
|
3448
|
-
if font[:glow]
|
|
3449
|
-
gl = font[:glow]
|
|
3450
|
-
ga = +(gl[:rad] ? %( rad="#{gl[:rad]}") : "")
|
|
3451
|
-
gc = gl[:color] ? srgb_clr_xml(gl[:color], alpha: gl[:alpha]) : ""
|
|
3452
|
-
eff << "<a:glow#{ga}>#{gc}</a:glow>"
|
|
3453
|
-
end
|
|
3454
|
-
if font[:inner_shadow]
|
|
3455
|
-
is = font[:inner_shadow]
|
|
3456
|
-
ia = +""
|
|
3457
|
-
ia << %( blurRad="#{is[:blur_rad]}") if is[:blur_rad]
|
|
3458
|
-
ia << %( dist="#{is[:dist]}") if is[:dist]
|
|
3459
|
-
ia << %( dir="#{is[:dir]}") if is[:dir]
|
|
3460
|
-
ic = is[:color] ? srgb_clr_xml(is[:color], alpha: is[:alpha]) : ""
|
|
3461
|
-
eff << "<a:innerShdw#{ia}>#{ic}</a:innerShdw>"
|
|
3462
|
-
end
|
|
3463
|
-
if font[:reflection]
|
|
3464
|
-
rf = font[:reflection]
|
|
3465
|
-
ra = +""
|
|
3466
|
-
ra << %( blurRad="#{rf[:blur_rad]}") if rf[:blur_rad]
|
|
3467
|
-
ra << %( stA="#{rf[:st_a]}") if rf[:st_a]
|
|
3468
|
-
ra << %( endA="#{rf[:end_a]}") if rf[:end_a]
|
|
3469
|
-
ra << %( dist="#{rf[:dist]}") unless rf[:dist].nil?
|
|
3470
|
-
ra << %( dir="#{rf[:dir]}") if rf[:dir]
|
|
3471
|
-
ra << %( fadeDir="#{rf[:fade_dir]}") if rf[:fade_dir]
|
|
3472
|
-
ra << %( sx="#{rf[:sx]}") if rf[:sx]
|
|
3473
|
-
ra << %( sy="#{rf[:sy]}") if rf[:sy]
|
|
3474
|
-
ra << %( kx="#{rf[:kx]}") if rf[:kx]
|
|
3475
|
-
ra << %( ky="#{rf[:ky]}") if rf[:ky]
|
|
3476
|
-
ra << %( algn="#{xml_escape(rf[:algn])}") if rf[:algn]
|
|
3477
|
-
ra << %( rotWithShape="#{rf[:rot_with_shape] ? 1 : 0}") unless rf[:rot_with_shape].nil?
|
|
3478
|
-
eff << "<a:reflection#{ra}/>"
|
|
3479
|
-
end
|
|
3480
|
-
if font[:soft_edge]
|
|
3481
|
-
se = font[:soft_edge]
|
|
3482
|
-
eff << %(<a:softEdge rad="#{se[:rad]}"/>)
|
|
3483
|
-
end
|
|
3484
|
-
if font[:blur]
|
|
3485
|
-
bl = font[:blur]
|
|
3486
|
-
ba = +(bl[:rad] ? %( rad="#{bl[:rad]}") : "")
|
|
3487
|
-
ba << %( grow="#{bl[:grow] ? 1 : 0}") unless bl[:grow].nil?
|
|
3488
|
-
eff << "<a:blur#{ba}/>"
|
|
3489
|
-
end
|
|
3490
|
-
children << "<a:effectLst>#{eff}</a:effectLst>"
|
|
3491
|
-
end
|
|
3492
|
-
if children.empty?
|
|
3493
|
-
"<#{tag}#{attrs}/>"
|
|
3494
|
-
else
|
|
3495
|
-
"<#{tag}#{attrs}>#{children}</#{tag}>"
|
|
3496
|
-
end
|
|
3497
|
-
end
|
|
3498
|
-
|
|
3499
|
-
# : (untyped tag, untyped col, untyped row, ?col_off: ::Integer, ?row_off: ::Integer) -> ::String
|
|
3500
|
-
def anchor_xml(tag, col, row, col_off: 0, row_off: 0)
|
|
3501
|
-
"<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}>"
|
|
3502
|
-
end
|
|
3503
|
-
|
|
3504
|
-
# : (untyped obj) -> ::String
|
|
3505
|
-
def client_data_xml(obj)
|
|
3506
|
-
cd_attrs = +""
|
|
3507
|
-
cd_attrs << ' fLocksWithSheet="0"' if obj[:locks_with_sheet] == false
|
|
3508
|
-
cd_attrs << ' fLocksWithSheet="1"' if obj[:locks_with_sheet] == true
|
|
3509
|
-
cd_attrs << ' fPrintsWithSheet="0"' if obj[:prints_with_sheet] == false
|
|
3510
|
-
cd_attrs << ' fPrintsWithSheet="1"' if obj[:prints_with_sheet] == true
|
|
3511
|
-
"<xdr:clientData#{cd_attrs}/>"
|
|
3512
|
-
end
|
|
3513
|
-
|
|
3514
|
-
# : (untyped rels_data) -> untyped
|
|
3515
|
-
def generate_drawing_rels(rels_data)
|
|
3516
|
-
parts = [XML_HEADER, %(<Relationships xmlns="#{REL_NS}">)]
|
|
3517
|
-
rels_data.each_with_index do |rel, i|
|
|
3518
|
-
rel_type = case rel[:type]
|
|
3519
|
-
when :image then "#{DOC_REL_NS}/image"
|
|
3520
|
-
when :chart then "#{DOC_REL_NS}/chart"
|
|
3521
|
-
end
|
|
3522
|
-
parts << %(<Relationship Id="rId#{i + 1}" Type="#{rel_type}" Target="#{rel[:target]}"/>)
|
|
3523
|
-
end
|
|
3524
|
-
parts << "</Relationships>"
|
|
3525
|
-
parts.join
|
|
3526
|
-
end
|
|
3527
|
-
|
|
3528
|
-
# : (untyped title_spec, ?overlay: bool) -> untyped
|
|
3529
|
-
def build_chart_title_xml(title_spec, overlay: false)
|
|
3530
|
-
overlay_val = overlay ? 1 : 0
|
|
3531
|
-
if title_spec.is_a?(Hash)
|
|
3532
|
-
text = xml_escape(title_spec[:text].to_s)
|
|
3533
|
-
rotation = title_spec[:rotation]
|
|
3534
|
-
body_pr = rotation ? %(<a:bodyPr rot="#{rotation}"/>) : "<a:bodyPr/>"
|
|
3535
|
-
font = title_spec[:font] || {}
|
|
3536
|
-
rpr_attrs = +""
|
|
3537
|
-
rpr_attrs << %( b="1") if font[:bold]
|
|
3538
|
-
rpr_attrs << %( i="1") if font[:italic]
|
|
3539
|
-
rpr_attrs << %( sz="#{font[:size]}") if font[:size]
|
|
3540
|
-
rpr_children = +""
|
|
3541
|
-
rpr_children << %(<a:solidFill>#{color_xml(font[:color])}</a:solidFill>) if font[:color]
|
|
3542
|
-
rpr_children << %(<a:latin typeface="#{xml_escape(font[:name])}"/>) if font[:name]
|
|
3543
|
-
rpr_xml = if rpr_children.empty?
|
|
3544
|
-
"<a:rPr#{rpr_attrs}/>"
|
|
3545
|
-
else
|
|
3546
|
-
"<a:rPr#{rpr_attrs}>#{rpr_children}</a:rPr>"
|
|
3547
|
-
end
|
|
3548
|
-
layout_xml = build_title_layout_xml(title_spec[:layout])
|
|
3549
|
-
sp_xml = build_title_sp_pr(title_spec)
|
|
3550
|
-
"<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>"
|
|
3551
|
-
else
|
|
3552
|
-
"<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>"
|
|
3553
|
-
end
|
|
3554
|
-
end
|
|
3555
|
-
|
|
3556
|
-
# : (untyped title_spec, untyped chart, untyped prefix) -> untyped
|
|
3557
|
-
def merge_flat_title_styling(title_spec, chart, prefix)
|
|
3558
|
-
font_key = :"#{prefix}_font"
|
|
3559
|
-
fill_key = :"#{prefix}_fill"
|
|
3560
|
-
no_fill_key = :"#{prefix}_no_fill"
|
|
3561
|
-
lc_key = :"#{prefix}_line_color"
|
|
3562
|
-
lw_key = :"#{prefix}_line_width"
|
|
3563
|
-
ld_key = :"#{prefix}_line_dash"
|
|
3564
|
-
rot_key = :"#{prefix}_rotation"
|
|
3565
|
-
return title_spec unless chart[font_key] || chart[fill_key] || chart[no_fill_key] ||
|
|
3566
|
-
chart[lc_key] || chart[lw_key] || chart[ld_key] || chart[rot_key]
|
|
3567
|
-
|
|
3568
|
-
spec = title_spec.is_a?(Hash) ? title_spec : { text: title_spec }
|
|
3569
|
-
spec[:font] ||= chart[font_key] if chart[font_key]
|
|
3570
|
-
spec[:fill_color] ||= chart[fill_key] if chart[fill_key]
|
|
3571
|
-
spec[:no_fill] = chart[no_fill_key] if spec[:no_fill].nil? && !chart[no_fill_key].nil?
|
|
3572
|
-
spec[:line_color] ||= chart[lc_key] if chart[lc_key]
|
|
3573
|
-
spec[:line_width] ||= chart[lw_key] if chart[lw_key]
|
|
3574
|
-
spec[:line_dash] ||= chart[ld_key] if chart[ld_key]
|
|
3575
|
-
spec[:rotation] ||= chart[rot_key] if chart[rot_key]
|
|
3576
|
-
spec
|
|
3577
|
-
end
|
|
3578
|
-
|
|
3579
|
-
# : (untyped spec) -> untyped
|
|
3580
|
-
def build_title_sp_pr(spec)
|
|
3581
|
-
children = +""
|
|
3582
|
-
children << %(<a:solidFill>#{color_xml(spec[:fill_color])}</a:solidFill>) if spec[:fill_color]
|
|
3583
|
-
children << "<a:noFill/>" if spec[:no_fill]
|
|
3584
|
-
if spec[:line_color] || spec[:line_width] || spec[:line_dash]
|
|
3585
|
-
lw = spec[:line_width] ? %( w="#{(spec[:line_width] * 12_700).to_i}") : ""
|
|
3586
|
-
lf = spec[:line_color] ? %(<a:solidFill>#{color_xml(spec[:line_color])}</a:solidFill>) : ""
|
|
3587
|
-
ld = spec[:line_dash] ? %(<a:prstDash val="#{xml_escape(spec[:line_dash])}"/>) : ""
|
|
3588
|
-
children << "<a:ln#{lw}>#{lf}#{ld}</a:ln>"
|
|
3589
|
-
end
|
|
3590
|
-
children.empty? ? "" : "<c:spPr>#{children}</c:spPr>"
|
|
3591
|
-
end
|
|
3592
|
-
|
|
3593
|
-
# : (untyped layout) -> ("" | untyped)
|
|
3594
|
-
def build_title_layout_xml(layout)
|
|
3595
|
-
return "" unless layout.is_a?(Hash)
|
|
3596
|
-
|
|
3597
|
-
ml = +""
|
|
3598
|
-
ml << %(<c:x val="#{layout[:x]}"/>) if layout[:x]
|
|
3599
|
-
ml << %(<c:y val="#{layout[:y]}"/>) if layout[:y]
|
|
3600
|
-
ml << %(<c:w val="#{layout[:w]}"/>) if layout[:w]
|
|
3601
|
-
ml << %(<c:h val="#{layout[:h]}"/>) if layout[:h]
|
|
3602
|
-
ml.empty? ? "" : "<c:layout><c:manualLayout>#{ml}</c:manualLayout></c:layout>"
|
|
3603
|
-
end
|
|
3604
|
-
|
|
3605
|
-
# : (untyped tag, untyped spec) -> ("" | untyped)
|
|
3606
|
-
def gridlines_xml(tag, spec)
|
|
3607
|
-
return "" unless spec
|
|
3608
|
-
|
|
3609
|
-
if spec.is_a?(Hash)
|
|
3610
|
-
sp_children = +""
|
|
3611
|
-
if spec[:line_color] || spec[:line_width] || spec[:line_dash]
|
|
3612
|
-
lw = spec[:line_width] ? %( w="#{(spec[:line_width] * 12_700).to_i}") : ""
|
|
3613
|
-
lf = spec[:line_color] ? %(<a:solidFill>#{color_xml(spec[:line_color])}</a:solidFill>) : ""
|
|
3614
|
-
ld = spec[:line_dash] ? %(<a:prstDash val="#{xml_escape(spec[:line_dash])}"/>) : ""
|
|
3615
|
-
sp_children << "<a:ln#{lw}>#{lf}#{ld}</a:ln>"
|
|
3616
|
-
end
|
|
3617
|
-
sp_children.empty? ? "<c:#{tag}/>" : "<c:#{tag}><c:spPr>#{sp_children}</c:spPr></c:#{tag}>"
|
|
3618
|
-
else
|
|
3619
|
-
"<c:#{tag}/>"
|
|
3620
|
-
end
|
|
3621
|
-
end
|
|
3622
|
-
|
|
3623
|
-
# Resolves a cell reference like "Sheet1!$A$1:$A$5" to an array of values from @sheets.
|
|
3624
|
-
# : (untyped ref) -> (nil | untyped)
|
|
3625
|
-
def resolve_sheet_ref(ref)
|
|
3626
|
-
return nil unless ref
|
|
3627
|
-
|
|
3628
|
-
m = if ref.start_with?("'")
|
|
3629
|
-
ref.match(/\A'([^']+)'!(.+)\z/)
|
|
3630
|
-
|
|
3631
|
-
else
|
|
3632
|
-
ref.match(/\A([^!]+)!(.+)\z/)
|
|
3633
|
-
|
|
3634
|
-
end
|
|
3635
|
-
return nil unless m
|
|
3636
|
-
|
|
3637
|
-
sheet_name = m[1]
|
|
3638
|
-
range_part = m[2]
|
|
3639
|
-
return nil unless @sheets.key?(sheet_name)
|
|
3640
|
-
|
|
3641
|
-
range_part = range_part.delete("$")
|
|
3642
|
-
cells = if range_part.include?(":")
|
|
3643
|
-
start_cell, end_cell = range_part.split(":", 2)
|
|
3644
|
-
enumerate_cell_range(start_cell, end_cell)
|
|
3645
|
-
else
|
|
3646
|
-
[range_part]
|
|
3647
|
-
end
|
|
3648
|
-
cells.map { |addr| @sheets[sheet_name][addr] }
|
|
3649
|
-
end
|
|
3650
|
-
|
|
3651
|
-
# : (untyped start_cell, untyped end_cell) -> (::Array[untyped] | untyped)
|
|
3652
|
-
def enumerate_cell_range(start_cell, end_cell)
|
|
3653
|
-
sc = start_cell.match(/\A([A-Z]+)(\d+)\z/)
|
|
3654
|
-
ec = end_cell.match(/\A([A-Z]+)(\d+)\z/)
|
|
3655
|
-
return [start_cell] unless sc && ec
|
|
3656
|
-
|
|
3657
|
-
start_col = column_letter_to_index(sc[1])
|
|
3658
|
-
start_row = sc[2].to_i
|
|
3659
|
-
end_col = column_letter_to_index(ec[1])
|
|
3660
|
-
end_row = ec[2].to_i
|
|
3661
|
-
cells = []
|
|
3662
|
-
(start_row..end_row).each do |row|
|
|
3663
|
-
(start_col..end_col).each do |col|
|
|
3664
|
-
cells << "#{index_to_column_letter(col)}#{row}"
|
|
3665
|
-
end
|
|
3666
|
-
end
|
|
3667
|
-
cells
|
|
3668
|
-
end
|
|
3669
|
-
|
|
3670
|
-
# : (untyped ref) -> ("" | ::String)
|
|
3671
|
-
def num_cache_xml(ref)
|
|
3672
|
-
values = resolve_sheet_ref(ref)
|
|
3673
|
-
return "" unless values
|
|
3674
|
-
|
|
3675
|
-
pts = values.each_with_index.filter_map do |v, i|
|
|
3676
|
-
next unless v
|
|
3677
|
-
|
|
3678
|
-
%(<c:pt idx="#{i}"><c:v>#{xml_escape(v.to_s)}</c:v></c:pt>)
|
|
3679
|
-
end
|
|
3680
|
-
%(<c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="#{values.size}"/>#{pts.join}</c:numCache>)
|
|
3681
|
-
end
|
|
3682
|
-
|
|
3683
|
-
# : (untyped ref) -> ("" | ::String)
|
|
3684
|
-
def str_cache_xml(ref)
|
|
3685
|
-
values = resolve_sheet_ref(ref)
|
|
3686
|
-
return "" unless values
|
|
3687
|
-
|
|
3688
|
-
pts = values.each_with_index.filter_map do |v, i|
|
|
3689
|
-
next unless v
|
|
3690
|
-
|
|
3691
|
-
%(<c:pt idx="#{i}"><c:v>#{xml_escape(v.to_s)}</c:v></c:pt>)
|
|
3692
|
-
end
|
|
3693
|
-
%(<c:strCache><c:ptCount val="#{values.size}"/>#{pts.join}</c:strCache>)
|
|
3694
|
-
end
|
|
3695
|
-
|
|
3696
|
-
# : (untyped color, ?alpha: untyped?, ?transforms: untyped?) -> untyped
|
|
3697
|
-
def color_xml(color, alpha: nil, transforms: nil)
|
|
3698
|
-
if color.is_a?(Hash) && color[:scheme]
|
|
3699
|
-
tag = "schemeClr"
|
|
3700
|
-
val = xml_escape(color[:scheme])
|
|
3701
|
-
transforms = color[:transforms] || transforms
|
|
3702
|
-
else
|
|
3703
|
-
tag = "srgbClr"
|
|
3704
|
-
val = xml_escape(normalize_drawing_rgb(color))
|
|
3705
|
-
end
|
|
3706
|
-
children = +""
|
|
3707
|
-
children << %(<a:alpha val="#{alpha}"/>) if alpha
|
|
3708
|
-
(transforms || []).each do |t|
|
|
3709
|
-
children << %(<a:#{xml_escape(t[:type])} val="#{t[:val]}"/>)
|
|
3710
|
-
end
|
|
3711
|
-
if children.empty?
|
|
3712
|
-
%(<a:#{tag} val="#{val}"/>)
|
|
3713
|
-
else
|
|
3714
|
-
%(<a:#{tag} val="#{val}">#{children}</a:#{tag}>)
|
|
3715
|
-
end
|
|
3716
|
-
end
|
|
3717
|
-
|
|
3718
|
-
# : (untyped color) -> untyped
|
|
3719
|
-
def normalize_drawing_rgb(color)
|
|
3720
|
-
str = color.to_s
|
|
3721
|
-
hex = str.delete_prefix("#")
|
|
3722
|
-
|
|
3723
|
-
# Common shorthand (e.g. #f00 -> FF0000)
|
|
3724
|
-
return hex.chars.map { |c| c * 2 }.join.upcase if hex.match?(/\A[0-9A-Fa-f]{3}\z/)
|
|
3725
|
-
|
|
3726
|
-
# ARGB -> RGB for DrawingML srgbClr (expects RRGGBB)
|
|
3727
|
-
return hex[-6, 6].upcase if hex.match?(/\A[0-9A-Fa-f]{8}\z/)
|
|
3728
|
-
|
|
3729
|
-
return hex.upcase if hex.match?(/\A[0-9A-Fa-f]{6}\z/)
|
|
3730
|
-
|
|
3731
|
-
str
|
|
3732
|
-
end
|
|
3733
|
-
|
|
3734
|
-
alias srgb_clr_xml color_xml
|
|
3735
|
-
|
|
3736
|
-
# : (untyped rotation, untyped font) -> ("" | ::String)
|
|
3737
|
-
def build_axis_txpr(rotation, font)
|
|
3738
|
-
return "" unless rotation || font
|
|
3739
|
-
|
|
3740
|
-
body_attrs = rotation ? %( rot="#{rotation}") : ""
|
|
3741
|
-
rpr_attrs = +""
|
|
3742
|
-
rpr_children = +""
|
|
3743
|
-
if font
|
|
3744
|
-
rpr_attrs << %( sz="#{(font[:size] * 100).to_i}") if font[:size]
|
|
3745
|
-
rpr_attrs << %( b="1") if font[:bold]
|
|
3746
|
-
rpr_attrs << %( i="1") if font[:italic]
|
|
3747
|
-
rpr_children << %(<a:solidFill>#{color_xml(font[:color])}</a:solidFill>) if font[:color]
|
|
3748
|
-
rpr_children << %(<a:latin typeface="#{xml_escape(font[:name])}"/>) if font[:name]
|
|
3749
|
-
end
|
|
3750
|
-
def_rpr = if rpr_children.empty?
|
|
3751
|
-
"<a:defRPr#{rpr_attrs}/>"
|
|
3752
|
-
else
|
|
3753
|
-
"<a:defRPr#{rpr_attrs}>#{rpr_children}</a:defRPr>"
|
|
3754
|
-
end
|
|
3755
|
-
%(<c:txPr><a:bodyPr#{body_attrs}/><a:lstStyle/><a:p><a:pPr>#{def_rpr}</a:pPr><a:endParaRPr/></a:p></c:txPr>)
|
|
3756
|
-
end
|
|
3757
|
-
|
|
3758
|
-
# : (untyped chart) -> untyped
|
|
3759
|
-
def generate_chart_xml(chart)
|
|
3760
|
-
chart_type = CHART_TYPE_MAP[chart[:type]] || "barChart"
|
|
3761
|
-
no_axes = NO_AXIS_CHARTS.include?(chart_type)
|
|
3762
|
-
parts = [
|
|
3763
|
-
XML_HEADER,
|
|
3764
|
-
%(<c:chartSpace xmlns:c="#{C_NS}" xmlns:a="#{A_NS}" xmlns:r="#{DOC_REL_NS}">)
|
|
3765
|
-
]
|
|
3766
|
-
rc = chart[:rounded_corners]
|
|
3767
|
-
parts << %(<c:roundedCorners val="#{rc ? 1 : 0}"/>) unless rc.nil?
|
|
3768
|
-
parts << %(<c:style val="#{chart[:style]}"/>) if chart[:style]
|
|
3769
|
-
if (prot = chart[:protection])
|
|
3770
|
-
parts << "<c:protection>"
|
|
3771
|
-
parts << %(<c:chartObject val="#{prot[:chart_object] ? 1 : 0}"/>) unless prot[:chart_object].nil?
|
|
3772
|
-
parts << %(<c:data val="#{prot[:data] ? 1 : 0}"/>) unless prot[:data].nil?
|
|
3773
|
-
parts << %(<c:formatting val="#{prot[:formatting] ? 1 : 0}"/>) unless prot[:formatting].nil?
|
|
3774
|
-
parts << %(<c:selection val="#{prot[:selection] ? 1 : 0}"/>) unless prot[:selection].nil?
|
|
3775
|
-
parts << %(<c:userInterface val="#{prot[:user_interface] ? 1 : 0}"/>) unless prot[:user_interface].nil?
|
|
3776
|
-
parts << "</c:protection>"
|
|
3777
|
-
end
|
|
3778
|
-
parts << "<c:chart>"
|
|
3779
|
-
|
|
3780
|
-
if chart[:title]
|
|
3781
|
-
title_spec = chart[:title]
|
|
3782
|
-
if chart[:title_font] || chart[:title_fill_color] || chart[:title_no_fill] ||
|
|
3783
|
-
chart[:title_line_color] || chart[:title_line_width] || chart[:title_line_dash] ||
|
|
3784
|
-
chart[:title_rotation]
|
|
3785
|
-
title_spec = { text: title_spec } unless title_spec.is_a?(Hash)
|
|
3786
|
-
title_spec[:font] ||= chart[:title_font] if chart[:title_font]
|
|
3787
|
-
title_spec[:fill_color] ||= chart[:title_fill_color] if chart[:title_fill_color]
|
|
3788
|
-
title_spec[:no_fill] = chart[:title_no_fill] if title_spec[:no_fill].nil? && !chart[:title_no_fill].nil?
|
|
3789
|
-
title_spec[:line_color] ||= chart[:title_line_color] if chart[:title_line_color]
|
|
3790
|
-
title_spec[:line_width] ||= chart[:title_line_width] if chart[:title_line_width]
|
|
3791
|
-
title_spec[:line_dash] ||= chart[:title_line_dash] if chart[:title_line_dash]
|
|
3792
|
-
title_spec[:rotation] ||= chart[:title_rotation] if chart[:title_rotation]
|
|
3793
|
-
end
|
|
3794
|
-
parts << build_chart_title_xml(title_spec, overlay: chart[:title_overlay])
|
|
3795
|
-
end
|
|
3796
|
-
atd = chart[:auto_title_deleted]
|
|
3797
|
-
parts << %(<c:autoTitleDeleted val="#{atd ? 1 : 0}"/>) unless atd.nil?
|
|
3798
|
-
|
|
3799
|
-
if (v3d = chart[:view_3d])
|
|
3800
|
-
parts << "<c:view3D>"
|
|
3801
|
-
parts << %(<c:rotX val="#{v3d[:rot_x]}"/>) if v3d[:rot_x]
|
|
3802
|
-
parts << %(<c:hPercent val="#{v3d[:h_percent]}"/>) if v3d[:h_percent]
|
|
3803
|
-
parts << %(<c:rotY val="#{v3d[:rot_y]}"/>) if v3d[:rot_y]
|
|
3804
|
-
parts << %(<c:depthPercent val="#{v3d[:depth_percent]}"/>) if v3d[:depth_percent]
|
|
3805
|
-
r_ang = v3d[:r_ang_ax]
|
|
3806
|
-
parts << %(<c:rAngAx val="#{r_ang ? 1 : 0}"/>) unless r_ang.nil?
|
|
3807
|
-
parts << %(<c:perspective val="#{v3d[:perspective]}"/>) if v3d[:perspective]
|
|
3808
|
-
parts << "</c:view3D>"
|
|
3809
|
-
end
|
|
3810
|
-
|
|
3811
|
-
%i[floor side_wall back_wall].each do |wall_key|
|
|
3812
|
-
next unless (wall = chart[wall_key])
|
|
3813
|
-
|
|
3814
|
-
tag = { floor: "floor", side_wall: "sideWall", back_wall: "backWall" }[wall_key]
|
|
3815
|
-
parts << "<c:#{tag}><c:spPr>"
|
|
3816
|
-
parts << "<a:solidFill>#{srgb_clr_xml(wall[:fill_color])}</a:solidFill>" if wall[:fill_color]
|
|
3817
|
-
parts << "<a:noFill/>" if wall[:no_fill]
|
|
3818
|
-
if wall[:line_color] || wall[:line_dash]
|
|
3819
|
-
w_attr = wall[:line_width] ? %( w="#{wall[:line_width]}") : ""
|
|
3820
|
-
w_fill = wall[:line_color] ? %(<a:solidFill>#{srgb_clr_xml(wall[:line_color])}</a:solidFill>) : ""
|
|
3821
|
-
w_dash = wall[:line_dash] ? %(<a:prstDash val="#{xml_escape(wall[:line_dash])}"/>) : ""
|
|
3822
|
-
parts << "<a:ln#{w_attr}>#{w_fill}#{w_dash}</a:ln>"
|
|
3823
|
-
end
|
|
3824
|
-
parts << "</c:spPr></c:#{tag}>"
|
|
3825
|
-
end
|
|
3826
|
-
|
|
3827
|
-
parts << "<c:plotArea>"
|
|
3828
|
-
pa_layout = chart[:plot_area_layout]
|
|
3829
|
-
if pa_layout.is_a?(Hash)
|
|
3830
|
-
ml_parts = +""
|
|
3831
|
-
ml_parts << %(<c:layoutTarget val="#{xml_escape(pa_layout[:target])}"/>) if pa_layout[:target]
|
|
3832
|
-
ml_parts << %(<c:xMode val="#{xml_escape(pa_layout[:x_mode])}"/>) if pa_layout[:x_mode]
|
|
3833
|
-
ml_parts << %(<c:yMode val="#{xml_escape(pa_layout[:y_mode])}"/>) if pa_layout[:y_mode]
|
|
3834
|
-
ml_parts << %(<c:wMode val="#{xml_escape(pa_layout[:w_mode])}"/>) if pa_layout[:w_mode]
|
|
3835
|
-
ml_parts << %(<c:hMode val="#{xml_escape(pa_layout[:h_mode])}"/>) if pa_layout[:h_mode]
|
|
3836
|
-
ml_parts << %(<c:x val="#{pa_layout[:x]}"/>) if pa_layout[:x]
|
|
3837
|
-
ml_parts << %(<c:y val="#{pa_layout[:y]}"/>) if pa_layout[:y]
|
|
3838
|
-
ml_parts << %(<c:w val="#{pa_layout[:w]}"/>) if pa_layout[:w]
|
|
3839
|
-
ml_parts << %(<c:h val="#{pa_layout[:h]}"/>) if pa_layout[:h]
|
|
3840
|
-
parts << if ml_parts.empty?
|
|
3841
|
-
"<c:layout/>"
|
|
3842
|
-
else
|
|
3843
|
-
"<c:layout><c:manualLayout>#{ml_parts}</c:manualLayout></c:layout>"
|
|
3844
|
-
end
|
|
3845
|
-
else
|
|
3846
|
-
parts << "<c:layout/>"
|
|
3847
|
-
end
|
|
3848
|
-
parts << "<c:#{chart_type}>"
|
|
3849
|
-
parts << %(<c:ofPieType val="#{xml_escape(chart[:of_pie_type] || "pie")}"/>) if chart_type == "ofPieChart"
|
|
3850
|
-
if %w[barChart bar3DChart].include?(chart_type)
|
|
3851
|
-
bd = chart[:bar_dir] || "col"
|
|
3852
|
-
gr = chart[:grouping] || "clustered"
|
|
3853
|
-
parts << %(<c:barDir val="#{bd}"/><c:grouping val="#{gr}"/>)
|
|
3854
|
-
elsif GROUPING_CHARTS.include?(chart_type)
|
|
3855
|
-
parts << %(<c:grouping val="#{chart[:grouping] || "standard"}"/>)
|
|
3856
|
-
end
|
|
3857
|
-
parts << %(<c:scatterStyle val="#{chart[:scatter_style] || "lineMarker"}"/>) if chart_type == "scatterChart"
|
|
3858
|
-
parts << %(<c:radarStyle val="#{chart[:radar_style] || "standard"}"/>) if chart_type == "radarChart"
|
|
3859
|
-
vc = chart[:vary_colors]
|
|
3860
|
-
parts << %(<c:varyColors val="#{vc ? 1 : 0}"/>) unless vc.nil?
|
|
3861
|
-
wf = chart[:wireframe]
|
|
3862
|
-
parts << %(<c:wireframe val="#{wf ? 1 : 0}"/>) unless wf.nil?
|
|
3863
|
-
|
|
3864
|
-
all_series = chart[:series] || []
|
|
3865
|
-
all_series.each_with_index do |ser, idx|
|
|
3866
|
-
ser_order = ser[:order] || idx
|
|
3867
|
-
parts << "<c:ser><c:idx val=\"#{idx}\"/><c:order val=\"#{ser_order}\"/>"
|
|
3868
|
-
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]
|
|
3869
|
-
iin = ser[:invert_if_negative]
|
|
3870
|
-
parts << %(<c:invertIfNegative val="#{iin ? 1 : 0}"/>) unless iin.nil?
|
|
3871
|
-
parts << %(<c:explosion val="#{ser[:explosion]}"/>) if ser[:explosion]
|
|
3872
|
-
ser[:data_points]&.each do |dp|
|
|
3873
|
-
parts << "<c:dPt><c:idx val=\"#{dp[:idx]}\"/>"
|
|
3874
|
-
dp_iin = dp[:invert_if_negative]
|
|
3875
|
-
parts << %(<c:invertIfNegative val="#{dp_iin ? 1 : 0}"/>) unless dp_iin.nil?
|
|
3876
|
-
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]
|
|
3877
|
-
parts << "<c:marker>"
|
|
3878
|
-
parts << %(<c:symbol val="#{xml_escape(dp[:marker_symbol])}"/>) if dp[:marker_symbol]
|
|
3879
|
-
parts << %(<c:size val="#{dp[:marker_size]}"/>) if dp[:marker_size]
|
|
3880
|
-
if dp[:marker_fill] || dp[:marker_no_fill] || dp[:marker_line_color] || dp[:marker_line_dash] || dp[:marker_no_line]
|
|
3881
|
-
mk_sp = +""
|
|
3882
|
-
mk_sp << "<a:noFill/>" if dp[:marker_no_fill]
|
|
3883
|
-
mk_sp << %(<a:solidFill>#{color_xml(dp[:marker_fill])}</a:solidFill>) if dp[:marker_fill]
|
|
3884
|
-
if dp[:marker_no_line]
|
|
3885
|
-
mk_sp << "<a:ln><a:noFill/></a:ln>"
|
|
3886
|
-
elsif dp[:marker_line_color] || dp[:marker_line_dash]
|
|
3887
|
-
mk_ln_w = dp[:marker_line_width] ? %( w="#{(dp[:marker_line_width] * 12_700).to_i}") : ""
|
|
3888
|
-
mk_ln_f = dp[:marker_line_color] ? %(<a:solidFill>#{color_xml(dp[:marker_line_color])}</a:solidFill>) : ""
|
|
3889
|
-
mk_ln_d = dp[:marker_line_dash] ? %(<a:prstDash val="#{xml_escape(dp[:marker_line_dash])}"/>) : ""
|
|
3890
|
-
mk_sp << "<a:ln#{mk_ln_w}>#{mk_ln_f}#{mk_ln_d}</a:ln>"
|
|
3891
|
-
end
|
|
3892
|
-
parts << "<c:spPr>#{mk_sp}</c:spPr>"
|
|
3893
|
-
end
|
|
3894
|
-
parts << "</c:marker>"
|
|
3895
|
-
end
|
|
3896
|
-
dp_b3d = dp[:bubble_3d]
|
|
3897
|
-
parts << %(<c:bubble3D val="#{dp_b3d ? 1 : 0}"/>) unless dp_b3d.nil?
|
|
3898
|
-
parts << %(<c:explosion val="#{dp[:explosion]}"/>) if dp[:explosion]
|
|
3899
|
-
dp_sp_children = +""
|
|
3900
|
-
dp_sp_children << %(<a:solidFill>#{color_xml(dp[:fill_color])}</a:solidFill>) if dp[:fill_color]
|
|
3901
|
-
dp_sp_children << "<a:noFill/>" if dp[:no_fill]
|
|
3902
|
-
if dp[:no_line]
|
|
3903
|
-
dp_sp_children << "<a:ln><a:noFill/></a:ln>"
|
|
3904
|
-
elsif dp[:line_color] || dp[:line_width] || dp[:line_dash]
|
|
3905
|
-
dp_ln_attrs = dp[:line_width] ? %( w="#{(dp[:line_width] * 12_700).to_i}") : ""
|
|
3906
|
-
dp_ln_fill = dp[:line_color] ? %(<a:solidFill>#{color_xml(dp[:line_color])}</a:solidFill>) : ""
|
|
3907
|
-
dp_ln_dash = dp[:line_dash] ? %(<a:prstDash val="#{xml_escape(dp[:line_dash])}"/>) : ""
|
|
3908
|
-
dp_sp_children << "<a:ln#{dp_ln_attrs}>#{dp_ln_fill}#{dp_ln_dash}</a:ln>"
|
|
3909
|
-
end
|
|
3910
|
-
parts << "<c:spPr>#{dp_sp_children}</c:spPr>" unless dp_sp_children.empty?
|
|
3911
|
-
parts << "</c:dPt>"
|
|
3912
|
-
end
|
|
3913
|
-
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]
|
|
3914
|
-
parts << "<c:spPr>"
|
|
3915
|
-
parts << %(<a:solidFill>#{color_xml(ser[:fill_color])}</a:solidFill>) if ser[:fill_color]
|
|
3916
|
-
parts << "<a:noFill/>" if ser[:no_fill]
|
|
3917
|
-
if ser[:line_color] || ser[:no_line] || ser[:line_width] || ser[:line_cap] || ser[:line_join] || ser[:line_dash]
|
|
3918
|
-
lw = ser[:line_width] ? %( w="#{(ser[:line_width] * 12_700).to_i}") : ""
|
|
3919
|
-
lc = ser[:line_cap] ? %( cap="#{xml_escape(ser[:line_cap])}") : ""
|
|
3920
|
-
parts << "<a:ln#{lw}#{lc}>"
|
|
3921
|
-
parts << "<a:noFill/>" if ser[:no_line]
|
|
3922
|
-
parts << %(<a:solidFill>#{color_xml(ser[:line_color])}</a:solidFill>) if ser[:line_color]
|
|
3923
|
-
parts << %(<a:prstDash val="#{xml_escape(ser[:line_dash])}"/>) if ser[:line_dash]
|
|
3924
|
-
case ser[:line_join]
|
|
3925
|
-
when "round" then parts << "<a:round/>"
|
|
3926
|
-
when "bevel" then parts << "<a:bevel/>"
|
|
3927
|
-
when "miter"
|
|
3928
|
-
parts << (ser[:line_miter_limit] ? "<a:miter lim=\"#{ser[:line_miter_limit].to_i}\"/>" : "<a:miter/>")
|
|
3929
|
-
end
|
|
3930
|
-
parts << "</a:ln>"
|
|
3931
|
-
end
|
|
3932
|
-
parts << "</c:spPr>"
|
|
3933
|
-
end
|
|
3934
|
-
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]
|
|
3935
|
-
parts << "<c:marker>"
|
|
3936
|
-
parts << %(<c:symbol val="#{xml_escape(ser[:marker_symbol])}"/>) if ser[:marker_symbol]
|
|
3937
|
-
parts << %(<c:size val="#{ser[:marker_size]}"/>) if ser[:marker_size]
|
|
3938
|
-
if ser[:marker_fill] || ser[:marker_no_fill] || ser[:marker_line_color] || ser[:marker_line_dash] || ser[:marker_no_line]
|
|
3939
|
-
parts << "<c:spPr>"
|
|
3940
|
-
parts << "<a:noFill/>" if ser[:marker_no_fill]
|
|
3941
|
-
parts << %(<a:solidFill>#{color_xml(ser[:marker_fill])}</a:solidFill>) if ser[:marker_fill]
|
|
3942
|
-
if ser[:marker_no_line]
|
|
3943
|
-
parts << "<a:ln><a:noFill/></a:ln>"
|
|
3944
|
-
elsif ser[:marker_line_color] || ser[:marker_line_dash]
|
|
3945
|
-
mk_ln_w = ser[:marker_line_width] ? %( w="#{(ser[:marker_line_width] * 12_700).to_i}") : ""
|
|
3946
|
-
mk_ln_f = ser[:marker_line_color] ? %(<a:solidFill>#{color_xml(ser[:marker_line_color])}</a:solidFill>) : ""
|
|
3947
|
-
mk_ln_d = ser[:marker_line_dash] ? %(<a:prstDash val="#{xml_escape(ser[:marker_line_dash])}"/>) : ""
|
|
3948
|
-
parts << "<a:ln#{mk_ln_w}>#{mk_ln_f}#{mk_ln_d}</a:ln>"
|
|
3949
|
-
end
|
|
3950
|
-
parts << "</c:spPr>"
|
|
3951
|
-
end
|
|
3952
|
-
parts << "</c:marker>"
|
|
3953
|
-
end
|
|
3954
|
-
if (dl = ser[:data_labels] || chart[:data_labels])
|
|
3955
|
-
parts << "<c:dLbls>"
|
|
3956
|
-
dl[:labels]&.each do |lbl|
|
|
3957
|
-
parts << "<c:dLbl>"
|
|
3958
|
-
parts << %(<c:idx val="#{lbl[:idx]}"/>)
|
|
3959
|
-
if lbl[:delete]
|
|
3960
|
-
parts << '<c:delete val="1"/>'
|
|
3961
|
-
else
|
|
3962
|
-
if (lbl_layout = lbl[:layout])
|
|
3963
|
-
ml = +""
|
|
3964
|
-
ml << %(<c:x val="#{lbl_layout[:x]}"/>) if lbl_layout[:x]
|
|
3965
|
-
ml << %(<c:y val="#{lbl_layout[:y]}"/>) if lbl_layout[:y]
|
|
3966
|
-
parts << (ml.empty? ? "<c:layout/>" : "<c:layout><c:manualLayout>#{ml}</c:manualLayout></c:layout>")
|
|
3967
|
-
end
|
|
3968
|
-
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]
|
|
3969
|
-
if lbl[:num_fmt]
|
|
3970
|
-
lnf = lbl[:num_fmt]
|
|
3971
|
-
lnf_src = lnf[:source_linked] ? ' sourceLinked="1"' : ""
|
|
3972
|
-
parts << %(<c:numFmt formatCode="#{xml_escape(lnf[:format_code])}"#{lnf_src}/>)
|
|
3973
|
-
end
|
|
3974
|
-
lbl_sp = +""
|
|
3975
|
-
lbl_sp << %(<a:solidFill>#{color_xml(lbl[:fill_color])}</a:solidFill>) if lbl[:fill_color]
|
|
3976
|
-
lbl_sp << "<a:noFill/>" if lbl[:no_fill]
|
|
3977
|
-
if lbl[:line_color] || lbl[:line_width] || lbl[:line_dash]
|
|
3978
|
-
lbl_lw = lbl[:line_width] ? %( w="#{(lbl[:line_width] * 12_700).to_i}") : ""
|
|
3979
|
-
lbl_lf = lbl[:line_color] ? %(<a:solidFill>#{color_xml(lbl[:line_color])}</a:solidFill>) : ""
|
|
3980
|
-
lbl_ld = lbl[:line_dash] ? %(<a:prstDash val="#{xml_escape(lbl[:line_dash])}"/>) : ""
|
|
3981
|
-
lbl_sp << "<a:ln#{lbl_lw}>#{lbl_lf}#{lbl_ld}</a:ln>"
|
|
3982
|
-
end
|
|
3983
|
-
parts << "<c:spPr>#{lbl_sp}</c:spPr>" unless lbl_sp.empty?
|
|
3984
|
-
parts << build_axis_txpr(nil, lbl[:font]) if lbl[:font]
|
|
3985
|
-
parts << %(<c:dLblPos val="#{lbl[:position]}"/>) if lbl[:position]
|
|
3986
|
-
parts << "<c:showLegendKey val=\"#{lbl[:show_legend_key] ? 1 : 0}\"/>" unless lbl[:show_legend_key].nil?
|
|
3987
|
-
parts << "<c:showVal val=\"#{lbl[:show_val] ? 1 : 0}\"/>" unless lbl[:show_val].nil?
|
|
3988
|
-
parts << "<c:showCatName val=\"#{lbl[:show_cat_name] ? 1 : 0}\"/>" unless lbl[:show_cat_name].nil?
|
|
3989
|
-
parts << "<c:showSerName val=\"#{lbl[:show_ser_name] ? 1 : 0}\"/>" unless lbl[:show_ser_name].nil?
|
|
3990
|
-
parts << "<c:showPercent val=\"#{lbl[:show_percent] ? 1 : 0}\"/>" unless lbl[:show_percent].nil?
|
|
3991
|
-
parts << "<c:showBubbleSize val=\"#{lbl[:show_bubble_size] ? 1 : 0}\"/>" unless lbl[:show_bubble_size].nil?
|
|
3992
|
-
parts << "<c:separator>#{xml_escape(lbl[:separator])}</c:separator>" if lbl[:separator]
|
|
3993
|
-
end
|
|
3994
|
-
parts << "</c:dLbl>"
|
|
3995
|
-
end
|
|
3996
|
-
if dl[:num_fmt]
|
|
3997
|
-
nf = dl[:num_fmt]
|
|
3998
|
-
nf_src = nf[:source_linked] ? ' sourceLinked="1"' : ""
|
|
3999
|
-
parts << %(<c:numFmt formatCode="#{xml_escape(nf[:format_code])}"#{nf_src}/>)
|
|
4000
|
-
end
|
|
4001
|
-
dl_sp_children = +""
|
|
4002
|
-
dl_sp_children << %(<a:solidFill>#{color_xml(dl[:fill_color])}</a:solidFill>) if dl[:fill_color]
|
|
4003
|
-
dl_sp_children << "<a:noFill/>" if dl[:no_fill]
|
|
4004
|
-
if dl[:line_color] || dl[:line_width] || dl[:line_dash]
|
|
4005
|
-
dl_lw = dl[:line_width] ? %( w="#{(dl[:line_width] * 12_700).to_i}") : ""
|
|
4006
|
-
dl_lf = dl[:line_color] ? %(<a:solidFill>#{color_xml(dl[:line_color])}</a:solidFill>) : ""
|
|
4007
|
-
dl_ld = dl[:line_dash] ? %(<a:prstDash val="#{xml_escape(dl[:line_dash])}"/>) : ""
|
|
4008
|
-
dl_sp_children << "<a:ln#{dl_lw}>#{dl_lf}#{dl_ld}</a:ln>"
|
|
4009
|
-
end
|
|
4010
|
-
parts << "<c:spPr>#{dl_sp_children}</c:spPr>" unless dl_sp_children.empty?
|
|
4011
|
-
parts << build_axis_txpr(nil, dl[:font]) if dl[:font]
|
|
4012
|
-
parts << %(<c:dLblPos val="#{dl[:position]}"/>) if dl[:position]
|
|
4013
|
-
parts << "<c:showLegendKey val=\"#{dl[:show_legend_key] ? 1 : 0}\"/>" unless dl[:show_legend_key].nil?
|
|
4014
|
-
parts << "<c:showVal val=\"#{dl[:show_val] ? 1 : 0}\"/>" unless dl[:show_val].nil?
|
|
4015
|
-
parts << "<c:showCatName val=\"#{dl[:show_cat_name] ? 1 : 0}\"/>" unless dl[:show_cat_name].nil?
|
|
4016
|
-
parts << "<c:showSerName val=\"#{dl[:show_ser_name] ? 1 : 0}\"/>" unless dl[:show_ser_name].nil?
|
|
4017
|
-
parts << "<c:showPercent val=\"#{dl[:show_percent] ? 1 : 0}\"/>" unless dl[:show_percent].nil?
|
|
4018
|
-
parts << "<c:showBubbleSize val=\"#{dl[:show_bubble_size] ? 1 : 0}\"/>" unless dl[:show_bubble_size].nil?
|
|
4019
|
-
parts << "<c:separator>#{xml_escape(dl[:separator])}</c:separator>" if dl[:separator]
|
|
4020
|
-
sl = dl[:show_leader_lines]
|
|
4021
|
-
parts << "<c:showLeaderLines val=\"#{sl ? 1 : 0}\"/>" unless sl.nil?
|
|
4022
|
-
if dl[:leader_lines]
|
|
4023
|
-
ll_spec = dl[:leader_lines]
|
|
4024
|
-
if ll_spec.is_a?(Hash)
|
|
4025
|
-
ll_sp = +""
|
|
4026
|
-
if ll_spec[:line_color] || ll_spec[:line_width] || ll_spec[:line_dash]
|
|
4027
|
-
ll_lw = ll_spec[:line_width] ? %( w="#{(ll_spec[:line_width] * 12_700).to_i}") : ""
|
|
4028
|
-
ll_lf = ll_spec[:line_color] ? %(<a:solidFill>#{color_xml(ll_spec[:line_color])}</a:solidFill>) : ""
|
|
4029
|
-
ll_ld = ll_spec[:line_dash] ? %(<a:prstDash val="#{xml_escape(ll_spec[:line_dash])}"/>) : ""
|
|
4030
|
-
ll_sp << "<a:ln#{ll_lw}>#{ll_lf}#{ll_ld}</a:ln>"
|
|
4031
|
-
end
|
|
4032
|
-
parts << (ll_sp.empty? ? "<c:leaderLines/>" : "<c:leaderLines><c:spPr>#{ll_sp}</c:spPr></c:leaderLines>")
|
|
4033
|
-
else
|
|
4034
|
-
parts << "<c:leaderLines/>"
|
|
4035
|
-
end
|
|
4036
|
-
end
|
|
4037
|
-
parts << "</c:dLbls>"
|
|
4038
|
-
end
|
|
4039
|
-
trendline_list = ser[:trendlines] || (ser[:trendline] ? [ser[:trendline]] : [])
|
|
4040
|
-
trendline_list.each do |tl|
|
|
4041
|
-
parts << "<c:trendline>"
|
|
4042
|
-
parts << "<c:name>#{xml_escape(tl[:name])}</c:name>" if tl[:name]
|
|
4043
|
-
tl_sp_children = +""
|
|
4044
|
-
if tl[:line_color] || tl[:line_width] || tl[:line_dash]
|
|
4045
|
-
tl_ln_w = tl[:line_width] ? %( w="#{(tl[:line_width] * 12_700).to_i}") : ""
|
|
4046
|
-
tl_ln_f = tl[:line_color] ? %(<a:solidFill>#{color_xml(tl[:line_color])}</a:solidFill>) : ""
|
|
4047
|
-
tl_ln_d = tl[:line_dash] ? %(<a:prstDash val="#{xml_escape(tl[:line_dash])}"/>) : ""
|
|
4048
|
-
tl_sp_children << "<a:ln#{tl_ln_w}>#{tl_ln_f}#{tl_ln_d}</a:ln>"
|
|
4049
|
-
end
|
|
4050
|
-
parts << "<c:spPr>#{tl_sp_children}</c:spPr>" unless tl_sp_children.empty?
|
|
4051
|
-
parts << %(<c:trendlineType val="#{xml_escape(tl[:type] || "linear")}"/>)
|
|
4052
|
-
parts << %(<c:order val="#{tl[:order]}"/>) if tl[:order]
|
|
4053
|
-
parts << %(<c:period val="#{tl[:period]}"/>) if tl[:period]
|
|
4054
|
-
parts << %(<c:forward val="#{tl[:forward]}"/>) if tl[:forward]
|
|
4055
|
-
parts << %(<c:backward val="#{tl[:backward]}"/>) if tl[:backward]
|
|
4056
|
-
parts << %(<c:intercept val="#{tl[:intercept]}"/>) if tl[:intercept]
|
|
4057
|
-
parts << %(<c:dispRSqr val="#{tl[:disp_r_sqr] ? 1 : 0}"/>) unless tl[:disp_r_sqr].nil?
|
|
4058
|
-
parts << %(<c:dispEq val="#{tl[:disp_eq] ? 1 : 0}"/>) unless tl[:disp_eq].nil?
|
|
4059
|
-
if tl[:label]
|
|
4060
|
-
lbl = tl[:label]
|
|
4061
|
-
parts << "<c:trendlineLbl>"
|
|
4062
|
-
parts << build_title_layout_xml(lbl[:layout])
|
|
4063
|
-
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]
|
|
4064
|
-
if lbl[:num_fmt]
|
|
4065
|
-
nf = lbl[:num_fmt]
|
|
4066
|
-
nf_src = nf.is_a?(Hash) ? nf : { format_code: nf }
|
|
4067
|
-
nf_linked = if nf_src.key?(:source_linked)
|
|
4068
|
-
nf_src[:source_linked] ? 1 : 0
|
|
4069
|
-
else
|
|
4070
|
-
0
|
|
4071
|
-
end
|
|
4072
|
-
parts << %(<c:numFmt formatCode="#{xml_escape(nf_src[:format_code])}" sourceLinked="#{nf_linked}"/>)
|
|
4073
|
-
end
|
|
4074
|
-
tll_sp = +""
|
|
4075
|
-
tll_sp << %(<a:solidFill>#{color_xml(lbl[:fill_color])}</a:solidFill>) if lbl[:fill_color]
|
|
4076
|
-
tll_sp << "<a:noFill/>" if lbl[:no_fill]
|
|
4077
|
-
if lbl[:line_color] || lbl[:line_width] || lbl[:line_dash]
|
|
4078
|
-
tll_lw = lbl[:line_width] ? %( w="#{(lbl[:line_width] * 12_700).to_i}") : ""
|
|
4079
|
-
tll_lf = lbl[:line_color] ? %(<a:solidFill>#{color_xml(lbl[:line_color])}</a:solidFill>) : ""
|
|
4080
|
-
tll_ld = lbl[:line_dash] ? %(<a:prstDash val="#{xml_escape(lbl[:line_dash])}"/>) : ""
|
|
4081
|
-
tll_sp << "<a:ln#{tll_lw}>#{tll_lf}#{tll_ld}</a:ln>"
|
|
4082
|
-
end
|
|
4083
|
-
parts << "<c:spPr>#{tll_sp}</c:spPr>" unless tll_sp.empty?
|
|
4084
|
-
parts << build_axis_txpr(nil, lbl[:font]) if lbl[:font]
|
|
4085
|
-
parts << "</c:trendlineLbl>"
|
|
4086
|
-
end
|
|
4087
|
-
parts << "</c:trendline>"
|
|
4088
|
-
end
|
|
4089
|
-
err_bars_list = ser[:error_bars_list] || (ser[:error_bars] ? [ser[:error_bars]] : [])
|
|
4090
|
-
err_bars_list.each do |eb|
|
|
4091
|
-
parts << "<c:errBars>"
|
|
4092
|
-
parts << %(<c:errDir val="#{xml_escape(eb[:direction])}"/>) if eb[:direction]
|
|
4093
|
-
parts << %(<c:errBarType val="#{xml_escape(eb[:bar_type] || "both")}"/>)
|
|
4094
|
-
parts << %(<c:errValType val="#{xml_escape(eb[:val_type] || "fixedVal")}"/>)
|
|
4095
|
-
parts << %(<c:noEndCap val="#{eb[:no_end_cap] ? 1 : 0}"/>) unless eb[:no_end_cap].nil?
|
|
4096
|
-
parts << "<c:plus><c:numRef><c:f>#{xml_escape(eb[:plus])}</c:f></c:numRef></c:plus>" if eb[:plus]
|
|
4097
|
-
parts << "<c:minus><c:numRef><c:f>#{xml_escape(eb[:minus])}</c:f></c:numRef></c:minus>" if eb[:minus]
|
|
4098
|
-
parts << %(<c:val val="#{eb[:val]}"/>) if eb[:val]
|
|
4099
|
-
eb_sp_children = +""
|
|
4100
|
-
eb_sp_children << %(<a:solidFill>#{color_xml(eb[:fill_color])}</a:solidFill>) if eb[:fill_color]
|
|
4101
|
-
eb_sp_children << "<a:noFill/>" if eb[:no_fill]
|
|
4102
|
-
if eb[:line_color] || eb[:line_width] || eb[:line_dash]
|
|
4103
|
-
eb_ln_w = eb[:line_width] ? %( w="#{(eb[:line_width] * 12_700).to_i}") : ""
|
|
4104
|
-
eb_ln_f = eb[:line_color] ? %(<a:solidFill>#{color_xml(eb[:line_color])}</a:solidFill>) : ""
|
|
4105
|
-
eb_ln_d = eb[:line_dash] ? %(<a:prstDash val="#{xml_escape(eb[:line_dash])}"/>) : ""
|
|
4106
|
-
eb_sp_children << "<a:ln#{eb_ln_w}>#{eb_ln_f}#{eb_ln_d}</a:ln>"
|
|
4107
|
-
end
|
|
4108
|
-
parts << "<c:spPr>#{eb_sp_children}</c:spPr>" unless eb_sp_children.empty?
|
|
4109
|
-
parts << "</c:errBars>"
|
|
4110
|
-
end
|
|
4111
|
-
uses_xy = %w[scatterChart bubbleChart].include?(chart_type)
|
|
4112
|
-
cat_tag = uses_xy ? "xVal" : "cat"
|
|
4113
|
-
val_tag = uses_xy ? "yVal" : "val"
|
|
4114
|
-
if ser[:cat_ref]
|
|
4115
|
-
parts << if uses_xy || ser[:cat_ref_type] == :num
|
|
4116
|
-
"<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}>"
|
|
4117
|
-
else
|
|
4118
|
-
"<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}>"
|
|
4119
|
-
end
|
|
4120
|
-
end
|
|
4121
|
-
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]
|
|
4122
|
-
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]
|
|
4123
|
-
parts << %(<c:smooth val="#{ser[:smooth] ? 1 : 0}"/>) unless ser[:smooth].nil?
|
|
4124
|
-
parts << %(<c:shape val="#{xml_escape(ser[:shape])}"/>) if ser[:shape]
|
|
4125
|
-
parts << "</c:ser>"
|
|
4126
|
-
end
|
|
4127
|
-
|
|
4128
|
-
if chart[:band_fmts]
|
|
4129
|
-
parts << "<c:bandFmts>"
|
|
4130
|
-
chart[:band_fmts].each do |bf|
|
|
4131
|
-
parts << "<c:bandFmt><c:idx val=\"#{bf[:idx]}\"/>"
|
|
4132
|
-
bf_sp = +""
|
|
4133
|
-
bf_sp << %(<a:solidFill>#{color_xml(bf[:fill_color])}</a:solidFill>) if bf[:fill_color]
|
|
4134
|
-
bf_sp << "<a:noFill/>" if bf[:no_fill]
|
|
4135
|
-
if bf[:line_color] || bf[:line_width] || bf[:line_dash]
|
|
4136
|
-
bf_lw = bf[:line_width] ? %( w="#{(bf[:line_width] * 12_700).to_i}") : ""
|
|
4137
|
-
bf_lf = bf[:line_color] ? %(<a:solidFill>#{color_xml(bf[:line_color])}</a:solidFill>) : ""
|
|
4138
|
-
bf_ld = bf[:line_dash] ? %(<a:prstDash val="#{xml_escape(bf[:line_dash])}"/>) : ""
|
|
4139
|
-
bf_sp << "<a:ln#{bf_lw}>#{bf_lf}#{bf_ld}</a:ln>"
|
|
4140
|
-
end
|
|
4141
|
-
parts << "<c:spPr>#{bf_sp}</c:spPr>" unless bf_sp.empty?
|
|
4142
|
-
parts << "</c:bandFmt>"
|
|
4143
|
-
end
|
|
4144
|
-
parts << "</c:bandFmts>"
|
|
4145
|
-
end
|
|
4146
|
-
|
|
4147
|
-
parts << %(<c:gapWidth val="#{chart[:gap_width]}"/>) if chart[:gap_width]
|
|
4148
|
-
parts << %(<c:splitType val="#{xml_escape(chart[:split_type])}"/>) if chart[:split_type]
|
|
4149
|
-
parts << %(<c:splitPos val="#{chart[:split_pos]}"/>) if chart[:split_pos]
|
|
4150
|
-
if chart[:cust_split]&.any?
|
|
4151
|
-
parts << "<c:custSplit>"
|
|
4152
|
-
chart[:cust_split].each { |idx| parts << %(<c:secondPiePt val="#{idx}"/>) }
|
|
4153
|
-
parts << "</c:custSplit>"
|
|
4154
|
-
end
|
|
4155
|
-
parts << %(<c:secondPieSize val="#{chart[:second_pie_size]}"/>) if chart[:second_pie_size]
|
|
4156
|
-
parts << %(<c:gapDepth val="#{chart[:gap_depth]}"/>) if chart[:gap_depth]
|
|
4157
|
-
parts << %(<c:overlap val="#{chart[:overlap]}"/>) if chart[:overlap]
|
|
4158
|
-
parts << %(<c:shape val="#{chart[:bar_shape]}"/>) if chart[:bar_shape]
|
|
4159
|
-
b3d = chart[:bubble_3d]
|
|
4160
|
-
parts << %(<c:bubble3D val="#{b3d ? 1 : 0}"/>) unless b3d.nil?
|
|
4161
|
-
parts << %(<c:bubbleScale val="#{chart[:bubble_scale]}"/>) if chart[:bubble_scale]
|
|
4162
|
-
snb = chart[:show_neg_bubbles]
|
|
4163
|
-
parts << %(<c:showNegBubbles val="#{snb ? 1 : 0}"/>) unless snb.nil?
|
|
4164
|
-
parts << %(<c:sizeRepresents val="#{chart[:size_represents]}"/>) if chart[:size_represents]
|
|
4165
|
-
parts << %(<c:firstSliceAng val="#{chart[:first_slice_ang]}"/>) if chart[:first_slice_ang]
|
|
4166
|
-
parts << %(<c:holeSize val="#{chart[:hole_size]}"/>) if chart[:hole_size]
|
|
4167
|
-
if chart[:ser_lines]
|
|
4168
|
-
sl_spec = chart[:ser_lines]
|
|
4169
|
-
if sl_spec.is_a?(Hash)
|
|
4170
|
-
sl_sp = +""
|
|
4171
|
-
if sl_spec[:line_color] || sl_spec[:line_width] || sl_spec[:line_dash]
|
|
4172
|
-
sl_lw = sl_spec[:line_width] ? %( w="#{(sl_spec[:line_width] * 12_700).to_i}") : ""
|
|
4173
|
-
sl_lf = sl_spec[:line_color] ? %(<a:solidFill>#{color_xml(sl_spec[:line_color])}</a:solidFill>) : ""
|
|
4174
|
-
sl_ld = sl_spec[:line_dash] ? %(<a:prstDash val="#{xml_escape(sl_spec[:line_dash])}"/>) : ""
|
|
4175
|
-
sl_sp << "<a:ln#{sl_lw}>#{sl_lf}#{sl_ld}</a:ln>"
|
|
4176
|
-
end
|
|
4177
|
-
parts << (sl_sp.empty? ? "<c:serLines/>" : "<c:serLines><c:spPr>#{sl_sp}</c:spPr></c:serLines>")
|
|
4178
|
-
else
|
|
4179
|
-
parts << "<c:serLines/>"
|
|
4180
|
-
end
|
|
4181
|
-
end
|
|
4182
|
-
if chart[:drop_lines]
|
|
4183
|
-
dl_spec = chart[:drop_lines]
|
|
4184
|
-
if dl_spec.is_a?(Hash)
|
|
4185
|
-
dl_sp = +""
|
|
4186
|
-
if dl_spec[:line_color] || dl_spec[:line_width] || dl_spec[:line_dash]
|
|
4187
|
-
dl_lw = dl_spec[:line_width] ? %( w="#{(dl_spec[:line_width] * 12_700).to_i}") : ""
|
|
4188
|
-
dl_lf = dl_spec[:line_color] ? %(<a:solidFill>#{color_xml(dl_spec[:line_color])}</a:solidFill>) : ""
|
|
4189
|
-
dl_ld = dl_spec[:line_dash] ? %(<a:prstDash val="#{xml_escape(dl_spec[:line_dash])}"/>) : ""
|
|
4190
|
-
dl_sp << "<a:ln#{dl_lw}>#{dl_lf}#{dl_ld}</a:ln>"
|
|
4191
|
-
end
|
|
4192
|
-
parts << (dl_sp.empty? ? "<c:dropLines/>" : "<c:dropLines><c:spPr>#{dl_sp}</c:spPr></c:dropLines>")
|
|
4193
|
-
else
|
|
4194
|
-
parts << "<c:dropLines/>"
|
|
4195
|
-
end
|
|
4196
|
-
end
|
|
4197
|
-
if chart[:hi_low_lines]
|
|
4198
|
-
hl_spec = chart[:hi_low_lines]
|
|
4199
|
-
if hl_spec.is_a?(Hash)
|
|
4200
|
-
hl_sp = +""
|
|
4201
|
-
if hl_spec[:line_color] || hl_spec[:line_width] || hl_spec[:line_dash]
|
|
4202
|
-
hl_lw = hl_spec[:line_width] ? %( w="#{(hl_spec[:line_width] * 12_700).to_i}") : ""
|
|
4203
|
-
hl_lf = hl_spec[:line_color] ? %(<a:solidFill>#{color_xml(hl_spec[:line_color])}</a:solidFill>) : ""
|
|
4204
|
-
hl_ld = hl_spec[:line_dash] ? %(<a:prstDash val="#{xml_escape(hl_spec[:line_dash])}"/>) : ""
|
|
4205
|
-
hl_sp << "<a:ln#{hl_lw}>#{hl_lf}#{hl_ld}</a:ln>"
|
|
4206
|
-
end
|
|
4207
|
-
parts << (hl_sp.empty? ? "<c:hiLowLines/>" : "<c:hiLowLines><c:spPr>#{hl_sp}</c:spPr></c:hiLowLines>")
|
|
4208
|
-
else
|
|
4209
|
-
parts << "<c:hiLowLines/>"
|
|
4210
|
-
end
|
|
4211
|
-
end
|
|
4212
|
-
if chart[:up_down_bars]
|
|
4213
|
-
udb = chart[:up_down_bars]
|
|
4214
|
-
parts << "<c:upDownBars>"
|
|
4215
|
-
parts << %(<c:gapWidth val="#{udb[:gap_width]}"/>) if udb.is_a?(Hash) && udb[:gap_width]
|
|
4216
|
-
%i[up_bars down_bars].each do |bar_key|
|
|
4217
|
-
tag = bar_key == :up_bars ? "upBars" : "downBars"
|
|
4218
|
-
bar = udb.is_a?(Hash) ? udb[bar_key] : nil
|
|
4219
|
-
if bar
|
|
4220
|
-
bar_sp = +""
|
|
4221
|
-
bar_sp << %(<a:solidFill>#{color_xml(bar[:fill_color])}</a:solidFill>) if bar[:fill_color]
|
|
4222
|
-
bar_sp << "<a:noFill/>" if bar[:no_fill]
|
|
4223
|
-
if bar[:line_color] || bar[:line_width] || bar[:line_dash]
|
|
4224
|
-
b_lw = bar[:line_width] ? %( w="#{(bar[:line_width] * 12_700).to_i}") : ""
|
|
4225
|
-
b_lf = bar[:line_color] ? %(<a:solidFill>#{color_xml(bar[:line_color])}</a:solidFill>) : ""
|
|
4226
|
-
b_ld = bar[:line_dash] ? %(<a:prstDash val="#{xml_escape(bar[:line_dash])}"/>) : ""
|
|
4227
|
-
bar_sp << "<a:ln#{b_lw}>#{b_lf}#{b_ld}</a:ln>"
|
|
4228
|
-
end
|
|
4229
|
-
parts << (bar_sp.empty? ? "<c:#{tag}/>" : "<c:#{tag}><c:spPr>#{bar_sp}</c:spPr></c:#{tag}>")
|
|
4230
|
-
else
|
|
4231
|
-
parts << "<c:#{tag}/>"
|
|
4232
|
-
end
|
|
4233
|
-
end
|
|
4234
|
-
parts << "</c:upDownBars>"
|
|
4235
|
-
end
|
|
4236
|
-
mk = chart[:marker]
|
|
4237
|
-
parts << %(<c:marker val="#{mk ? 1 : 0}"/>) unless mk.nil?
|
|
4238
|
-
sm = chart[:smooth]
|
|
4239
|
-
parts << %(<c:smooth val="#{sm ? 1 : 0}"/>) unless sm.nil?
|
|
4240
|
-
three_axes = THREE_AXIS_CHARTS.include?(chart_type)
|
|
4241
|
-
unless no_axes
|
|
4242
|
-
parts << '<c:axId val="1"/><c:axId val="2"/>'
|
|
4243
|
-
parts << '<c:axId val="3"/>' if three_axes
|
|
4244
|
-
end
|
|
4245
|
-
parts << "</c:#{chart_type}>"
|
|
4246
|
-
|
|
4247
|
-
unless no_axes
|
|
4248
|
-
cat_del = chart[:cat_axis_delete] ? 1 : 0
|
|
4249
|
-
cat_orient = chart[:cat_axis_orientation] || "minMax"
|
|
4250
|
-
cat_ax_tag = chart[:cat_axis_type] == :date ? "dateAx" : "catAx"
|
|
4251
|
-
parts << %(<c:#{cat_ax_tag}><c:axId val="1"/><c:scaling>)
|
|
4252
|
-
parts << %(<c:logBase val="#{chart[:cat_axis_log_base]}"/>) if chart[:cat_axis_log_base]
|
|
4253
|
-
parts << %(<c:orientation val="#{cat_orient}"/>)
|
|
4254
|
-
parts << %(<c:max val="#{chart[:cat_axis_scaling_max]}"/>) if chart[:cat_axis_scaling_max]
|
|
4255
|
-
parts << %(<c:min val="#{chart[:cat_axis_scaling_min]}"/>) if chart[:cat_axis_scaling_min]
|
|
4256
|
-
parts << %(</c:scaling><c:delete val="#{cat_del}"/><c:axPos val="#{chart[:cat_axis_pos] || "b"}"/>)
|
|
4257
|
-
parts << gridlines_xml("majorGridlines", chart[:cat_axis_major_gridlines])
|
|
4258
|
-
parts << gridlines_xml("minorGridlines", chart[:cat_axis_minor_gridlines])
|
|
4259
|
-
if chart[:cat_axis_title]
|
|
4260
|
-
cat_title_spec = merge_flat_title_styling(chart[:cat_axis_title], chart, :cat_axis_title)
|
|
4261
|
-
parts << build_chart_title_xml(cat_title_spec)
|
|
4262
|
-
end
|
|
4263
|
-
if (cnf = chart[:cat_axis_num_fmt])
|
|
4264
|
-
sl = cnf[:source_linked] ? 1 : 0
|
|
4265
|
-
parts << %(<c:numFmt formatCode="#{xml_escape(cnf[:format_code])}" sourceLinked="#{sl}"/>)
|
|
4266
|
-
end
|
|
4267
|
-
parts << %(<c:majorTickMark val="#{chart[:cat_axis_major_tick_mark]}"/>) if chart[:cat_axis_major_tick_mark]
|
|
4268
|
-
parts << %(<c:minorTickMark val="#{chart[:cat_axis_minor_tick_mark]}"/>) if chart[:cat_axis_minor_tick_mark]
|
|
4269
|
-
parts << %(<c:tickLblPos val="#{chart[:cat_axis_tick_lbl_pos]}"/>) if chart[:cat_axis_tick_lbl_pos]
|
|
4270
|
-
if chart[:cat_axis_line_color] || chart[:cat_axis_fill] || chart[:cat_axis_no_fill] || chart[:cat_axis_line_dash]
|
|
4271
|
-
parts << "<c:spPr>"
|
|
4272
|
-
parts << "<a:noFill/>" if chart[:cat_axis_no_fill]
|
|
4273
|
-
parts << %(<a:solidFill>#{color_xml(chart[:cat_axis_fill])}</a:solidFill>) if chart[:cat_axis_fill]
|
|
4274
|
-
if chart[:cat_axis_line_color] || chart[:cat_axis_line_dash]
|
|
4275
|
-
w_attr = chart[:cat_axis_line_width] ? %( w="#{chart[:cat_axis_line_width]}") : ""
|
|
4276
|
-
ca_ln_f = chart[:cat_axis_line_color] ? %(<a:solidFill>#{srgb_clr_xml(chart[:cat_axis_line_color])}</a:solidFill>) : ""
|
|
4277
|
-
ca_ln_d = chart[:cat_axis_line_dash] ? %(<a:prstDash val="#{xml_escape(chart[:cat_axis_line_dash])}"/>) : ""
|
|
4278
|
-
parts << "<a:ln#{w_attr}>#{ca_ln_f}#{ca_ln_d}</a:ln>"
|
|
4279
|
-
end
|
|
4280
|
-
parts << "</c:spPr>"
|
|
4281
|
-
end
|
|
4282
|
-
parts << build_axis_txpr(chart[:cat_axis_label_rotation], chart[:cat_axis_font])
|
|
4283
|
-
parts << '<c:crossAx val="2"/>'
|
|
4284
|
-
parts << %(<c:crosses val="#{chart[:cat_axis_crosses]}"/>) if chart[:cat_axis_crosses]
|
|
4285
|
-
parts << %(<c:crossesAt val="#{chart[:cat_axis_crosses_at]}"/>) if !chart[:cat_axis_crosses] && chart[:cat_axis_crosses_at]
|
|
4286
|
-
ca_auto = chart[:cat_axis_auto]
|
|
4287
|
-
parts << %(<c:auto val="#{ca_auto ? 1 : 0}"/>) unless ca_auto.nil?
|
|
4288
|
-
parts << %(<c:lblAlgn val="#{xml_escape(chart[:cat_axis_lbl_algn])}"/>) if chart[:cat_axis_lbl_algn]
|
|
4289
|
-
parts << %(<c:lblOffset val="#{chart[:cat_axis_lbl_offset]}"/>) if chart[:cat_axis_lbl_offset]
|
|
4290
|
-
parts << %(<c:tickLblSkip val="#{chart[:cat_axis_tick_lbl_skip]}"/>) if chart[:cat_axis_tick_lbl_skip]
|
|
4291
|
-
parts << %(<c:tickMarkSkip val="#{chart[:cat_axis_tick_mark_skip]}"/>) if chart[:cat_axis_tick_mark_skip]
|
|
4292
|
-
nml = chart[:cat_axis_no_multi_lvl_lbl]
|
|
4293
|
-
parts << %(<c:noMultiLvlLbl val="#{nml ? 1 : 0}"/>) unless nml.nil?
|
|
4294
|
-
if cat_ax_tag == "dateAx"
|
|
4295
|
-
parts << %(<c:baseTimeUnit val="#{chart[:cat_axis_base_time_unit]}"/>) if chart[:cat_axis_base_time_unit]
|
|
4296
|
-
parts << %(<c:majorUnit val="#{chart[:cat_axis_major_unit]}"/>) if chart[:cat_axis_major_unit]
|
|
4297
|
-
parts << %(<c:majorTimeUnit val="#{chart[:cat_axis_major_time_unit]}"/>) if chart[:cat_axis_major_time_unit]
|
|
4298
|
-
parts << %(<c:minorUnit val="#{chart[:cat_axis_minor_unit]}"/>) if chart[:cat_axis_minor_unit]
|
|
4299
|
-
parts << %(<c:minorTimeUnit val="#{chart[:cat_axis_minor_time_unit]}"/>) if chart[:cat_axis_minor_time_unit]
|
|
4300
|
-
end
|
|
4301
|
-
parts << "</c:#{cat_ax_tag}>"
|
|
4302
|
-
val_del = chart[:val_axis_delete] ? 1 : 0
|
|
4303
|
-
val_orient = chart[:val_axis_orientation] || "minMax"
|
|
4304
|
-
parts << %(<c:valAx><c:axId val="2"/><c:scaling>)
|
|
4305
|
-
parts << %(<c:logBase val="#{chart[:val_axis_log_base]}"/>) if chart[:val_axis_log_base]
|
|
4306
|
-
parts << %(<c:orientation val="#{val_orient}"/>)
|
|
4307
|
-
parts << %(<c:max val="#{chart[:val_axis_scaling_max]}"/>) if chart[:val_axis_scaling_max]
|
|
4308
|
-
parts << %(<c:min val="#{chart[:val_axis_scaling_min]}"/>) if chart[:val_axis_scaling_min]
|
|
4309
|
-
parts << %(</c:scaling><c:delete val="#{val_del}"/><c:axPos val="#{chart[:val_axis_pos] || "l"}"/>)
|
|
4310
|
-
parts << gridlines_xml("majorGridlines", chart[:val_axis_major_gridlines])
|
|
4311
|
-
parts << gridlines_xml("minorGridlines", chart[:val_axis_minor_gridlines])
|
|
4312
|
-
if chart[:val_axis_title]
|
|
4313
|
-
val_title_spec = merge_flat_title_styling(chart[:val_axis_title], chart, :val_axis_title)
|
|
4314
|
-
parts << build_chart_title_xml(val_title_spec)
|
|
4315
|
-
end
|
|
4316
|
-
if (vnf = chart[:val_axis_num_fmt])
|
|
4317
|
-
sl = vnf[:source_linked] ? 1 : 0
|
|
4318
|
-
parts << %(<c:numFmt formatCode="#{xml_escape(vnf[:format_code])}" sourceLinked="#{sl}"/>)
|
|
4319
|
-
end
|
|
4320
|
-
parts << %(<c:majorTickMark val="#{chart[:val_axis_major_tick_mark]}"/>) if chart[:val_axis_major_tick_mark]
|
|
4321
|
-
parts << %(<c:minorTickMark val="#{chart[:val_axis_minor_tick_mark]}"/>) if chart[:val_axis_minor_tick_mark]
|
|
4322
|
-
parts << %(<c:tickLblPos val="#{chart[:val_axis_tick_lbl_pos]}"/>) if chart[:val_axis_tick_lbl_pos]
|
|
4323
|
-
if chart[:val_axis_line_color] || chart[:val_axis_fill] || chart[:val_axis_no_fill] || chart[:val_axis_line_dash]
|
|
4324
|
-
parts << "<c:spPr>"
|
|
4325
|
-
parts << "<a:noFill/>" if chart[:val_axis_no_fill]
|
|
4326
|
-
parts << %(<a:solidFill>#{color_xml(chart[:val_axis_fill])}</a:solidFill>) if chart[:val_axis_fill]
|
|
4327
|
-
if chart[:val_axis_line_color] || chart[:val_axis_line_dash]
|
|
4328
|
-
w_attr = chart[:val_axis_line_width] ? %( w="#{chart[:val_axis_line_width]}") : ""
|
|
4329
|
-
va_ln_f = chart[:val_axis_line_color] ? %(<a:solidFill>#{srgb_clr_xml(chart[:val_axis_line_color])}</a:solidFill>) : ""
|
|
4330
|
-
va_ln_d = chart[:val_axis_line_dash] ? %(<a:prstDash val="#{xml_escape(chart[:val_axis_line_dash])}"/>) : ""
|
|
4331
|
-
parts << "<a:ln#{w_attr}>#{va_ln_f}#{va_ln_d}</a:ln>"
|
|
4332
|
-
end
|
|
4333
|
-
parts << "</c:spPr>"
|
|
4334
|
-
end
|
|
4335
|
-
parts << build_axis_txpr(chart[:val_axis_label_rotation], chart[:val_axis_font])
|
|
4336
|
-
parts << '<c:crossAx val="1"/>'
|
|
4337
|
-
parts << %(<c:crosses val="#{chart[:val_axis_crosses]}"/>) if chart[:val_axis_crosses]
|
|
4338
|
-
parts << %(<c:crossesAt val="#{chart[:val_axis_crosses_at]}"/>) if !chart[:val_axis_crosses] && chart[:val_axis_crosses_at]
|
|
4339
|
-
parts << %(<c:crossBetween val="#{chart[:val_axis_cross_between]}"/>) if chart[:val_axis_cross_between]
|
|
4340
|
-
parts << %(<c:majorUnit val="#{chart[:val_axis_major_unit]}"/>) if chart[:val_axis_major_unit]
|
|
4341
|
-
parts << %(<c:minorUnit val="#{chart[:val_axis_minor_unit]}"/>) if chart[:val_axis_minor_unit]
|
|
4342
|
-
if chart[:val_axis_disp_units]
|
|
4343
|
-
du = chart[:val_axis_disp_units]
|
|
4344
|
-
if du.is_a?(Hash)
|
|
4345
|
-
parts << "<c:dispUnits>"
|
|
4346
|
-
parts << %(<c:builtInUnit val="#{xml_escape(du[:built_in_unit])}"/>) if du[:built_in_unit]
|
|
4347
|
-
parts << %(<c:custUnit val="#{du[:cust_unit]}"/>) if du[:cust_unit]
|
|
4348
|
-
if du[:label]
|
|
4349
|
-
dul = du[:label]
|
|
4350
|
-
parts << "<c:dispUnitsLbl>"
|
|
4351
|
-
if dul[:num_fmt]
|
|
4352
|
-
nf = dul[:num_fmt]
|
|
4353
|
-
nf_src = nf.is_a?(Hash) ? nf : { format_code: nf }
|
|
4354
|
-
nf_linked = if nf_src.key?(:source_linked)
|
|
4355
|
-
nf_src[:source_linked] ? 1 : 0
|
|
4356
|
-
else
|
|
4357
|
-
0
|
|
4358
|
-
end
|
|
4359
|
-
parts << %(<c:numFmt formatCode="#{xml_escape(nf_src[:format_code])}" sourceLinked="#{nf_linked}"/>)
|
|
4360
|
-
end
|
|
4361
|
-
dul_sp = +""
|
|
4362
|
-
dul_sp << %(<a:solidFill>#{color_xml(dul[:fill_color])}</a:solidFill>) if dul[:fill_color]
|
|
4363
|
-
dul_sp << "<a:noFill/>" if dul[:no_fill]
|
|
4364
|
-
if dul[:line_color] || dul[:line_width] || dul[:line_dash]
|
|
4365
|
-
dul_lw = dul[:line_width] ? %( w="#{(dul[:line_width] * 12_700).to_i}") : ""
|
|
4366
|
-
dul_lf = dul[:line_color] ? %(<a:solidFill>#{color_xml(dul[:line_color])}</a:solidFill>) : ""
|
|
4367
|
-
dul_ld = dul[:line_dash] ? %(<a:prstDash val="#{xml_escape(dul[:line_dash])}"/>) : ""
|
|
4368
|
-
dul_sp << "<a:ln#{dul_lw}>#{dul_lf}#{dul_ld}</a:ln>"
|
|
4369
|
-
end
|
|
4370
|
-
parts << "<c:spPr>#{dul_sp}</c:spPr>" unless dul_sp.empty?
|
|
4371
|
-
parts << build_axis_txpr(nil, dul[:font]) if dul[:font]
|
|
4372
|
-
parts << "</c:dispUnitsLbl>"
|
|
4373
|
-
end
|
|
4374
|
-
parts << "</c:dispUnits>"
|
|
4375
|
-
else
|
|
4376
|
-
parts << %(<c:dispUnits><c:builtInUnit val="#{xml_escape(du.to_s)}"/></c:dispUnits>)
|
|
4377
|
-
end
|
|
4378
|
-
end
|
|
4379
|
-
parts << "</c:valAx>"
|
|
4380
|
-
|
|
4381
|
-
if three_axes
|
|
4382
|
-
parts << '<c:serAx><c:axId val="3"/><c:scaling><c:orientation val="minMax"/></c:scaling>'
|
|
4383
|
-
parts << '<c:delete val="0"/><c:axPos val="b"/><c:crossAx val="2"/></c:serAx>'
|
|
4384
|
-
end
|
|
4385
|
-
end
|
|
4386
|
-
|
|
4387
|
-
if chart[:data_table]
|
|
4388
|
-
dt = chart[:data_table]
|
|
4389
|
-
parts << "<c:dTable>"
|
|
4390
|
-
parts << %(<c:showHorzBorder val="#{dt[:show_horz_border] ? 1 : 0}"/>) unless dt[:show_horz_border].nil?
|
|
4391
|
-
parts << %(<c:showVertBorder val="#{dt[:show_vert_border] ? 1 : 0}"/>) unless dt[:show_vert_border].nil?
|
|
4392
|
-
parts << %(<c:showOutline val="#{dt[:show_outline] ? 1 : 0}"/>) unless dt[:show_outline].nil?
|
|
4393
|
-
parts << %(<c:showKeys val="#{dt[:show_keys] ? 1 : 0}"/>) unless dt[:show_keys].nil?
|
|
4394
|
-
dt_sp_children = +""
|
|
4395
|
-
dt_sp_children << %(<a:solidFill>#{color_xml(dt[:fill_color])}</a:solidFill>) if dt[:fill_color]
|
|
4396
|
-
dt_sp_children << "<a:noFill/>" if dt[:no_fill]
|
|
4397
|
-
if dt[:line_color] || dt[:line_width] || dt[:line_dash]
|
|
4398
|
-
dt_ln_w = dt[:line_width] ? %( w="#{(dt[:line_width] * 12_700).to_i}") : ""
|
|
4399
|
-
dt_ln_f = dt[:line_color] ? %(<a:solidFill>#{color_xml(dt[:line_color])}</a:solidFill>) : ""
|
|
4400
|
-
dt_ln_d = dt[:line_dash] ? %(<a:prstDash val="#{xml_escape(dt[:line_dash])}"/>) : ""
|
|
4401
|
-
dt_sp_children << "<a:ln#{dt_ln_w}>#{dt_ln_f}#{dt_ln_d}</a:ln>"
|
|
4402
|
-
end
|
|
4403
|
-
parts << "<c:spPr>#{dt_sp_children}</c:spPr>" unless dt_sp_children.empty?
|
|
4404
|
-
parts << build_axis_txpr(nil, dt[:font]) if dt[:font]
|
|
4405
|
-
parts << "</c:dTable>"
|
|
4406
|
-
end
|
|
4407
|
-
|
|
4408
|
-
if chart[:plot_area_fill] || chart[:plot_area_no_fill] || chart[:plot_area_line_color] || chart[:plot_area_line_dash]
|
|
4409
|
-
pa_sp = +""
|
|
4410
|
-
pa_sp << %(<a:solidFill>#{color_xml(chart[:plot_area_fill])}</a:solidFill>) if chart[:plot_area_fill]
|
|
4411
|
-
pa_sp << "<a:noFill/>" if chart[:plot_area_no_fill]
|
|
4412
|
-
if chart[:plot_area_line_color] || chart[:plot_area_line_dash]
|
|
4413
|
-
pa_ln_w = chart[:plot_area_line_width] ? %( w="#{chart[:plot_area_line_width].to_i}") : ""
|
|
4414
|
-
pa_ln_f = chart[:plot_area_line_color] ? %(<a:solidFill>#{color_xml(chart[:plot_area_line_color])}</a:solidFill>) : ""
|
|
4415
|
-
pa_ln_d = chart[:plot_area_line_dash] ? %(<a:prstDash val="#{xml_escape(chart[:plot_area_line_dash])}"/>) : ""
|
|
4416
|
-
pa_sp << "<a:ln#{pa_ln_w}>#{pa_ln_f}#{pa_ln_d}</a:ln>"
|
|
4417
|
-
end
|
|
4418
|
-
parts << "<c:spPr>#{pa_sp}</c:spPr>"
|
|
4419
|
-
end
|
|
4420
|
-
|
|
4421
|
-
parts << "</c:plotArea>"
|
|
4422
|
-
legend_pos = chart.dig(:legend, :position) || "r"
|
|
4423
|
-
legend_overlay = chart.dig(:legend, :overlay)
|
|
4424
|
-
parts << %(<c:legend><c:legendPos val="#{legend_pos}"/>)
|
|
4425
|
-
legend_entries = chart.dig(:legend, :entries)
|
|
4426
|
-
legend_entries&.each do |entry|
|
|
4427
|
-
parts << %(<c:legendEntry><c:idx val="#{entry[:idx]}"/>)
|
|
4428
|
-
if !entry[:delete].nil?
|
|
4429
|
-
parts << %(<c:delete val="#{entry[:delete] ? 1 : 0}"/>)
|
|
4430
|
-
elsif entry[:font]
|
|
4431
|
-
parts << build_axis_txpr(nil, entry[:font])
|
|
4432
|
-
end
|
|
4433
|
-
parts << "</c:legendEntry>"
|
|
4434
|
-
end
|
|
4435
|
-
legend_layout = chart.dig(:legend, :layout)
|
|
4436
|
-
if legend_layout.is_a?(Hash)
|
|
4437
|
-
ml_parts = +""
|
|
4438
|
-
ml_parts << %(<c:layoutTarget val="#{xml_escape(legend_layout[:target])}"/>) if legend_layout[:target]
|
|
4439
|
-
ml_parts << %(<c:xMode val="#{xml_escape(legend_layout[:x_mode])}"/>) if legend_layout[:x_mode]
|
|
4440
|
-
ml_parts << %(<c:yMode val="#{xml_escape(legend_layout[:y_mode])}"/>) if legend_layout[:y_mode]
|
|
4441
|
-
ml_parts << %(<c:wMode val="#{xml_escape(legend_layout[:w_mode])}"/>) if legend_layout[:w_mode]
|
|
4442
|
-
ml_parts << %(<c:hMode val="#{xml_escape(legend_layout[:h_mode])}"/>) if legend_layout[:h_mode]
|
|
4443
|
-
ml_parts << %(<c:x val="#{legend_layout[:x]}"/>) if legend_layout[:x]
|
|
4444
|
-
ml_parts << %(<c:y val="#{legend_layout[:y]}"/>) if legend_layout[:y]
|
|
4445
|
-
ml_parts << %(<c:w val="#{legend_layout[:w]}"/>) if legend_layout[:w]
|
|
4446
|
-
ml_parts << %(<c:h val="#{legend_layout[:h]}"/>) if legend_layout[:h]
|
|
4447
|
-
parts << "<c:layout><c:manualLayout>#{ml_parts}</c:manualLayout></c:layout>" unless ml_parts.empty?
|
|
4448
|
-
end
|
|
4449
|
-
parts << %(<c:overlay val="#{legend_overlay ? 1 : 0}"/>) unless legend_overlay.nil?
|
|
4450
|
-
leg_sp_children = +""
|
|
4451
|
-
leg_fill = chart.dig(:legend, :fill_color)
|
|
4452
|
-
leg_sp_children << %(<a:solidFill>#{color_xml(leg_fill)}</a:solidFill>) if leg_fill
|
|
4453
|
-
leg_no_fill = chart.dig(:legend, :no_fill)
|
|
4454
|
-
leg_sp_children << "<a:noFill/>" if leg_no_fill
|
|
4455
|
-
leg_lc = chart.dig(:legend, :line_color)
|
|
4456
|
-
leg_lw = chart.dig(:legend, :line_width)
|
|
4457
|
-
leg_ld = chart.dig(:legend, :line_dash)
|
|
4458
|
-
if leg_lc || leg_lw || leg_ld
|
|
4459
|
-
lw_attr = leg_lw ? %( w="#{(leg_lw * 12_700).to_i}") : ""
|
|
4460
|
-
lf = leg_lc ? %(<a:solidFill>#{color_xml(leg_lc)}</a:solidFill>) : ""
|
|
4461
|
-
ld = leg_ld ? %(<a:prstDash val="#{xml_escape(leg_ld)}"/>) : ""
|
|
4462
|
-
leg_sp_children << "<a:ln#{lw_attr}>#{lf}#{ld}</a:ln>"
|
|
4463
|
-
end
|
|
4464
|
-
parts << "<c:spPr>#{leg_sp_children}</c:spPr>" unless leg_sp_children.empty?
|
|
4465
|
-
if (lfont = chart.dig(:legend, :font))
|
|
4466
|
-
parts << build_axis_txpr(nil, lfont)
|
|
4467
|
-
end
|
|
4468
|
-
parts << %(</c:legend>)
|
|
4469
|
-
pvo = chart[:plot_vis_only]
|
|
4470
|
-
parts << %(<c:plotVisOnly val="#{pvo ? 1 : 0}"/>) unless pvo.nil?
|
|
4471
|
-
parts << %(<c:dispBlanksAs val="#{chart[:disp_blanks_as]}"/>) if chart[:disp_blanks_as]
|
|
4472
|
-
sdom = chart[:show_d_lbls_over_max]
|
|
4473
|
-
parts << %(<c:showDLblsOverMax val="#{sdom ? 1 : 0}"/>) unless sdom.nil?
|
|
4474
|
-
parts << "</c:chart>"
|
|
4475
|
-
if chart[:chart_fill] || chart[:chart_no_fill] || chart[:chart_line_color] || chart[:chart_line_dash]
|
|
4476
|
-
cs_sp = +""
|
|
4477
|
-
cs_sp << %(<a:solidFill>#{color_xml(chart[:chart_fill])}</a:solidFill>) if chart[:chart_fill]
|
|
4478
|
-
cs_sp << "<a:noFill/>" if chart[:chart_no_fill]
|
|
4479
|
-
if chart[:chart_line_color] || chart[:chart_line_dash]
|
|
4480
|
-
cs_lw = chart[:chart_line_width] ? %( w="#{(chart[:chart_line_width] * 12_700).to_i}") : ""
|
|
4481
|
-
cs_ln_f = chart[:chart_line_color] ? %(<a:solidFill>#{color_xml(chart[:chart_line_color])}</a:solidFill>) : ""
|
|
4482
|
-
cs_ln_d = chart[:chart_line_dash] ? %(<a:prstDash val="#{xml_escape(chart[:chart_line_dash])}"/>) : ""
|
|
4483
|
-
cs_sp << "<a:ln#{cs_lw}>#{cs_ln_f}#{cs_ln_d}</a:ln>"
|
|
4484
|
-
end
|
|
4485
|
-
parts << "<c:spPr>#{cs_sp}</c:spPr>"
|
|
4486
|
-
end
|
|
4487
|
-
parts << build_axis_txpr(nil, chart[:chart_font]) if chart[:chart_font]
|
|
4488
|
-
if (ps = chart[:print_settings])
|
|
4489
|
-
parts << "<c:printSettings>"
|
|
4490
|
-
if (hf = ps[:header_footer])
|
|
4491
|
-
parts << "<c:headerFooter>"
|
|
4492
|
-
parts << "<c:oddHeader>#{xml_escape(hf[:odd_header])}</c:oddHeader>" if hf[:odd_header]
|
|
4493
|
-
parts << "<c:oddFooter>#{xml_escape(hf[:odd_footer])}</c:oddFooter>" if hf[:odd_footer]
|
|
4494
|
-
parts << "<c:evenHeader>#{xml_escape(hf[:even_header])}</c:evenHeader>" if hf[:even_header]
|
|
4495
|
-
parts << "<c:evenFooter>#{xml_escape(hf[:even_footer])}</c:evenFooter>" if hf[:even_footer]
|
|
4496
|
-
parts << "<c:firstHeader>#{xml_escape(hf[:first_header])}</c:firstHeader>" if hf[:first_header]
|
|
4497
|
-
parts << "<c:firstFooter>#{xml_escape(hf[:first_footer])}</c:firstFooter>" if hf[:first_footer]
|
|
4498
|
-
parts << "</c:headerFooter>"
|
|
4499
|
-
end
|
|
4500
|
-
if (pm = ps[:page_margins])
|
|
4501
|
-
pm_attrs = %w[b l r t header footer].filter_map { |a| pm[a.to_sym] ? %( #{a}="#{pm[a.to_sym]}") : nil }.join
|
|
4502
|
-
parts << "<c:pageMargins#{pm_attrs}/>"
|
|
4503
|
-
end
|
|
4504
|
-
if (psu = ps[:page_setup])
|
|
4505
|
-
psu_parts = +""
|
|
4506
|
-
psu_parts << %( paperSize="#{psu[:paper_size]}") if psu[:paper_size]
|
|
4507
|
-
psu_parts << %( firstPageNumber="#{psu[:first_page_number]}") if psu[:first_page_number]
|
|
4508
|
-
psu_parts << %( orientation="#{xml_escape(psu[:orientation])}") if psu[:orientation]
|
|
4509
|
-
psu_parts << %( horizontalDpi="#{psu[:horizontal_dpi]}") if psu[:horizontal_dpi]
|
|
4510
|
-
psu_parts << %( verticalDpi="#{psu[:vertical_dpi]}") if psu[:vertical_dpi]
|
|
4511
|
-
psu_parts << %( copies="#{psu[:copies]}") if psu[:copies]
|
|
4512
|
-
parts << "<c:pageSetup#{psu_parts}/>"
|
|
4513
|
-
end
|
|
4514
|
-
parts << "</c:printSettings>"
|
|
4515
|
-
end
|
|
4516
|
-
parts << "</c:chartSpace>"
|
|
4517
|
-
parts.join
|
|
4518
|
-
end
|
|
4519
|
-
|
|
4520
|
-
# : (untyped sheet_comments) -> untyped
|
|
4521
|
-
def generate_comments_xml(sheet_comments)
|
|
4522
|
-
authors = sheet_comments.map { |c| c[:author] }.uniq
|
|
4523
|
-
parts = [
|
|
4524
|
-
XML_HEADER,
|
|
4525
|
-
%(<comments xmlns="#{SSML_NS}">),
|
|
4526
|
-
"<authors>"
|
|
4527
|
-
]
|
|
4528
|
-
authors.each { |a| parts << "<author>#{xml_escape(a)}</author>" }
|
|
4529
|
-
parts << "</authors><commentList>"
|
|
4530
|
-
sheet_comments.each do |c|
|
|
4531
|
-
aid = authors.index(c[:author]) || 0
|
|
4532
|
-
text_xml = if c[:text].is_a?(Xlsxrb::Elements::RichText)
|
|
4533
|
-
rich_text_xml(c[:text])
|
|
4534
|
-
else
|
|
4535
|
-
"<r><t>#{xml_escape(c[:text])}</t></r>"
|
|
4536
|
-
end
|
|
4537
|
-
comment_attrs = %(ref="#{c[:ref]}" authorId="#{aid}")
|
|
4538
|
-
comment_attrs << %( guid="#{c[:guid]}") if c[:guid]
|
|
4539
|
-
comment_attrs << %( shapeId="#{c[:shape_id]}") if c[:shape_id]
|
|
4540
|
-
parts << "<comment #{comment_attrs}><text>#{text_xml}</text></comment>"
|
|
4541
|
-
end
|
|
4542
|
-
parts << "</commentList></comments>"
|
|
4543
|
-
parts.join
|
|
4544
|
-
end
|
|
4545
|
-
|
|
4546
|
-
# : (untyped sheet_comments) -> untyped
|
|
4547
|
-
def generate_vml_drawing_xml(sheet_comments)
|
|
4548
|
-
parts = [
|
|
4549
|
-
'<xml xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel">',
|
|
4550
|
-
'<o:shapelayout v:ext="edit"><o:idmap v:ext="edit" data="1"/></o:shapelayout>',
|
|
4551
|
-
'<v:shapetype id="_x0000_t202" coordsize="21600,21600" o:spt="202" path="m,l,21600r21600,l21600,xe">',
|
|
4552
|
-
'<v:stroke joinstyle="miter"/><v:path gradientshapeok="t" o:connecttype="rect"/>',
|
|
4553
|
-
"</v:shapetype>"
|
|
4554
|
-
]
|
|
4555
|
-
sheet_comments.each_with_index do |c, idx|
|
|
4556
|
-
col, row = cell_to_col_row(c[:ref])
|
|
4557
|
-
shape_id = 1025 + idx
|
|
4558
|
-
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">)
|
|
4559
|
-
parts << '<v:fill color2="#ffffe1"/>'
|
|
4560
|
-
parts << '<v:shadow on="t" color="black" obscured="t"/>'
|
|
4561
|
-
parts << '<v:path o:connecttype="none"/>'
|
|
4562
|
-
parts << '<v:textbox style="mso-direction-alt:auto"><div style="text-align:left"></div></v:textbox>'
|
|
4563
|
-
parts << '<x:ClientData ObjectType="Note">'
|
|
4564
|
-
parts << "<x:MoveWithCells/><x:SizeWithCells/>"
|
|
4565
|
-
parts << "<x:Anchor>#{col + 1}, 15, #{row}, 10, #{col + 3}, 15, #{row + 4}, 4</x:Anchor>"
|
|
4566
|
-
parts << "<x:AutoFill>False</x:AutoFill>"
|
|
4567
|
-
parts << "<x:Row>#{row}</x:Row>"
|
|
4568
|
-
parts << "<x:Column>#{col}</x:Column>"
|
|
4569
|
-
parts << "</x:ClientData></v:shape>"
|
|
4570
|
-
end
|
|
4571
|
-
parts << "</xml>"
|
|
4572
|
-
parts.join
|
|
4573
|
-
end
|
|
4574
|
-
|
|
4575
|
-
# : (untyped cell_ref) -> (::Array[0] | ::Array[untyped])
|
|
4576
|
-
def cell_to_col_row(cell_ref)
|
|
4577
|
-
m = cell_ref.match(/\A([A-Z]+)(\d+)\z/)
|
|
4578
|
-
return [0, 0] unless m
|
|
4579
|
-
|
|
4580
|
-
col = column_letter_to_index(m[1]) - 1
|
|
4581
|
-
row = m[2].to_i - 1
|
|
4582
|
-
[col, row]
|
|
4583
|
-
end
|
|
4584
|
-
|
|
4585
|
-
# : (untyped pivot_table, untyped cache_id) -> untyped
|
|
4586
|
-
def generate_pivot_table_xml(pivot_table, cache_id)
|
|
4587
|
-
data_caption = pivot_table[:data_caption] || (pivot_table[:data_fields].first ? pivot_table[:data_fields].first[:name] : "Values")
|
|
4588
|
-
pt_attrs = %( xmlns="#{SSML_NS}" name="#{xml_escape(pivot_table[:name])}" cacheId="#{cache_id}" dataCaption="#{xml_escape(data_caption)}")
|
|
4589
|
-
pt_attrs << %( grandTotalCaption="#{xml_escape(pivot_table[:grand_total_caption])}") if pivot_table[:grand_total_caption]
|
|
4590
|
-
pt_attrs << %( errorCaption="#{xml_escape(pivot_table[:error_caption])}") if pivot_table[:error_caption]
|
|
4591
|
-
pt_attrs << ' showError="1"' if pivot_table[:show_error]
|
|
4592
|
-
pt_attrs << %( missingCaption="#{xml_escape(pivot_table[:missing_caption])}") if pivot_table[:missing_caption]
|
|
4593
|
-
pt_attrs << ' showMissing="0"' if pivot_table[:show_missing] == false
|
|
4594
|
-
pt_attrs << %( tag="#{xml_escape(pivot_table[:tag])}") if pivot_table[:tag]
|
|
4595
|
-
pt_attrs << %( dataOnRows="1") if pivot_table[:data_on_rows]
|
|
4596
|
-
pt_attrs << %( dataOnRows="0") unless pivot_table[:data_on_rows]
|
|
4597
|
-
pt_attrs << %( rowGrandTotals="0") if pivot_table[:row_grand_totals] == false
|
|
4598
|
-
pt_attrs << %( colGrandTotals="0") if pivot_table[:col_grand_totals] == false
|
|
4599
|
-
pt_attrs << %( compact="0") if pivot_table[:compact] == false
|
|
4600
|
-
pt_attrs << %( outline="0") if pivot_table[:outline] == false
|
|
4601
|
-
pt_attrs << %( outlineData="1") if pivot_table[:outline_data]
|
|
4602
|
-
pt_attrs << %( compactData="0") if pivot_table[:compact_data] == false
|
|
4603
|
-
pt_attrs << %( showHeaders="0") if pivot_table[:show_headers] == false
|
|
4604
|
-
pt_attrs << %( showMultipleLabel="0") if pivot_table[:show_multiple_label] == false
|
|
4605
|
-
pt_attrs << %( showDataDropDown="0") if pivot_table[:show_data_drop_down] == false
|
|
4606
|
-
pt_attrs << %( indent="#{pivot_table[:indent]}") if pivot_table[:indent]
|
|
4607
|
-
pt_attrs << ' published="1"' if pivot_table[:published]
|
|
4608
|
-
pt_attrs << ' editData="1"' if pivot_table[:edit_data]
|
|
4609
|
-
pt_attrs << ' disableFieldList="1"' if pivot_table[:disable_field_list]
|
|
4610
|
-
pt_attrs << ' visualTotals="0"' if pivot_table[:visual_totals] == false
|
|
4611
|
-
pt_attrs << ' printDrill="1"' if pivot_table[:print_drill]
|
|
4612
|
-
pt_attrs << %( createdVersion="#{pivot_table[:created_version]}") if pivot_table[:created_version]
|
|
4613
|
-
pt_attrs << %( updatedVersion="#{pivot_table[:updated_version]}") if pivot_table[:updated_version]
|
|
4614
|
-
pt_attrs << %( minRefreshableVersion="#{pivot_table[:min_refreshable_version]}") if pivot_table[:min_refreshable_version]
|
|
4615
|
-
anf = pivot_table.fetch(:apply_number_formats, false)
|
|
4616
|
-
abf = pivot_table.fetch(:apply_border_formats, false)
|
|
4617
|
-
aff = pivot_table.fetch(:apply_font_formats, false)
|
|
4618
|
-
apf = pivot_table.fetch(:apply_pattern_formats, false)
|
|
4619
|
-
aaf = pivot_table.fetch(:apply_alignment_formats, false)
|
|
4620
|
-
awf = pivot_table.fetch(:apply_width_height_formats, true)
|
|
4621
|
-
pt_attrs << %( applyNumberFormats="#{anf ? 1 : 0}")
|
|
4622
|
-
pt_attrs << %( applyBorderFormats="#{abf ? 1 : 0}")
|
|
4623
|
-
pt_attrs << %( applyFontFormats="#{aff ? 1 : 0}")
|
|
4624
|
-
pt_attrs << %( applyPatternFormats="#{apf ? 1 : 0}")
|
|
4625
|
-
pt_attrs << %( applyAlignmentFormats="#{aaf ? 1 : 0}")
|
|
4626
|
-
pt_attrs << %( applyWidthHeightFormats="#{awf ? 1 : 0}")
|
|
4627
|
-
pt_attrs << ' multipleFieldFilters="0"' if pivot_table[:multiple_field_filters] == false
|
|
4628
|
-
pt_attrs << ' showDrill="0"' if pivot_table[:show_drill] == false
|
|
4629
|
-
pt_attrs << ' showDataTips="0"' if pivot_table[:show_data_tips] == false
|
|
4630
|
-
pt_attrs << ' enableDrill="0"' if pivot_table[:enable_drill] == false
|
|
4631
|
-
pt_attrs << ' showMemberPropertyTips="0"' if pivot_table[:show_member_property_tips] == false
|
|
4632
|
-
pt_attrs << ' itemPrintTitles="1"' if pivot_table[:item_print_titles]
|
|
4633
|
-
pt_attrs << ' fieldPrintTitles="1"' if pivot_table[:field_print_titles]
|
|
4634
|
-
pt_attrs << ' preserveFormatting="0"' if pivot_table[:preserve_formatting] == false
|
|
4635
|
-
pt_attrs << ' pageOverThenDown="1"' if pivot_table[:page_over_then_down]
|
|
4636
|
-
pt_attrs << %( pageWrap="#{pivot_table[:page_wrap]}") if pivot_table[:page_wrap]
|
|
4637
|
-
parts = [
|
|
4638
|
-
XML_HEADER,
|
|
4639
|
-
"<pivotTableDefinition#{pt_attrs}>"
|
|
4640
|
-
]
|
|
4641
|
-
|
|
4642
|
-
# Compute field count from source range or explicit field_names.
|
|
4643
|
-
field_count = if pivot_table[:field_names]
|
|
4644
|
-
pivot_table[:field_names].size
|
|
4645
|
-
else
|
|
4646
|
-
(pivot_table[:row_fields].size + pivot_table[:col_fields].size + pivot_table[:data_fields].size).clamp(1, 100)
|
|
4647
|
-
end
|
|
4648
|
-
loc_attrs = %(<location ref="#{pivot_table[:dest_ref]}" firstHeaderRow="1" firstDataRow="1" firstDataCol="1")
|
|
4649
|
-
loc_attrs << %( rowPageCount="#{pivot_table[:row_page_count]}") if pivot_table[:row_page_count]
|
|
4650
|
-
loc_attrs << %( colPageCount="#{pivot_table[:col_page_count]}") if pivot_table[:col_page_count]
|
|
4651
|
-
loc_attrs << "/>"
|
|
4652
|
-
parts << loc_attrs
|
|
4653
|
-
parts << %(<pivotFields count="#{field_count}">)
|
|
4654
|
-
field_count.times do |fi|
|
|
4655
|
-
attrs = +""
|
|
4656
|
-
if pivot_table[:row_fields].include?(fi)
|
|
4657
|
-
attrs << ' axis="axisRow"'
|
|
4658
|
-
elsif pivot_table[:col_fields].include?(fi)
|
|
4659
|
-
attrs << ' axis="axisCol"'
|
|
4660
|
-
end
|
|
4661
|
-
attrs << ' dataField="1"' if pivot_table[:data_fields].any? { |df| df[:fld] == fi }
|
|
4662
|
-
fa = pivot_table[:field_attrs] && pivot_table[:field_attrs][fi]
|
|
4663
|
-
attrs << %( compact="#{fa[:compact] ? "1" : "0"}") if fa && !fa[:compact].nil?
|
|
4664
|
-
attrs << %( outline="#{fa[:outline] ? "1" : "0"}") if fa && !fa[:outline].nil?
|
|
4665
|
-
attrs << %( subtotalTop="#{fa[:subtotal_top] ? "1" : "0"}") if fa && !fa[:subtotal_top].nil?
|
|
4666
|
-
attrs << %( showAll="#{fa && fa[:show_all] == true ? "1" : "0"}")
|
|
4667
|
-
attrs << %( numFmtId="#{fa[:num_fmt_id]}") if fa && fa[:num_fmt_id]
|
|
4668
|
-
attrs << %( sortType="#{xml_escape(fa[:sort_type])}") if fa && fa[:sort_type]
|
|
4669
|
-
attrs << ' defaultSubtotal="0"' if fa && fa[:default_subtotal] == false
|
|
4670
|
-
attrs << ' insertBlankRow="1"' if fa && fa[:insert_blank_row]
|
|
4671
|
-
attrs << ' insertPageBreak="1"' if fa && fa[:insert_page_break]
|
|
4672
|
-
attrs << ' includeNewItemsInFilter="1"' if fa && fa[:include_new_items_in_filter]
|
|
4673
|
-
|
|
4674
|
-
field_items = pivot_table[:items] && pivot_table[:items][fi]
|
|
4675
|
-
if field_items
|
|
4676
|
-
parts << "<pivotField#{attrs}>"
|
|
4677
|
-
parts << %(<items count="#{field_items.size + 1}">)
|
|
4678
|
-
field_items.size.times { |ix| parts << %(<item x="#{ix}"/>) }
|
|
4679
|
-
parts << '<item t="default"/>'
|
|
4680
|
-
parts << "</items>"
|
|
4681
|
-
parts << "</pivotField>"
|
|
4682
|
-
else
|
|
4683
|
-
parts << "<pivotField#{attrs}/>"
|
|
4684
|
-
end
|
|
4685
|
-
end
|
|
4686
|
-
parts << "</pivotFields>"
|
|
4687
|
-
|
|
4688
|
-
unless pivot_table[:row_fields].empty?
|
|
4689
|
-
parts << %(<rowFields count="#{pivot_table[:row_fields].size}">)
|
|
4690
|
-
pivot_table[:row_fields].each { |f| parts << %(<field x="#{f}"/>) }
|
|
4691
|
-
parts << "</rowFields>"
|
|
4692
|
-
end
|
|
4693
|
-
|
|
4694
|
-
unless pivot_table[:col_fields].empty?
|
|
4695
|
-
parts << %(<colFields count="#{pivot_table[:col_fields].size}">)
|
|
4696
|
-
pivot_table[:col_fields].each { |f| parts << %(<field x="#{f}"/>) }
|
|
4697
|
-
parts << "</colFields>"
|
|
4698
|
-
end
|
|
4699
|
-
|
|
4700
|
-
unless pivot_table[:data_fields].empty?
|
|
4701
|
-
parts << %(<dataFields count="#{pivot_table[:data_fields].size}">)
|
|
4702
|
-
pivot_table[:data_fields].each do |df|
|
|
4703
|
-
df_attrs = %( name="#{xml_escape(df[:name])}" fld="#{df[:fld]}" subtotal="#{df[:subtotal] || "sum"}")
|
|
4704
|
-
df_attrs << %( showDataAs="#{xml_escape(df[:show_data_as])}") if df[:show_data_as]
|
|
4705
|
-
df_attrs << %( baseField="#{df[:base_field]}") if df[:base_field]
|
|
4706
|
-
df_attrs << %( baseItem="#{df[:base_item]}") if df[:base_item]
|
|
4707
|
-
df_attrs << %( numFmtId="#{df[:num_fmt_id]}") if df[:num_fmt_id]
|
|
4708
|
-
parts << "<dataField#{df_attrs}/>"
|
|
4709
|
-
end
|
|
4710
|
-
parts << "</dataFields>"
|
|
4711
|
-
end
|
|
4712
|
-
|
|
4713
|
-
if pivot_table[:pivot_table_style]
|
|
4714
|
-
psi = pivot_table[:pivot_table_style]
|
|
4715
|
-
psi_attrs = +""
|
|
4716
|
-
psi_attrs << %( name="#{xml_escape(psi[:name])}") if psi[:name]
|
|
4717
|
-
psi_attrs << %( showRowHeaders="#{psi[:show_row_headers] ? "1" : "0"}") unless psi[:show_row_headers].nil?
|
|
4718
|
-
psi_attrs << %( showColHeaders="#{psi[:show_col_headers] ? "1" : "0"}") unless psi[:show_col_headers].nil?
|
|
4719
|
-
psi_attrs << %( showRowStripes="#{psi[:show_row_stripes] ? "1" : "0"}") unless psi[:show_row_stripes].nil?
|
|
4720
|
-
psi_attrs << %( showColStripes="#{psi[:show_col_stripes] ? "1" : "0"}") unless psi[:show_col_stripes].nil?
|
|
4721
|
-
psi_attrs << %( showLastColumn="#{psi[:show_last_column] ? "1" : "0"}") unless psi[:show_last_column].nil?
|
|
4722
|
-
parts << "<pivotTableStyleInfo#{psi_attrs}/>"
|
|
4723
|
-
end
|
|
4724
|
-
|
|
4725
|
-
parts << "</pivotTableDefinition>"
|
|
4726
|
-
parts.join
|
|
4727
|
-
end
|
|
4728
|
-
|
|
4729
|
-
# : (untyped pivot_table, untyped _cache_id) -> untyped
|
|
4730
|
-
def generate_pivot_cache_definition_xml(pivot_table, _cache_id)
|
|
4731
|
-
pcd_attrs = %( xmlns="#{SSML_NS}" xmlns:r="#{DOC_REL_NS}" r:id="rId1" refreshOnLoad="1")
|
|
4732
|
-
pcd_attrs << ' saveData="0"' if pivot_table[:cache_save_data] == false
|
|
4733
|
-
pcd_attrs << ' enableRefresh="0"' if pivot_table[:cache_enable_refresh] == false
|
|
4734
|
-
pcd_attrs << %( refreshedBy="#{xml_escape(pivot_table[:cache_refreshed_by])}") if pivot_table[:cache_refreshed_by]
|
|
4735
|
-
pcd_attrs << %( refreshedVersion="#{pivot_table[:cache_refreshed_version]}") if pivot_table[:cache_refreshed_version]
|
|
4736
|
-
pcd_attrs << %( createdVersion="#{pivot_table[:cache_created_version]}") if pivot_table[:cache_created_version]
|
|
4737
|
-
pcd_attrs << %( recordCount="#{pivot_table[:cache_record_count]}") if pivot_table[:cache_record_count]
|
|
4738
|
-
pcd_attrs << ' optimizeMemory="1"' if pivot_table[:cache_optimize_memory]
|
|
4739
|
-
parts = [
|
|
4740
|
-
XML_HEADER,
|
|
4741
|
-
"<pivotCacheDefinition#{pcd_attrs}>"
|
|
4742
|
-
]
|
|
4743
|
-
|
|
4744
|
-
# Parse source ref: "Sheet1!A1:C4" => sheet name + range.
|
|
4745
|
-
source = pivot_table[:source_ref]
|
|
4746
|
-
ws_name_attr = pivot_table[:source_name] ? %( name="#{xml_escape(pivot_table[:source_name])}") : ""
|
|
4747
|
-
if source.include?("!")
|
|
4748
|
-
sname, srange = source.split("!", 2)
|
|
4749
|
-
sname = sname.delete("'")
|
|
4750
|
-
parts << %(<cacheSource type="worksheet"><worksheetSource ref="#{srange}" sheet="#{xml_escape(sname)}"#{ws_name_attr}/></cacheSource>)
|
|
4751
|
-
else
|
|
4752
|
-
parts << %(<cacheSource type="worksheet"><worksheetSource ref="#{source}"#{ws_name_attr}/></cacheSource>)
|
|
4753
|
-
end
|
|
4754
|
-
|
|
4755
|
-
field_count = pivot_table[:field_names] ? pivot_table[:field_names].size : (pivot_table[:row_fields].size + pivot_table[:col_fields].size + pivot_table[:data_fields].size)
|
|
4756
|
-
parts << %(<cacheFields count="#{field_count}">)
|
|
4757
|
-
field_count.times do |fi|
|
|
4758
|
-
fname = if pivot_table[:field_names] && pivot_table[:field_names][fi]
|
|
4759
|
-
pivot_table[:field_names][fi]
|
|
4760
|
-
else
|
|
4761
|
-
df = pivot_table[:data_fields].find { |d| d[:fld] == fi }
|
|
4762
|
-
df ? df[:name] : "Field#{fi + 1}"
|
|
4763
|
-
end
|
|
4764
|
-
fa = pivot_table[:field_attrs] && pivot_table[:field_attrs][fi]
|
|
4765
|
-
cf_num_fmt = (fa && fa[:cache_num_fmt_id]) || 0
|
|
4766
|
-
cf_attrs = %( name="#{xml_escape(fname)}" numFmtId="#{cf_num_fmt}")
|
|
4767
|
-
cf_attrs << %( caption="#{xml_escape(fa[:cache_caption])}") if fa && fa[:cache_caption]
|
|
4768
|
-
cf_attrs << %( formula="#{xml_escape(fa[:cache_formula])}") if fa && fa[:cache_formula]
|
|
4769
|
-
field_items = pivot_table[:items] && pivot_table[:items][fi]
|
|
4770
|
-
if field_items
|
|
4771
|
-
parts << "<cacheField#{cf_attrs}>"
|
|
4772
|
-
parts << %(<sharedItems count="#{field_items.size}">)
|
|
4773
|
-
field_items.each { |v| parts << %(<s v="#{xml_escape(v.to_s)}"/>) }
|
|
4774
|
-
parts << "</sharedItems>"
|
|
4775
|
-
parts << "</cacheField>"
|
|
4776
|
-
else
|
|
4777
|
-
parts << "<cacheField#{cf_attrs}><sharedItems/></cacheField>"
|
|
4778
|
-
end
|
|
4779
|
-
end
|
|
4780
|
-
parts << "</cacheFields>"
|
|
4781
|
-
parts << "</pivotCacheDefinition>"
|
|
4782
|
-
parts.join
|
|
4783
|
-
end
|
|
4784
|
-
|
|
4785
|
-
# : (untyped pivot_table) -> untyped
|
|
4786
|
-
def generate_pivot_cache_records_xml(pivot_table)
|
|
4787
|
-
items = pivot_table[:items]
|
|
4788
|
-
if items&.values&.any? { |v| v && !v.empty? }
|
|
4789
|
-
max_len = items.values.map { |v| v ? v.size : 0 }.max
|
|
4790
|
-
parts = [XML_HEADER, %(<pivotCacheRecords xmlns="#{SSML_NS}" count="#{max_len}">)]
|
|
4791
|
-
max_len.times do |ri|
|
|
4792
|
-
parts << "<r>"
|
|
4793
|
-
field_count = pivot_table[:field_names] ? pivot_table[:field_names].size : (pivot_table[:row_fields].size + pivot_table[:col_fields].size + pivot_table[:data_fields].size)
|
|
4794
|
-
field_count.times do |fi|
|
|
4795
|
-
field_items = items[fi]
|
|
4796
|
-
parts << if field_items
|
|
4797
|
-
%(<x v="#{ri < field_items.size ? ri : 0}"/>)
|
|
4798
|
-
else
|
|
4799
|
-
%(<n v="0"/>)
|
|
4800
|
-
end
|
|
4801
|
-
end
|
|
4802
|
-
parts << "</r>"
|
|
4803
|
-
end
|
|
4804
|
-
parts << "</pivotCacheRecords>"
|
|
4805
|
-
parts.join
|
|
4806
|
-
else
|
|
4807
|
-
[XML_HEADER, %(<pivotCacheRecords xmlns="#{SSML_NS}" count="0"/>)].join
|
|
4808
|
-
end
|
|
4809
|
-
end
|
|
4810
|
-
|
|
4811
|
-
# : (untyped cache_id) -> untyped
|
|
4812
|
-
def generate_pivot_cache_rels(cache_id)
|
|
4813
|
-
[
|
|
4814
|
-
XML_HEADER,
|
|
4815
|
-
%(<Relationships xmlns="#{REL_NS}">),
|
|
4816
|
-
%(<Relationship Id="rId1" Type="#{DOC_REL_NS}/pivotCacheRecords" Target="pivotCacheRecords#{cache_id}.xml"/>),
|
|
4817
|
-
"</Relationships>"
|
|
4818
|
-
].join
|
|
4819
|
-
end
|
|
4820
|
-
|
|
4821
|
-
# : (untyped cache_id) -> untyped
|
|
4822
|
-
def generate_pivot_table_rels(cache_id)
|
|
4823
|
-
[
|
|
4824
|
-
XML_HEADER,
|
|
4825
|
-
%(<Relationships xmlns="#{REL_NS}">),
|
|
4826
|
-
%(<Relationship Id="rId1" Type="#{DOC_REL_NS}/pivotCacheDefinition" Target="../pivotCache/pivotCacheDefinition#{cache_id}.xml"/>),
|
|
4827
|
-
"</Relationships>"
|
|
4828
|
-
].join
|
|
4829
|
-
end
|
|
4830
|
-
|
|
4831
|
-
# : (untyped ext_link) -> untyped
|
|
4832
|
-
def generate_external_link_xml(ext_link)
|
|
4833
|
-
parts = [
|
|
4834
|
-
XML_HEADER,
|
|
4835
|
-
%(<externalLink xmlns="#{SSML_NS}" xmlns:r="#{DOC_REL_NS}">),
|
|
4836
|
-
'<externalBook r:id="rId1">'
|
|
4837
|
-
]
|
|
4838
|
-
unless ext_link[:sheet_names].empty?
|
|
4839
|
-
parts << "<sheetNames>"
|
|
4840
|
-
ext_link[:sheet_names].each { |sn| parts << %(<sheetName val="#{xml_escape(sn)}"/>) }
|
|
4841
|
-
parts << "</sheetNames>"
|
|
4842
|
-
end
|
|
4843
|
-
parts << "</externalBook>"
|
|
4844
|
-
parts << "</externalLink>"
|
|
4845
|
-
parts.join
|
|
4846
|
-
end
|
|
4847
|
-
|
|
4848
|
-
# : (untyped ext_link) -> untyped
|
|
4849
|
-
def generate_external_link_rels(ext_link)
|
|
4850
|
-
[
|
|
4851
|
-
XML_HEADER,
|
|
4852
|
-
%(<Relationships xmlns="#{REL_NS}">),
|
|
4853
|
-
%(<Relationship Id="rId1" Type="#{DOC_REL_NS}/externalLinkPath" Target="#{xml_escape(ext_link[:target])}" TargetMode="External"/>),
|
|
4854
|
-
"</Relationships>"
|
|
4855
|
-
].join
|
|
4856
|
-
end
|
|
4857
|
-
|
|
4858
|
-
# : (untyped ct_xml) -> untyped
|
|
4859
|
-
def parse_extra_content_types(ct_xml)
|
|
4860
|
-
ct_xml.scan(/<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"/).each do |ext, ct|
|
|
4861
|
-
@extra_ct_defaults[ext] ||= ct
|
|
4862
|
-
end
|
|
4863
|
-
ct_xml.scan(/<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"/).each do |pn, ct|
|
|
4864
|
-
@extra_ct_overrides[pn] ||= ct
|
|
4865
|
-
end
|
|
4866
|
-
end
|
|
4867
|
-
|
|
4868
|
-
# : (untyped element, untyped opts) -> ("" | ::String)
|
|
4869
|
-
def build_line_end_xml(element, opts)
|
|
4870
|
-
return "" unless opts
|
|
4871
|
-
|
|
4872
|
-
attrs = +""
|
|
4873
|
-
attrs << %( type="#{xml_escape(opts[:type])}") if opts[:type]
|
|
4874
|
-
attrs << %( w="#{xml_escape(opts[:w])}") if opts[:w]
|
|
4875
|
-
attrs << %( len="#{xml_escape(opts[:len])}") if opts[:len]
|
|
4876
|
-
"<a:#{element}#{attrs}/>"
|
|
4877
|
-
end
|
|
4878
|
-
|
|
4879
|
-
# : (untyped value) -> untyped
|
|
4880
|
-
def xml_escape(value)
|
|
4881
|
-
value.to_s
|
|
4882
|
-
.gsub(/[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/, "")
|
|
4883
|
-
.gsub("&", "&")
|
|
4884
|
-
.gsub("<", "<")
|
|
4885
|
-
.gsub(">", ">")
|
|
4886
|
-
.gsub('"', """)
|
|
4887
|
-
.gsub("'", "'")
|
|
4888
|
-
end
|
|
4889
|
-
|
|
4890
|
-
# : (untyped rich_text) -> untyped
|
|
4891
|
-
def rich_text_xml(rich_text)
|
|
4892
|
-
rich_text.runs.map do |run|
|
|
4893
|
-
font = run[:font]
|
|
4894
|
-
if font && !font.empty?
|
|
4895
|
-
rpr = +""
|
|
4896
|
-
rpr << "<b/>" if font[:bold]
|
|
4897
|
-
rpr << "<i/>" if font[:italic]
|
|
4898
|
-
rpr << "<strike/>" if font[:strike]
|
|
4899
|
-
if font[:underline]
|
|
4900
|
-
rpr << if font[:underline] == true
|
|
4901
|
-
"<u/>"
|
|
4902
|
-
else
|
|
4903
|
-
%(<u val="#{font[:underline]}"/>)
|
|
4904
|
-
end
|
|
4905
|
-
end
|
|
4906
|
-
rpr << %(<vertAlign val="#{font[:vert_align]}"/>) if font[:vert_align]
|
|
4907
|
-
rpr << %(<sz val="#{font[:sz]}"/>) if font[:sz]
|
|
4908
|
-
rpr << emit_color_xml(font)
|
|
4909
|
-
rpr << %(<rFont val="#{xml_escape(font[:name])}"/>) if font[:name]
|
|
4910
|
-
rpr << %(<family val="#{font[:family]}"/>) if font[:family]
|
|
4911
|
-
rpr << %(<scheme val="#{font[:scheme]}"/>) if font[:scheme]
|
|
4912
|
-
"<r><rPr>#{rpr}</rPr><t>#{xml_escape(run[:text])}</t></r>"
|
|
4913
|
-
else
|
|
4914
|
-
"<r><t>#{xml_escape(run[:text])}</t></r>"
|
|
4915
|
-
end
|
|
4916
|
-
end.join
|
|
4917
|
-
end
|
|
4918
|
-
|
|
4919
|
-
# : (untyped cell_ref, untyped value, untyped style_idx, ?untyped? sst, ?ph: untyped?) -> untyped
|
|
4920
|
-
def cell_xml(cell_ref, value, style_idx, sst = nil, ph: nil) # rubocop:disable Naming/MethodParameterName
|
|
4921
|
-
s_attr = style_idx ? %( s="#{style_idx}") : ""
|
|
4922
|
-
ph_attr = ph ? ' ph="1"' : ""
|
|
4923
|
-
case value
|
|
4924
|
-
when Xlsxrb::Elements::CellError
|
|
4925
|
-
%(<c r="#{cell_ref}" t="e"#{s_attr}#{ph_attr}><v>#{xml_escape(value.code)}</v></c>)
|
|
4926
|
-
when Xlsxrb::Elements::Formula
|
|
4927
|
-
f_attrs = +""
|
|
4928
|
-
case value.type
|
|
4929
|
-
when :shared
|
|
4930
|
-
f_attrs << %( t="shared" si="#{value.shared_index}")
|
|
4931
|
-
f_attrs << %( ref="#{value.ref}") if value.ref
|
|
4932
|
-
when :array
|
|
4933
|
-
f_attrs << %( t="array" ref="#{value.ref}") if value.ref
|
|
4934
|
-
when :data_table
|
|
4935
|
-
f_attrs << ' t="dataTable"'
|
|
4936
|
-
f_attrs << ' dt2D="1"' if value.dt2d
|
|
4937
|
-
f_attrs << ' dtr="1"' if value.dtr
|
|
4938
|
-
f_attrs << %( r1="#{value.r1}") if value.r1
|
|
4939
|
-
f_attrs << %( r2="#{value.r2}") if value.r2
|
|
4940
|
-
end
|
|
4941
|
-
f_attrs << ' ca="1"' if value.calculate_always
|
|
4942
|
-
f_attrs << ' aca="1"' if value.aca
|
|
4943
|
-
f_attrs << ' bx="1"' if value.bx
|
|
4944
|
-
t_attr = ""
|
|
4945
|
-
cached_val_str = value.cached_value.to_s
|
|
4946
|
-
case value.cached_value
|
|
4947
|
-
when String
|
|
4948
|
-
t_attr = ' t="str"'
|
|
4949
|
-
when true
|
|
4950
|
-
t_attr = ' t="b"'
|
|
4951
|
-
cached_val_str = "1"
|
|
4952
|
-
when false
|
|
4953
|
-
t_attr = ' t="b"'
|
|
4954
|
-
cached_val_str = "0"
|
|
4955
|
-
end
|
|
4956
|
-
parts = %(<c r="#{cell_ref}"#{t_attr}#{s_attr}#{ph_attr}><f#{f_attrs}>#{xml_escape(value.expression)}</f>)
|
|
4957
|
-
parts << "<v>#{xml_escape(cached_val_str)}</v>" unless value.cached_value.nil?
|
|
4958
|
-
parts << "</c>"
|
|
4959
|
-
parts
|
|
4960
|
-
when Xlsxrb::Elements::RichText
|
|
4961
|
-
if sst
|
|
4962
|
-
rt_sst, = sst
|
|
4963
|
-
idx = rt_sst[value]
|
|
4964
|
-
%(<c r="#{cell_ref}" t="s"#{s_attr}#{ph_attr}><v>#{idx}</v></c>)
|
|
4965
|
-
else
|
|
4966
|
-
%(<c r="#{cell_ref}" t="inlineStr"#{s_attr}#{ph_attr}><is>#{rich_text_xml(value)}</is></c>)
|
|
4967
|
-
end
|
|
4968
|
-
when true, false
|
|
4969
|
-
%(<c r="#{cell_ref}" t="b"#{s_attr}#{ph_attr}><v>#{value ? 1 : 0}</v></c>)
|
|
4970
|
-
when Time
|
|
4971
|
-
serial = Xlsxrb::Ooxml::Utils.datetime_to_serial(value)
|
|
4972
|
-
dt_attr = s_attr.empty? && (dt_style = resolve_style_index(datetime_num_fmt_id)) ? %( s="#{dt_style}") : s_attr
|
|
4973
|
-
%(<c r="#{cell_ref}"#{dt_attr}#{ph_attr}><v>#{serial}</v></c>)
|
|
4974
|
-
when Date
|
|
4975
|
-
serial = Xlsxrb::Ooxml::Utils.date_to_serial(value)
|
|
4976
|
-
ds_attr = s_attr.empty? && (date_style = resolve_style_index(date_num_fmt_id)) ? %( s="#{date_style}") : s_attr
|
|
4977
|
-
%(<c r="#{cell_ref}"#{ds_attr}#{ph_attr}><v>#{serial}</v></c>)
|
|
4978
|
-
when Numeric
|
|
4979
|
-
%(<c r="#{cell_ref}"#{s_attr}#{ph_attr}><v>#{value}</v></c>)
|
|
4980
|
-
else
|
|
4981
|
-
if sst
|
|
4982
|
-
_, str_sst = sst
|
|
4983
|
-
idx = str_sst[value.to_s]
|
|
4984
|
-
%(<c r="#{cell_ref}" t="s"#{s_attr}#{ph_attr}><v>#{idx}</v></c>)
|
|
4985
|
-
else
|
|
4986
|
-
%(<c r="#{cell_ref}" t="inlineStr"#{s_attr}#{ph_attr}><is><t>#{xml_escape(value)}</t></is></c>)
|
|
4987
|
-
end
|
|
4988
|
-
end
|
|
4989
|
-
end
|
|
4990
|
-
|
|
4991
|
-
# Returns the numFmtId for dates, registering it on first use.
|
|
4992
|
-
# : () -> untyped
|
|
4993
|
-
def date_num_fmt_id
|
|
4994
|
-
@date_num_fmt_id ||= add_number_format(Xlsxrb::Ooxml::Utils::DEFAULT_DATE_FORMAT)
|
|
4995
|
-
end
|
|
4996
|
-
|
|
4997
|
-
# Returns the numFmtId for datetime, registering it on first use.
|
|
4998
|
-
# : () -> untyped
|
|
4999
|
-
def datetime_num_fmt_id
|
|
5000
|
-
@datetime_num_fmt_id ||= add_number_format(Xlsxrb::Ooxml::Utils::DEFAULT_DATETIME_FORMAT)
|
|
5001
|
-
end
|
|
5002
|
-
|
|
5003
|
-
# Maps a numFmtId to a cellXfs index. Index 0 is the default (no format).
|
|
5004
|
-
# : (untyped style_value) -> (nil | untyped)
|
|
5005
|
-
def resolve_style_index(style_value)
|
|
5006
|
-
return nil if style_value.nil?
|
|
5007
|
-
|
|
5008
|
-
# New-style: { xf_index: N } from set_cell_style.
|
|
5009
|
-
return style_value[:xf_index] if style_value.is_a?(Hash) && style_value.key?(:xf_index)
|
|
5010
|
-
|
|
5011
|
-
# Legacy: raw num_fmt_id from set_cell_format — find or create matching xf entry.
|
|
5012
|
-
num_fmt_id = style_value
|
|
5013
|
-
@xf_index_map ||= begin
|
|
5014
|
-
map = {}
|
|
5015
|
-
@num_fmts.each_with_index do |nf, _i|
|
|
5016
|
-
entry = { num_fmt_id: nf[:num_fmt_id], font_id: 0, fill_id: 0, border_id: 0 }
|
|
5017
|
-
idx = @xf_entries.index(entry)
|
|
5018
|
-
unless idx
|
|
5019
|
-
@xf_entries << entry
|
|
5020
|
-
idx = @xf_entries.size - 1
|
|
5021
|
-
end
|
|
5022
|
-
map[nf[:num_fmt_id]] = idx
|
|
5023
|
-
end
|
|
5024
|
-
map
|
|
5025
|
-
end
|
|
5026
|
-
@xf_index_map[num_fmt_id]
|
|
5027
|
-
end
|
|
5028
|
-
|
|
5029
|
-
# : (untyped filter) -> untyped
|
|
5030
|
-
def emit_filter_xml(filter)
|
|
5031
|
-
case filter[:type]
|
|
5032
|
-
when :filters
|
|
5033
|
-
attrs = filter[:blank] ? +' blank="1"' : +""
|
|
5034
|
-
attrs << %( calendarType="#{filter[:calendar_type]}") if filter[:calendar_type]
|
|
5035
|
-
has_values = filter[:values]&.any?
|
|
5036
|
-
has_date_groups = filter[:date_group_items]&.any?
|
|
5037
|
-
if has_values || has_date_groups
|
|
5038
|
-
parts = ["<filters#{attrs}>"]
|
|
5039
|
-
filter[:values]&.each { |v| parts << %(<filter val="#{xml_escape(v)}"/>) }
|
|
5040
|
-
filter[:date_group_items]&.each do |dg|
|
|
5041
|
-
dg_attrs = %(dateTimeGrouping="#{dg[:date_time_grouping]}")
|
|
5042
|
-
dg_attrs << %( year="#{dg[:year]}") if dg[:year]
|
|
5043
|
-
dg_attrs << %( month="#{dg[:month]}") if dg[:month]
|
|
5044
|
-
dg_attrs << %( day="#{dg[:day]}") if dg[:day]
|
|
5045
|
-
dg_attrs << %( hour="#{dg[:hour]}") if dg[:hour]
|
|
5046
|
-
dg_attrs << %( minute="#{dg[:minute]}") if dg[:minute]
|
|
5047
|
-
dg_attrs << %( second="#{dg[:second]}") if dg[:second]
|
|
5048
|
-
parts << "<dateGroupItem #{dg_attrs}/>"
|
|
5049
|
-
end
|
|
5050
|
-
parts << "</filters>"
|
|
5051
|
-
parts.join
|
|
5052
|
-
else
|
|
5053
|
-
"<filters#{attrs}/>"
|
|
5054
|
-
end
|
|
5055
|
-
when :custom
|
|
5056
|
-
if filter[:filters]
|
|
5057
|
-
and_attr = filter[:and] ? ' and="1"' : ""
|
|
5058
|
-
parts = ["<customFilters#{and_attr}>"]
|
|
5059
|
-
filter[:filters].each do |cf|
|
|
5060
|
-
parts << %(<customFilter operator="#{cf[:operator]}" val="#{xml_escape(cf[:val])}"/>)
|
|
5061
|
-
end
|
|
5062
|
-
parts << "</customFilters>"
|
|
5063
|
-
parts.join
|
|
5064
|
-
else
|
|
5065
|
-
%(<customFilters><customFilter operator="#{filter[:operator]}" val="#{xml_escape(filter[:val])}"/></customFilters>)
|
|
5066
|
-
end
|
|
5067
|
-
when :dynamic
|
|
5068
|
-
dyn_attrs = %( type="#{filter[:dynamic_type]}")
|
|
5069
|
-
dyn_attrs << %( val="#{filter[:val]}") if filter[:val]
|
|
5070
|
-
dyn_attrs << %( valIso="#{filter[:val_iso]}") if filter[:val_iso]
|
|
5071
|
-
dyn_attrs << %( maxVal="#{filter[:max_val]}") if filter[:max_val]
|
|
5072
|
-
dyn_attrs << %( maxValIso="#{filter[:max_val_iso]}") if filter[:max_val_iso]
|
|
5073
|
-
"<dynamicFilter#{dyn_attrs}/>"
|
|
5074
|
-
when :top10
|
|
5075
|
-
top_attr = filter[:top] ? ' top="1"' : ""
|
|
5076
|
-
pct_attr = filter[:percent] ? ' percent="1"' : ""
|
|
5077
|
-
fv_attr = filter[:filter_val] ? %( filterVal="#{filter[:filter_val]}") : ""
|
|
5078
|
-
%(<top10#{top_attr}#{pct_attr} val="#{filter[:val]}"#{fv_attr}/>)
|
|
5079
|
-
when :color_filter
|
|
5080
|
-
cf_attrs = %(dxfId="#{filter[:dxf_id]}")
|
|
5081
|
-
cf_attrs << ' cellColor="0"' if filter[:cell_color] == false
|
|
5082
|
-
%(<colorFilter #{cf_attrs}/>)
|
|
5083
|
-
when :icon_filter
|
|
5084
|
-
if_attrs = %(iconSet="#{filter[:icon_set]}")
|
|
5085
|
-
if_attrs << %( iconId="#{filter[:icon_id]}") if filter[:icon_id]
|
|
5086
|
-
%(<iconFilter #{if_attrs}/>)
|
|
5087
|
-
else
|
|
5088
|
-
""
|
|
5089
|
-
end
|
|
5090
|
-
end
|
|
5091
|
-
|
|
5092
|
-
# : (untyped parts, untyped rule) -> untyped
|
|
5093
|
-
def emit_cf_rule(parts, rule)
|
|
5094
|
-
type = rule[:type]
|
|
5095
|
-
if type.is_a?(String) || type.is_a?(Symbol)
|
|
5096
|
-
t_str = type.to_s
|
|
5097
|
-
snake = t_str.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
5098
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
5099
|
-
.downcase.to_sym
|
|
5100
|
-
type = snake if CF_TYPE_MAP.key?(snake)
|
|
5101
|
-
end
|
|
5102
|
-
rule_type = CF_TYPE_MAP[type] || type.to_s
|
|
5103
|
-
rule_attrs = %(type="#{rule_type}")
|
|
5104
|
-
rule_attrs << %( priority="#{rule[:priority]}") if rule[:priority]
|
|
5105
|
-
rule_attrs << %( operator="#{rule[:operator]}") if rule[:operator]
|
|
5106
|
-
rule_attrs << %( dxfId="#{rule[:format_id]}") if rule[:format_id]
|
|
5107
|
-
rule_attrs << %( stopIfTrue="1") if rule[:stop_if_true]
|
|
5108
|
-
rule_attrs << %( aboveAverage="0") if rule[:above_average] == false
|
|
5109
|
-
rule_attrs << %( equalAverage="1") if rule[:equal_average]
|
|
5110
|
-
rule_attrs << %( rank="#{rule[:rank]}") if rule[:rank]
|
|
5111
|
-
rule_attrs << %( percent="1") if rule[:percent]
|
|
5112
|
-
rule_attrs << %( bottom="1") if rule[:bottom]
|
|
5113
|
-
rule_attrs << %( text="#{xml_escape(rule[:text])}") if rule[:text]
|
|
5114
|
-
rule_attrs << %( timePeriod="#{rule[:time_period]}") if rule[:time_period]
|
|
5115
|
-
rule_attrs << %( stdDev="#{rule[:std_dev]}") if rule[:std_dev]
|
|
5116
|
-
|
|
5117
|
-
case type
|
|
5118
|
-
when :cell_is, :expression, :above_average, :top10, :duplicate_values, :unique_values,
|
|
5119
|
-
:contains_text, :not_contains_text, :begins_with, :ends_with,
|
|
5120
|
-
:contains_blanks, :not_contains_blanks, :time_period
|
|
5121
|
-
formulas = rule[:formulas] || [rule[:formula]].compact
|
|
5122
|
-
if formulas.empty?
|
|
5123
|
-
parts << "<cfRule #{rule_attrs}/>"
|
|
5124
|
-
else
|
|
5125
|
-
parts << "<cfRule #{rule_attrs}>"
|
|
5126
|
-
formulas.each { |f| parts << "<formula>#{xml_escape(f)}</formula>" }
|
|
5127
|
-
parts << "</cfRule>"
|
|
5128
|
-
end
|
|
5129
|
-
when :color_scale
|
|
5130
|
-
cs = rule[:color_scale]
|
|
5131
|
-
parts << "<cfRule #{rule_attrs}>"
|
|
5132
|
-
parts << "<colorScale>"
|
|
5133
|
-
cs[:cfvo]&.each do |cfvo|
|
|
5134
|
-
cfvo_attrs = %(type="#{cfvo[:type]}")
|
|
5135
|
-
cfvo_attrs << %( val="#{cfvo[:val]}") if cfvo[:val]
|
|
5136
|
-
cfvo_attrs << ' gte="0"' if cfvo[:gte] == false
|
|
5137
|
-
parts << "<cfvo #{cfvo_attrs}/>"
|
|
5138
|
-
end
|
|
5139
|
-
cs[:colors]&.each { |c| parts << emit_cf_color_xml(c) }
|
|
5140
|
-
parts << "</colorScale>"
|
|
5141
|
-
parts << "</cfRule>"
|
|
5142
|
-
when :data_bar
|
|
5143
|
-
db = rule[:data_bar]
|
|
5144
|
-
parts << "<cfRule #{rule_attrs}>"
|
|
5145
|
-
db_attrs = +""
|
|
5146
|
-
db_attrs << %( minLength="#{db[:min_length]}") if db[:min_length]
|
|
5147
|
-
db_attrs << %( maxLength="#{db[:max_length]}") if db[:max_length]
|
|
5148
|
-
db_attrs << %( showValue="#{db[:show_value] ? 1 : 0}") unless db[:show_value].nil?
|
|
5149
|
-
parts << "<dataBar#{db_attrs}>"
|
|
5150
|
-
db[:cfvo]&.each do |cfvo|
|
|
5151
|
-
cfvo_attrs = %(type="#{cfvo[:type]}")
|
|
5152
|
-
cfvo_attrs << %( val="#{cfvo[:val]}") if cfvo[:val]
|
|
5153
|
-
cfvo_attrs << ' gte="0"' if cfvo[:gte] == false
|
|
5154
|
-
parts << "<cfvo #{cfvo_attrs}/>"
|
|
5155
|
-
end
|
|
5156
|
-
parts << emit_cf_color_xml(db[:color]) if db[:color]
|
|
5157
|
-
parts << "</dataBar>"
|
|
5158
|
-
parts << "</cfRule>"
|
|
5159
|
-
when :icon_set
|
|
5160
|
-
is = rule[:icon_set]
|
|
5161
|
-
parts << "<cfRule #{rule_attrs}>"
|
|
5162
|
-
is_attrs = +""
|
|
5163
|
-
is_attrs << %( iconSet="#{is[:icon_set]}") if is[:icon_set]
|
|
5164
|
-
is_attrs << %( reverse="#{is[:reverse] ? 1 : 0}") unless is[:reverse].nil?
|
|
5165
|
-
is_attrs << %( percent="#{is[:percent] ? 1 : 0}") unless is[:percent].nil?
|
|
5166
|
-
is_attrs << %( showValue="#{is[:show_value] ? 1 : 0}") unless is[:show_value].nil?
|
|
5167
|
-
parts << "<iconSet#{is_attrs}>"
|
|
5168
|
-
is[:cfvo]&.each do |cfvo|
|
|
5169
|
-
cfvo_attrs = %(type="#{cfvo[:type]}")
|
|
5170
|
-
cfvo_attrs << %( val="#{cfvo[:val]}") if cfvo[:val]
|
|
5171
|
-
cfvo_attrs << ' gte="0"' if cfvo[:gte] == false
|
|
5172
|
-
parts << "<cfvo #{cfvo_attrs}/>"
|
|
5173
|
-
end
|
|
5174
|
-
parts << "</iconSet>"
|
|
5175
|
-
parts << "</cfRule>"
|
|
5176
|
-
else
|
|
5177
|
-
parts << "<cfRule #{rule_attrs}/>"
|
|
5178
|
-
end
|
|
5179
|
-
end
|
|
5180
|
-
|
|
5181
|
-
# Emits a <color> element for CF rules, accepting either a plain RGB string or a hash.
|
|
5182
|
-
# : (untyped color) -> (untyped | ::String)
|
|
5183
|
-
def emit_cf_color_xml(color)
|
|
5184
|
-
if color.is_a?(Hash)
|
|
5185
|
-
emit_color_xml(color)
|
|
5186
|
-
else
|
|
5187
|
-
%(<color rgb="#{color}"/>)
|
|
5188
|
-
end
|
|
5189
|
-
end
|
|
5190
|
-
|
|
5191
|
-
# : (untyped sheet_cells) -> ("A1" | ::String)
|
|
5192
|
-
def compute_dimension(sheet_cells)
|
|
5193
|
-
return "A1" if sheet_cells.empty?
|
|
5194
|
-
|
|
5195
|
-
min_col = Float::INFINITY
|
|
5196
|
-
max_col = 0
|
|
5197
|
-
min_row = Float::INFINITY
|
|
5198
|
-
max_row = 0
|
|
5199
|
-
sheet_cells.each_key do |addr|
|
|
5200
|
-
col_letter = extract_column_letter(addr)
|
|
5201
|
-
row_num = extract_row_number(addr)
|
|
5202
|
-
col_idx = column_letter_to_index(col_letter)
|
|
5203
|
-
min_col = col_idx if col_idx < min_col
|
|
5204
|
-
max_col = col_idx if col_idx > max_col
|
|
5205
|
-
min_row = row_num if row_num < min_row
|
|
5206
|
-
max_row = row_num if row_num > max_row
|
|
5207
|
-
end
|
|
5208
|
-
start_col = index_to_column_letter(min_col)
|
|
5209
|
-
end_col = index_to_column_letter(max_col)
|
|
5210
|
-
"#{start_col}#{min_row}:#{end_col}#{max_row}"
|
|
5211
|
-
end
|
|
5212
|
-
|
|
5213
|
-
# : (untyped index) -> untyped
|
|
5214
|
-
def index_to_column_letter(index)
|
|
5215
|
-
result = +""
|
|
5216
|
-
while index.positive?
|
|
5217
|
-
index -= 1
|
|
5218
|
-
result.prepend(("A".ord + (index % 26)).chr)
|
|
5219
|
-
index /= 26
|
|
5220
|
-
end
|
|
5221
|
-
result
|
|
5222
|
-
end
|
|
5223
|
-
|
|
5224
|
-
# : () -> untyped
|
|
5225
|
-
def generate_core_properties_xml
|
|
5226
|
-
parts = [
|
|
5227
|
-
XML_HEADER,
|
|
5228
|
-
%(<cp:coreProperties xmlns:cp="#{CP_NS}" xmlns:dc="#{DC_NS}" xmlns:dcterms="#{DCTERMS_NS}" xmlns:xsi="#{XSI_NS}">)
|
|
5229
|
-
]
|
|
5230
|
-
parts << "<dc:title>#{xml_escape(@core_properties[:title])}</dc:title>" if @core_properties[:title]
|
|
5231
|
-
parts << "<dc:subject>#{xml_escape(@core_properties[:subject])}</dc:subject>" if @core_properties[:subject]
|
|
5232
|
-
parts << "<dc:creator>#{xml_escape(@core_properties[:creator])}</dc:creator>" if @core_properties[:creator]
|
|
5233
|
-
parts << "<cp:keywords>#{xml_escape(@core_properties[:keywords])}</cp:keywords>" if @core_properties[:keywords]
|
|
5234
|
-
parts << "<dc:description>#{xml_escape(@core_properties[:description])}</dc:description>" if @core_properties[:description]
|
|
5235
|
-
parts << "<cp:lastModifiedBy>#{xml_escape(@core_properties[:last_modified_by])}</cp:lastModifiedBy>" if @core_properties[:last_modified_by]
|
|
5236
|
-
parts << "<cp:revision>#{xml_escape(@core_properties[:revision])}</cp:revision>" if @core_properties[:revision]
|
|
5237
|
-
parts << %(<dcterms:created xsi:type="dcterms:W3CDTF">#{xml_escape(@core_properties[:created])}</dcterms:created>) if @core_properties[:created]
|
|
5238
|
-
parts << %(<dcterms:modified xsi:type="dcterms:W3CDTF">#{xml_escape(@core_properties[:modified])}</dcterms:modified>) if @core_properties[:modified]
|
|
5239
|
-
parts << "<cp:category>#{xml_escape(@core_properties[:category])}</cp:category>" if @core_properties[:category]
|
|
5240
|
-
parts << "<cp:contentStatus>#{xml_escape(@core_properties[:content_status])}</cp:contentStatus>" if @core_properties[:content_status]
|
|
5241
|
-
parts << "<dc:language>#{xml_escape(@core_properties[:language])}</dc:language>" if @core_properties[:language]
|
|
5242
|
-
parts << "</cp:coreProperties>"
|
|
5243
|
-
parts.join
|
|
5244
|
-
end
|
|
5245
|
-
|
|
5246
|
-
# : () -> untyped
|
|
5247
|
-
def generate_app_properties_xml
|
|
5248
|
-
parts = [
|
|
5249
|
-
XML_HEADER,
|
|
5250
|
-
%(<Properties xmlns="#{APP_NS}" xmlns:vt="#{VT_NS}">)
|
|
5251
|
-
]
|
|
5252
|
-
parts << "<Application>#{xml_escape(@app_properties[:application])}</Application>" if @app_properties[:application]
|
|
5253
|
-
parts << "<AppVersion>#{xml_escape(@app_properties[:app_version])}</AppVersion>" if @app_properties[:app_version]
|
|
5254
|
-
if @app_properties[:heading_pairs] && @app_properties[:titles_of_parts]
|
|
5255
|
-
hp = @app_properties[:heading_pairs]
|
|
5256
|
-
tp = @app_properties[:titles_of_parts]
|
|
5257
|
-
parts << "<HeadingPairs>"
|
|
5258
|
-
parts << %(<vt:vector size="#{hp.size * 2}" baseType="variant">)
|
|
5259
|
-
hp.each do |label, count|
|
|
5260
|
-
parts << "<vt:variant><vt:lpstr>#{xml_escape(label)}</vt:lpstr></vt:variant>"
|
|
5261
|
-
parts << "<vt:variant><vt:i4>#{count}</vt:i4></vt:variant>"
|
|
5262
|
-
end
|
|
5263
|
-
parts << "</vt:vector>"
|
|
5264
|
-
parts << "</HeadingPairs>"
|
|
5265
|
-
parts << "<TitlesOfParts>"
|
|
5266
|
-
parts << %(<vt:vector size="#{tp.size}" baseType="lpstr">)
|
|
5267
|
-
tp.each { |t| parts << "<vt:lpstr>#{xml_escape(t)}</vt:lpstr>" }
|
|
5268
|
-
parts << "</vt:vector>"
|
|
5269
|
-
parts << "</TitlesOfParts>"
|
|
5270
|
-
end
|
|
5271
|
-
parts << "</Properties>"
|
|
5272
|
-
parts.join
|
|
5273
|
-
end
|
|
5274
|
-
|
|
5275
|
-
# : () -> untyped
|
|
5276
|
-
def generate_custom_properties_xml
|
|
5277
|
-
custom_ns = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"
|
|
5278
|
-
parts = [
|
|
5279
|
-
XML_HEADER,
|
|
5280
|
-
%(<Properties xmlns="#{custom_ns}" xmlns:vt="#{VT_NS}">)
|
|
5281
|
-
]
|
|
5282
|
-
@custom_properties.each_with_index do |prop, idx|
|
|
5283
|
-
fmtid = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"
|
|
5284
|
-
pid = idx + 2 # pids start at 2
|
|
5285
|
-
parts << %(<property fmtid="#{fmtid}" pid="#{pid}" name="#{xml_escape(prop[:name])}">)
|
|
5286
|
-
parts << case prop[:type]
|
|
5287
|
-
when :number, :int, :i4
|
|
5288
|
-
"<vt:i4>#{prop[:value]}</vt:i4>"
|
|
5289
|
-
when :float, :r8
|
|
5290
|
-
"<vt:r8>#{prop[:value]}</vt:r8>"
|
|
5291
|
-
when :bool
|
|
5292
|
-
"<vt:bool>#{prop[:value] ? "true" : "false"}</vt:bool>"
|
|
5293
|
-
when :date, :filetime
|
|
5294
|
-
"<vt:filetime>#{prop[:value]}</vt:filetime>"
|
|
5295
|
-
else
|
|
5296
|
-
"<vt:lpwstr>#{xml_escape(prop[:value].to_s)}</vt:lpwstr>"
|
|
5297
|
-
end
|
|
5298
|
-
parts << "</property>"
|
|
5299
|
-
end
|
|
5300
|
-
parts << "</Properties>"
|
|
5301
|
-
parts.join
|
|
5302
|
-
end
|
|
5303
|
-
|
|
5304
|
-
# : () -> untyped
|
|
5305
|
-
def generate_styles_xml
|
|
5306
|
-
parts = [
|
|
5307
|
-
XML_HEADER,
|
|
5308
|
-
%(<styleSheet xmlns="#{SSML_NS}">)
|
|
5309
|
-
]
|
|
5310
|
-
|
|
5311
|
-
# numFmts
|
|
5312
|
-
unless @num_fmts.empty?
|
|
5313
|
-
parts << %(<numFmts count="#{@num_fmts.size}">)
|
|
5314
|
-
@num_fmts.each do |nf|
|
|
5315
|
-
parts << %(<numFmt numFmtId="#{nf[:num_fmt_id]}" formatCode="#{xml_escape(nf[:format_code])}"/>)
|
|
5316
|
-
end
|
|
5317
|
-
parts << "</numFmts>"
|
|
5318
|
-
end
|
|
5319
|
-
|
|
5320
|
-
# fonts
|
|
5321
|
-
parts << %(<fonts count="#{@fonts.size}">)
|
|
5322
|
-
@fonts.each { |f| parts << emit_font_xml(f) }
|
|
5323
|
-
parts << "</fonts>"
|
|
5324
|
-
|
|
5325
|
-
# fills
|
|
5326
|
-
parts << %(<fills count="#{@fills.size}">)
|
|
5327
|
-
@fills.each { |f| parts << emit_fill_xml(f) }
|
|
5328
|
-
parts << "</fills>"
|
|
5329
|
-
|
|
5330
|
-
# borders
|
|
5331
|
-
parts << %(<borders count="#{@borders.size}">)
|
|
5332
|
-
@borders.each { |b| parts << emit_border_xml(b) }
|
|
5333
|
-
parts << "</borders>"
|
|
5334
|
-
|
|
5335
|
-
# cellStyleXfs
|
|
5336
|
-
parts << %(<cellStyleXfs count="#{@cell_style_xfs.size}">)
|
|
5337
|
-
@cell_style_xfs.each do |xf|
|
|
5338
|
-
parts << %(<xf numFmtId="#{xf[:num_fmt_id]}" fontId="#{xf[:font_id]}" fillId="#{xf[:fill_id]}" borderId="#{xf[:border_id]}"/>)
|
|
5339
|
-
end
|
|
5340
|
-
parts << "</cellStyleXfs>"
|
|
5341
|
-
|
|
5342
|
-
# cellXfs
|
|
5343
|
-
parts << %(<cellXfs count="#{@xf_entries.size}">)
|
|
5344
|
-
@xf_entries.each do |xf|
|
|
5345
|
-
apply_attrs = []
|
|
5346
|
-
apply_attrs << ' applyNumberFormat="1"' if xf[:num_fmt_id].positive?
|
|
5347
|
-
apply_attrs << ' applyFont="1"' if xf[:font_id].positive?
|
|
5348
|
-
apply_attrs << ' applyFill="1"' if xf[:fill_id].positive?
|
|
5349
|
-
apply_attrs << ' applyBorder="1"' if xf[:border_id].positive?
|
|
5350
|
-
apply_attrs << ' applyAlignment="1"' if xf[:alignment]
|
|
5351
|
-
apply_attrs << ' applyProtection="1"' if xf[:protection]
|
|
5352
|
-
apply_attrs << ' quotePrefix="1"' if xf[:quote_prefix]
|
|
5353
|
-
apply_attrs << ' pivotButton="1"' if xf[:pivot_button]
|
|
5354
|
-
children = []
|
|
5355
|
-
children << emit_alignment_xml(xf[:alignment]) if xf[:alignment]
|
|
5356
|
-
children << emit_protection_xml(xf[:protection]) if xf[:protection]
|
|
5357
|
-
xf_id = xf[:xf_id] || 0
|
|
5358
|
-
parts << if children.any?
|
|
5359
|
-
%(<xf numFmtId="#{xf[:num_fmt_id]}" fontId="#{xf[:font_id]}" fillId="#{xf[:fill_id]}" borderId="#{xf[:border_id]}" xfId="#{xf_id}"#{apply_attrs.join}>#{children.join}</xf>)
|
|
5360
|
-
else
|
|
5361
|
-
%(<xf numFmtId="#{xf[:num_fmt_id]}" fontId="#{xf[:font_id]}" fillId="#{xf[:fill_id]}" borderId="#{xf[:border_id]}" xfId="#{xf_id}"#{apply_attrs.join}/>)
|
|
5362
|
-
end
|
|
5363
|
-
end
|
|
5364
|
-
parts << "</cellXfs>"
|
|
5365
|
-
|
|
5366
|
-
# cellStyles
|
|
5367
|
-
parts << %(<cellStyles count="#{@cell_style_names.size}">)
|
|
5368
|
-
@cell_style_names.each do |cs|
|
|
5369
|
-
cs_attrs = %(name="#{xml_escape(cs[:name])}" xfId="#{cs[:xf_id]}")
|
|
5370
|
-
cs_attrs << %( builtinId="#{cs[:builtin_id]}") if cs[:builtin_id]
|
|
5371
|
-
cs_attrs << %( iLevel="#{cs[:i_level]}") if cs[:i_level]
|
|
5372
|
-
cs_attrs << ' hidden="1"' if cs[:hidden]
|
|
5373
|
-
cs_attrs << ' customBuiltin="1"' if cs[:custom_builtin]
|
|
5374
|
-
parts << "<cellStyle #{cs_attrs}/>"
|
|
5375
|
-
end
|
|
5376
|
-
parts << "</cellStyles>"
|
|
5377
|
-
|
|
5378
|
-
# dxfs
|
|
5379
|
-
unless @dxfs.empty?
|
|
5380
|
-
parts << %(<dxfs count="#{@dxfs.size}">)
|
|
5381
|
-
@dxfs.each { |d| parts << emit_dxf_xml(d) }
|
|
5382
|
-
parts << "</dxfs>"
|
|
5383
|
-
end
|
|
5384
|
-
|
|
5385
|
-
# tableStyles
|
|
5386
|
-
ts_styles = @table_styles[:styles] || []
|
|
5387
|
-
unless ts_styles.empty? && @table_styles[:default_table_style].nil? && @table_styles[:default_pivot_style].nil?
|
|
5388
|
-
ts_attrs = [%(count="#{ts_styles.size}")]
|
|
5389
|
-
ts_attrs << %(defaultTableStyle="#{xml_escape(@table_styles[:default_table_style])}") if @table_styles[:default_table_style]
|
|
5390
|
-
ts_attrs << %(defaultPivotStyle="#{xml_escape(@table_styles[:default_pivot_style])}") if @table_styles[:default_pivot_style]
|
|
5391
|
-
if ts_styles.empty?
|
|
5392
|
-
parts << "<tableStyles #{ts_attrs.join(" ")}/>"
|
|
5393
|
-
else
|
|
5394
|
-
parts << "<tableStyles #{ts_attrs.join(" ")}>"
|
|
5395
|
-
ts_styles.each do |ts|
|
|
5396
|
-
s_attrs = [%(name="#{xml_escape(ts[:name])}")]
|
|
5397
|
-
s_attrs << %(pivot="0") if ts[:pivot] == false
|
|
5398
|
-
s_attrs << %(table="0") if ts[:table] == false
|
|
5399
|
-
s_attrs << %(count="#{ts[:elements].size}") unless ts[:elements].empty?
|
|
5400
|
-
if ts[:elements].empty?
|
|
5401
|
-
parts << "<tableStyle #{s_attrs.join(" ")}/>"
|
|
5402
|
-
else
|
|
5403
|
-
parts << "<tableStyle #{s_attrs.join(" ")}>"
|
|
5404
|
-
ts[:elements].each do |el|
|
|
5405
|
-
el_attrs = [%(type="#{el[:type]}")]
|
|
5406
|
-
el_attrs << %(size="#{el[:size]}") if el[:size] && el[:size] != 1
|
|
5407
|
-
el_attrs << %(dxfId="#{el[:dxf_id]}") if el[:dxf_id]
|
|
5408
|
-
parts << "<tableStyleElement #{el_attrs.join(" ")}/>"
|
|
5409
|
-
end
|
|
5410
|
-
parts << "</tableStyle>"
|
|
5411
|
-
end
|
|
5412
|
-
end
|
|
5413
|
-
parts << "</tableStyles>"
|
|
5414
|
-
end
|
|
5415
|
-
end
|
|
5416
|
-
|
|
5417
|
-
# colors
|
|
5418
|
-
unless @indexed_colors.empty? && @mru_colors.empty?
|
|
5419
|
-
parts << "<colors>"
|
|
5420
|
-
unless @indexed_colors.empty?
|
|
5421
|
-
parts << "<indexedColors>"
|
|
5422
|
-
@indexed_colors.each { |c| parts << %(<rgbColor rgb="#{c}"/>) }
|
|
5423
|
-
parts << "</indexedColors>"
|
|
5424
|
-
end
|
|
5425
|
-
unless @mru_colors.empty?
|
|
5426
|
-
parts << "<mruColors>"
|
|
5427
|
-
@mru_colors.each { |c| parts << emit_color_xml(c) }
|
|
5428
|
-
parts << "</mruColors>"
|
|
5429
|
-
end
|
|
5430
|
-
parts << "</colors>"
|
|
5431
|
-
end
|
|
5432
|
-
|
|
5433
|
-
parts << "</styleSheet>"
|
|
5434
|
-
parts.join
|
|
5435
|
-
end
|
|
5436
|
-
|
|
5437
|
-
# : (untyped alignment) -> ::String
|
|
5438
|
-
def emit_alignment_xml(alignment)
|
|
5439
|
-
attrs = []
|
|
5440
|
-
attrs << %(horizontal="#{alignment[:horizontal]}") if alignment[:horizontal]
|
|
5441
|
-
attrs << %(vertical="#{alignment[:vertical]}") if alignment[:vertical]
|
|
5442
|
-
attrs << %(wrapText="1") if alignment[:wrap_text]
|
|
5443
|
-
attrs << %(textRotation="#{alignment[:text_rotation]}") if alignment[:text_rotation]
|
|
5444
|
-
attrs << %(indent="#{alignment[:indent]}") if alignment[:indent]
|
|
5445
|
-
attrs << %(relativeIndent="#{alignment[:relative_indent]}") if alignment[:relative_indent]
|
|
5446
|
-
attrs << %(shrinkToFit="1") if alignment[:shrink_to_fit]
|
|
5447
|
-
attrs << %(readingOrder="#{alignment[:reading_order]}") if alignment[:reading_order]
|
|
5448
|
-
attrs << %(justifyLastLine="1") if alignment[:justify_last_line]
|
|
5449
|
-
"<alignment #{attrs.join(" ")}/>"
|
|
5450
|
-
end
|
|
5451
|
-
|
|
5452
|
-
# : (untyped protection) -> ::String
|
|
5453
|
-
def emit_protection_xml(protection)
|
|
5454
|
-
attrs = []
|
|
5455
|
-
attrs << %(locked="#{protection[:locked] ? "1" : "0"}") unless protection[:locked].nil?
|
|
5456
|
-
attrs << %(hidden="#{protection[:hidden] ? "1" : "0"}") unless protection[:hidden].nil?
|
|
5457
|
-
"<protection #{attrs.join(" ")}/>"
|
|
5458
|
-
end
|
|
5459
|
-
|
|
5460
|
-
# : (untyped source, ?tag: ::String) -> (::String | ::String | ::String | ::String | "")
|
|
5461
|
-
def emit_color_xml(source, tag: "color")
|
|
5462
|
-
if source[:auto]
|
|
5463
|
-
%(<#{tag} auto="1"/>)
|
|
5464
|
-
elsif source[:color] || source[:rgb]
|
|
5465
|
-
%(<#{tag} rgb="#{source[:color] || source[:rgb]}"/>)
|
|
5466
|
-
elsif source[:theme]
|
|
5467
|
-
attrs = [%(theme="#{source[:theme]}")]
|
|
5468
|
-
attrs << %(tint="#{source[:tint]}") if source[:tint]
|
|
5469
|
-
%(<#{tag} #{attrs.join(" ")}/>)
|
|
5470
|
-
elsif source[:indexed]
|
|
5471
|
-
%(<#{tag} indexed="#{source[:indexed]}"/>)
|
|
5472
|
-
else
|
|
5473
|
-
""
|
|
5474
|
-
end
|
|
5475
|
-
end
|
|
5476
|
-
|
|
5477
|
-
# : (untyped font) -> untyped
|
|
5478
|
-
def emit_font_xml(font)
|
|
5479
|
-
parts = ["<font>"]
|
|
5480
|
-
parts << "<b/>" if font[:bold]
|
|
5481
|
-
parts << "<i/>" if font[:italic]
|
|
5482
|
-
parts << "<strike/>" if font[:strike]
|
|
5483
|
-
parts << "<shadow/>" if font[:shadow]
|
|
5484
|
-
parts << "<outline/>" if font[:outline]
|
|
5485
|
-
parts << "<condense/>" if font[:condense]
|
|
5486
|
-
parts << "<extend/>" if font[:extend]
|
|
5487
|
-
if font[:underline]
|
|
5488
|
-
parts << if font[:underline] == true
|
|
5489
|
-
"<u/>"
|
|
5490
|
-
else
|
|
5491
|
-
%(<u val="#{font[:underline]}"/>)
|
|
5492
|
-
end
|
|
5493
|
-
end
|
|
5494
|
-
parts << %(<vertAlign val="#{font[:vert_align]}"/>) if font[:vert_align]
|
|
5495
|
-
parts << %(<sz val="#{font[:sz]}"/>) if font[:sz]
|
|
5496
|
-
parts << emit_color_xml(font)
|
|
5497
|
-
parts << %(<name val="#{xml_escape(font[:name])}"/>) if font[:name]
|
|
5498
|
-
parts << %(<family val="#{font[:family]}"/>) if font[:family]
|
|
5499
|
-
parts << %(<charset val="#{font[:charset]}"/>) if font[:charset]
|
|
5500
|
-
parts << %(<scheme val="#{font[:scheme]}"/>) if font[:scheme]
|
|
5501
|
-
parts << "</font>"
|
|
5502
|
-
parts.join
|
|
5503
|
-
end
|
|
5504
|
-
|
|
5505
|
-
# : (untyped fill) -> (untyped | ::String)
|
|
5506
|
-
def emit_fill_xml(fill)
|
|
5507
|
-
return emit_gradient_fill_xml(fill[:gradient]) if fill[:gradient]
|
|
5508
|
-
|
|
5509
|
-
has_fg = fill[:fg_color] || fill[:fg_color_theme] || fill[:fg_color_indexed] || fill[:fg_color_auto]
|
|
5510
|
-
has_bg = fill[:bg_color] || fill[:bg_color_theme] || fill[:bg_color_indexed] || fill[:bg_color_auto]
|
|
5511
|
-
return "<fill><patternFill patternType=\"#{fill[:pattern]}\"/></fill>" if fill[:pattern] && !has_fg && !has_bg
|
|
5512
|
-
|
|
5513
|
-
parts = ["<fill>"]
|
|
5514
|
-
pt = fill[:pattern] || "solid"
|
|
5515
|
-
parts << %(<patternFill patternType="#{pt}">)
|
|
5516
|
-
parts << emit_fill_color_xml("fgColor", fill, :fg)
|
|
5517
|
-
parts << emit_fill_color_xml("bgColor", fill, :bg)
|
|
5518
|
-
parts << "</patternFill>"
|
|
5519
|
-
parts << "</fill>"
|
|
5520
|
-
parts.join
|
|
5521
|
-
end
|
|
5522
|
-
|
|
5523
|
-
# : (untyped tag, untyped fill, untyped prefix) -> (::String | ::String | ::String | ::String | "")
|
|
5524
|
-
def emit_fill_color_xml(tag, fill, prefix)
|
|
5525
|
-
if fill[:"#{prefix}_color"]
|
|
5526
|
-
%(<#{tag} rgb="#{fill[:"#{prefix}_color"]}"/>)
|
|
5527
|
-
elsif fill[:"#{prefix}_color_theme"]
|
|
5528
|
-
attrs = [%(theme="#{fill[:"#{prefix}_color_theme"]}")]
|
|
5529
|
-
attrs << %(tint="#{fill[:"#{prefix}_color_tint"]}") if fill[:"#{prefix}_color_tint"]
|
|
5530
|
-
%(<#{tag} #{attrs.join(" ")}/>)
|
|
5531
|
-
elsif fill[:"#{prefix}_color_indexed"]
|
|
5532
|
-
%(<#{tag} indexed="#{fill[:"#{prefix}_color_indexed"]}"/>)
|
|
5533
|
-
elsif fill[:"#{prefix}_color_auto"]
|
|
5534
|
-
%(<#{tag} auto="1"/>)
|
|
5535
|
-
else
|
|
5536
|
-
""
|
|
5537
|
-
end
|
|
5538
|
-
end
|
|
5539
|
-
|
|
5540
|
-
# : (untyped gradient) -> untyped
|
|
5541
|
-
def emit_gradient_fill_xml(gradient)
|
|
5542
|
-
attrs = []
|
|
5543
|
-
attrs << %(type="#{gradient[:type]}") if gradient[:type]
|
|
5544
|
-
attrs << %(degree="#{gradient[:degree]}") if gradient[:degree]
|
|
5545
|
-
attrs << %(left="#{gradient[:left]}") if gradient[:left]
|
|
5546
|
-
attrs << %(right="#{gradient[:right]}") if gradient[:right]
|
|
5547
|
-
attrs << %(top="#{gradient[:top]}") if gradient[:top]
|
|
5548
|
-
attrs << %(bottom="#{gradient[:bottom]}") if gradient[:bottom]
|
|
5549
|
-
parts = ["<fill>"]
|
|
5550
|
-
parts << "<gradientFill#{" #{attrs.join(" ")}" unless attrs.empty?}"
|
|
5551
|
-
if gradient[:stops]&.any?
|
|
5552
|
-
parts[-1] = "#{parts[-1]}>"
|
|
5553
|
-
gradient[:stops].each do |stop|
|
|
5554
|
-
parts << %(<stop position="#{stop[:position]}">#{emit_color_xml(stop)}</stop>)
|
|
5555
|
-
end
|
|
5556
|
-
parts << "</gradientFill>"
|
|
5557
|
-
else
|
|
5558
|
-
parts[-1] = "#{parts[-1]}/>"
|
|
5559
|
-
end
|
|
5560
|
-
parts << "</fill>"
|
|
5561
|
-
parts.join
|
|
5562
|
-
end
|
|
5563
|
-
|
|
5564
|
-
# : (untyped brk, default_max: untyped) -> ::String
|
|
5565
|
-
def emit_brk_xml(brk, default_max:)
|
|
5566
|
-
if brk.is_a?(Hash)
|
|
5567
|
-
attrs = %(id="#{brk[:id]}")
|
|
5568
|
-
attrs << %( min="#{brk[:min]}") if brk[:min]
|
|
5569
|
-
attrs << %( max="#{brk.fetch(:max, default_max)}")
|
|
5570
|
-
attrs << ' man="1"' if brk.fetch(:man, true)
|
|
5571
|
-
attrs << ' pt="1"' if brk[:pt]
|
|
5572
|
-
"<brk #{attrs}/>"
|
|
5573
|
-
else
|
|
5574
|
-
%(<brk id="#{brk}" max="#{default_max}" man="1"/>)
|
|
5575
|
-
end
|
|
5576
|
-
end
|
|
5577
|
-
|
|
5578
|
-
# : (untyped bdr) -> untyped
|
|
5579
|
-
def emit_border_xml(bdr)
|
|
5580
|
-
border_attrs = []
|
|
5581
|
-
border_attrs << ' diagonalUp="1"' if bdr[:diagonal_up]
|
|
5582
|
-
border_attrs << ' diagonalDown="1"' if bdr[:diagonal_down]
|
|
5583
|
-
border_attrs << ' outline="0"' if bdr[:outline] == false
|
|
5584
|
-
parts = ["<border#{border_attrs.join}>"]
|
|
5585
|
-
%i[left right top bottom diagonal vertical horizontal].each do |side|
|
|
5586
|
-
s = bdr[side]
|
|
5587
|
-
if s.is_a?(Hash)
|
|
5588
|
-
parts << %(<#{side} style="#{s[:style]}">)
|
|
5589
|
-
parts << emit_color_xml(s)
|
|
5590
|
-
parts << "</#{side}>"
|
|
5591
|
-
else
|
|
5592
|
-
parts << "<#{side}/>"
|
|
5593
|
-
end
|
|
5594
|
-
end
|
|
5595
|
-
parts << "</border>"
|
|
5596
|
-
parts.join
|
|
5597
|
-
end
|
|
5598
|
-
|
|
5599
|
-
# : (untyped dxf) -> untyped
|
|
5600
|
-
def emit_dxf_xml(dxf)
|
|
5601
|
-
parts = ["<dxf>"]
|
|
5602
|
-
parts << emit_font_xml(dxf[:font]) if dxf[:font]
|
|
5603
|
-
if dxf[:num_fmt]
|
|
5604
|
-
nf = dxf[:num_fmt]
|
|
5605
|
-
parts << %(<numFmt numFmtId="#{nf[:num_fmt_id]}" formatCode="#{xml_escape(nf[:format_code])}"/>)
|
|
5606
|
-
end
|
|
5607
|
-
parts << emit_fill_xml(dxf[:fill]) if dxf[:fill]
|
|
5608
|
-
parts << emit_alignment_xml(dxf[:alignment]) if dxf[:alignment]
|
|
5609
|
-
parts << emit_border_xml(dxf[:border]) if dxf[:border]
|
|
5610
|
-
parts << emit_protection_xml(dxf[:protection]) if dxf[:protection]
|
|
5611
|
-
parts << "</dxf>"
|
|
5612
|
-
parts.join
|
|
5613
|
-
end
|
|
5614
|
-
|
|
5615
|
-
# : (untyped cell_address) -> untyped
|
|
5616
|
-
def validate_cell_address!(cell_address)
|
|
5617
|
-
raise ArgumentError, "cell address must be a String" unless cell_address.is_a?(String)
|
|
5618
|
-
|
|
5619
|
-
match = cell_address.match(CELL_ADDRESS_PATTERN)
|
|
5620
|
-
raise ArgumentError, "invalid cell address: #{cell_address.inspect}" unless match
|
|
5621
|
-
|
|
5622
|
-
row_num = match[2].to_i
|
|
5623
|
-
raise ArgumentError, "row out of range: #{row_num}" unless row_num.between?(1, MAX_ROW)
|
|
5624
|
-
|
|
5625
|
-
col_index = column_letter_to_index(match[1])
|
|
5626
|
-
raise ArgumentError, "column out of range: #{match[1]}" unless col_index.between?(1, MAX_COLUMN_INDEX)
|
|
5627
|
-
end
|
|
5628
|
-
|
|
5629
|
-
# : (untyped letters) -> untyped
|
|
5630
|
-
def column_letter_to_index(letters)
|
|
5631
|
-
letters.chars.reduce(0) { |sum, char| (sum * 26) + (char.ord - "A".ord + 1) }
|
|
5632
|
-
end
|
|
5633
|
-
|
|
5634
|
-
# Extracts the column letter(s) from a cell address, e.g. "A" from "A1".
|
|
5635
|
-
# : (untyped cell_address) -> untyped
|
|
5636
|
-
def extract_column_letter(cell_address)
|
|
5637
|
-
cell_address.match(/^([A-Z]+)/)[1]
|
|
5638
|
-
end
|
|
5639
|
-
|
|
5640
3003
|
# Extracts the row number from a cell address, e.g. 1 from "A1".
|
|
5641
3004
|
# : (untyped cell_address) -> untyped
|
|
5642
3005
|
def extract_row_number(cell_address)
|
|
@@ -5646,7 +3009,7 @@ module Xlsxrb
|
|
|
5646
3009
|
# Converts a range like "A1:D20" to absolute "$A$1:$D$20".
|
|
5647
3010
|
# : (untyped range) -> untyped
|
|
5648
3011
|
def absolute_range(range)
|
|
5649
|
-
|
|
3012
|
+
DslHelpers.absolute_range(range)
|
|
5650
3013
|
end
|
|
5651
3014
|
|
|
5652
3015
|
# Converts "A1" to "$A$1".
|