goldendocx 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/CHANGELOG.md +8 -2
- data/Gemfile.lock +3 -1
- data/README.md +10 -2
- data/Rakefile +14 -3
- data/demo/templates/generate_codes.rb +10 -0
- data/demo/templates/xml_to_class.rb +91 -0
- data/demo/texts/append_plain_text.rb +1 -1
- data/demo/texts/append_styled_text.rb +1 -2
- data/demo/texts/create_text.rb +14 -0
- data/lib/extensions/active_support_extensions.rb +16 -0
- data/lib/extensions/nokogiri_extensions.rb +41 -0
- data/lib/extensions/ox_extensions.rb +35 -0
- data/lib/extensions/xml_serialize_extensions.rb +45 -0
- data/lib/goldendocx/components/properties/font_property.rb +19 -0
- data/lib/goldendocx/components/properties/language_property.rb +18 -0
- data/lib/goldendocx/components/properties/run_property.rb +2 -0
- data/lib/goldendocx/content_types/default.rb +2 -6
- data/lib/goldendocx/content_types/override.rb +2 -6
- data/lib/goldendocx/document.rb +9 -28
- data/lib/goldendocx/documents/body.rb +20 -11
- data/lib/goldendocx/documents/document.rb +2 -4
- data/lib/goldendocx/documents/latent_styles.rb +12 -0
- data/lib/goldendocx/documents/properties/default_style_property.rb +17 -0
- data/lib/goldendocx/documents/properties/page_margin_property.rb +37 -0
- data/lib/goldendocx/documents/properties/page_size_property.rb +17 -0
- data/lib/goldendocx/documents/properties/paragraph_default_style_property.rb +16 -0
- data/lib/goldendocx/documents/properties/run_default_style_property.rb +22 -0
- data/lib/goldendocx/documents/properties/section_property.rb +17 -0
- data/lib/goldendocx/documents/properties/style_name_property.rb +16 -0
- data/lib/goldendocx/documents/properties.rb +12 -0
- data/lib/goldendocx/documents/settings.rb +23 -0
- data/lib/goldendocx/documents/style.rb +10 -10
- data/lib/goldendocx/documents/styles.rb +9 -33
- data/lib/goldendocx/docx.rb +55 -12
- data/lib/goldendocx/element.rb +33 -140
- data/lib/goldendocx/has_associations.rb +52 -0
- data/lib/goldendocx/has_attributes.rb +67 -0
- data/lib/goldendocx/has_children.rb +116 -0
- data/lib/goldendocx/{documents → models}/relationship.rb +1 -1
- data/lib/goldendocx/{documents → models}/relationships.rb +4 -12
- data/lib/goldendocx/models.rb +10 -0
- data/lib/goldendocx/parts/app.rb +46 -0
- data/lib/goldendocx/parts/content_types.rb +16 -30
- data/lib/goldendocx/parts/core.rb +46 -0
- data/lib/goldendocx/parts/documents.rb +26 -13
- data/lib/goldendocx/parts/properties/created_at_property.rb +17 -0
- data/lib/goldendocx/parts/properties/creator_property.rb +16 -0
- data/lib/goldendocx/parts/properties/revision_property.rb +16 -0
- data/lib/goldendocx/parts/properties/updated_at_property.rb +17 -0
- data/lib/goldendocx/parts/properties/updater_property.rb +16 -0
- data/lib/goldendocx/parts/properties.rb +12 -0
- data/lib/goldendocx/tables/row.rb +1 -1
- data/lib/goldendocx/version.rb +1 -1
- data/lib/goldendocx/xml_serializers/nokogiri.rb +30 -22
- data/lib/goldendocx/xml_serializers/ox.rb +10 -20
- data/lib/goldendocx.rb +14 -2
- metadata +51 -7
- data/lib/goldendocx/documents/element.rb +0 -23
- data/lib/goldendocx/documents/unparsed_style.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec01d96d925886ed23aa79de96bfcc71fd8c0cb0c151239c7696536095b7c8c4
|
4
|
+
data.tar.gz: e72d2a380906b12216d2e29ef2cb4ec94e9036860d9d5000a60313f8a95a8350
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c53baede5b94336cbf2b3461183d3301114842d3bea12957655f86118d9fa9050f7f78ab2ed072720bcd0f43c126b61a8ea59cdf78b28cb7fd3282f676937860
|
7
|
+
data.tar.gz: 576eca3f0caedb097062afb1952ee05ac39786848498cc9de1ac55359a25131320fd2d93a0df3e75ab992419b98bef1a4831e7d4bbb2e34a0fcbe540d69fa29b
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -30,7 +30,7 @@ Mostly refactoring
|
|
30
30
|
- Introduce `ActiveSupport` to make coding easier
|
31
31
|
- Clean irrelevant demos
|
32
32
|
|
33
|
-
## [0.2.2] - 2023-03-31
|
33
|
+
## [0.2.2] - 2023-03-31
|
34
34
|
|
35
35
|
### Features
|
36
36
|
|
@@ -40,4 +40,10 @@ Mostly refactoring
|
|
40
40
|
### Fixes
|
41
41
|
|
42
42
|
- Fix create charts issues
|
43
|
-
- Fix Nokogiri XML serializer compatible issues
|
43
|
+
- Fix Nokogiri XML serializer compatible issues
|
44
|
+
|
45
|
+
## [0.2.3] - 2023-04-07
|
46
|
+
|
47
|
+
### Features
|
48
|
+
|
49
|
+
- Support create new MS Word docx file without template
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
goldendocx (0.2.
|
4
|
+
goldendocx (0.2.3)
|
5
5
|
activesupport (~> 7.0)
|
6
6
|
nokogiri (~> 1.14)
|
7
7
|
ox (~> 2.14)
|
@@ -78,6 +78,7 @@ GEM
|
|
78
78
|
simplecov-html (0.12.3)
|
79
79
|
simplecov-lcov (0.8.0)
|
80
80
|
simplecov_json_formatter (0.1.4)
|
81
|
+
timecop (0.9.6)
|
81
82
|
tzinfo (2.0.6)
|
82
83
|
concurrent-ruby (~> 1.0)
|
83
84
|
unicode-display_width (2.2.0)
|
@@ -96,6 +97,7 @@ DEPENDENCIES
|
|
96
97
|
rubocop-rspec (~> 2.19)
|
97
98
|
simplecov (~> 0.22.0)
|
98
99
|
simplecov-lcov (~> 0.8.0)
|
100
|
+
timecop (~> 0.9.6)
|
99
101
|
|
100
102
|
BUNDLED WITH
|
101
103
|
2.3.23
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# The Ruby API for Microsoft Word
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/goldendocx.svg)](https://badge.fury.io/rb/goldendocx)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/github/SheldonLeo/goldendocx/badge.svg)](https://coveralls.io/github/SheldonLeo/goldendocx)
|
5
|
+
|
3
6
|
Ruby APIs for manipulating Microsoft Word based upon OOXML standards.
|
4
7
|
|
5
8
|
## Installation
|
@@ -23,6 +26,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
23
26
|
Support both `ox` and `nokogiri` as XML serializer, and `ox` as default.
|
24
27
|
|
25
28
|
You can customize with configuration
|
29
|
+
|
26
30
|
```ruby
|
27
31
|
Goldendocx.configure do |config|
|
28
32
|
config.xml_serializer = :nokogiri
|
@@ -30,9 +34,13 @@ end
|
|
30
34
|
```
|
31
35
|
|
32
36
|
### Compose MS Word
|
33
|
-
|
37
|
+
|
38
|
+
- Create new MS Word file or read exists file and write to another path
|
34
39
|
```ruby
|
40
|
+
docx = Goldendocx::Docx.new
|
35
41
|
docx = Goldendocx::Docx.new(docx_file_path)
|
42
|
+
docx.read_from(docx_file_path)
|
43
|
+
|
36
44
|
docx.write_to(new_file_path)
|
37
45
|
```
|
38
46
|
- Create texts to MS Word
|
@@ -71,6 +79,6 @@ end
|
|
71
79
|
values = [13, 12, 9, 9, 8, 7, 7, 6, 5, 3]
|
72
80
|
chart.add_series('地域分布详情数据', categories, values)
|
73
81
|
```
|
74
|
-
|
82
|
+
|
75
83
|
More demos view at [Demos](demo/)
|
76
84
|
|
data/Rakefile
CHANGED
@@ -4,7 +4,18 @@ require 'bundler/gem_tasks'
|
|
4
4
|
require 'rspec/core/rake_task'
|
5
5
|
require 'rubocop/rake_task'
|
6
6
|
|
7
|
-
RSpec::Core::RakeTask.new(:
|
8
|
-
|
7
|
+
RSpec::Core::RakeTask.new(:ox_compatible_spec) do |task|
|
8
|
+
ENV['XML_SERIALIZER'] = 'ox'
|
9
|
+
task.failure_message = 'Failed with ox xml serializer compatible'
|
10
|
+
end
|
9
11
|
|
10
|
-
|
12
|
+
RSpec::Core::RakeTask.new(:nokogiri_compatible_spec) do |task|
|
13
|
+
ENV['XML_SERIALIZER'] = 'nokogiri'
|
14
|
+
task.failure_message = 'Failed with nokogiri xml serializer compatible'
|
15
|
+
end
|
16
|
+
|
17
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
18
|
+
task.options = ['-P']
|
19
|
+
end
|
20
|
+
|
21
|
+
task default: %i[rubocop ox_compatible_spec nokogiri_compatible_spec]
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ox'
|
4
|
+
require_relative 'xml_to_class'
|
5
|
+
|
6
|
+
xml_fragment = File.read("#{Dir.pwd}/demo/templates/styles/reportTitle")
|
7
|
+
root_class = XmlToClass.new.parse(Ox.parse(xml_fragment))
|
8
|
+
|
9
|
+
root_class.write_rb('goldendocx/styles')
|
10
|
+
root_class.write_spec('goldendocx/styles')
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ox'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'active_support/core_ext/string/inflections'
|
6
|
+
|
7
|
+
class XmlToClass
|
8
|
+
def parse(node)
|
9
|
+
@node = node
|
10
|
+
@namespace, @tag = parse_tag(@node.name)
|
11
|
+
@class_name = "#{@tag}Property".classify
|
12
|
+
parse_attributes
|
13
|
+
parse_children
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def write_rb(dir)
|
18
|
+
dir_to_write = "#{Dir.pwd}/lib/#{dir}"
|
19
|
+
FileUtils.mkdir_p(dir_to_write)
|
20
|
+
|
21
|
+
File.open("#{dir_to_write}/#{@tag}.rb", 'w') do |f|
|
22
|
+
f.puts '# frozen_string_literal: true'
|
23
|
+
f.puts ''
|
24
|
+
f.puts "class Goldendocx::#{@class_name}"
|
25
|
+
f.puts ' include Goldendocx::Element'
|
26
|
+
f.puts ''
|
27
|
+
f.puts " namespace :#{@namespace}" if @namespace
|
28
|
+
f.puts " tag :#{@tag}"
|
29
|
+
f.puts ''
|
30
|
+
@attributes.each do |name, opt|
|
31
|
+
options = opt.map { |k, v| "#{k}: :#{v}" }.join(',')
|
32
|
+
f.puts " attribute :#{name.underscore}, #{options}"
|
33
|
+
end
|
34
|
+
f.puts ''
|
35
|
+
@children.group_by(&:to_s).each do |name, values|
|
36
|
+
method = values.size > 1 ? :embeds_many : :embeds_one
|
37
|
+
_, class_name = parse_tag(name)
|
38
|
+
f.puts " #{method} :#{class_name.underscore}, class_name: 'Goldendocx::#{class_name.classify}Property'"
|
39
|
+
end
|
40
|
+
f.puts 'end'
|
41
|
+
end
|
42
|
+
|
43
|
+
@subclasses.each { |c| c.write_rb("#{dir}/#{@tag}") }
|
44
|
+
end
|
45
|
+
|
46
|
+
def write_spec(dir)
|
47
|
+
dir_to_write = "#{Dir.pwd}/spec/#{dir}"
|
48
|
+
FileUtils.mkdir_p(dir_to_write)
|
49
|
+
|
50
|
+
File.open("#{dir_to_write}/#{@tag}_spec.rb", 'w') do |f|
|
51
|
+
f.puts '# frozen_string_literal: true'
|
52
|
+
f.puts ''
|
53
|
+
f.puts "describe Goldendocx::#{@class_name} do"
|
54
|
+
f.puts ' let(:property) { described_class.new }'
|
55
|
+
f.puts ''
|
56
|
+
f.puts " specify 'builds default #{@tag} property xml' do "
|
57
|
+
f.puts " expect(property.to_xml).to eq('<#{@namespace}:#{@tag}/>')"
|
58
|
+
f.puts ' end'
|
59
|
+
f.puts ''
|
60
|
+
f.puts " specify 'builds customized #{@tag} property xml' do"
|
61
|
+
f.puts ' property.attribute = :value '
|
62
|
+
f.puts " expect(property.to_xml).to eq('<#{@namespace}:#{@tag}/>') "
|
63
|
+
f.puts ' end '
|
64
|
+
f.puts 'end'
|
65
|
+
end
|
66
|
+
|
67
|
+
@subclasses.each { |c| c.write_spec("#{dir}/#{@tag}") }
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_tag(string)
|
71
|
+
string = string.to_s
|
72
|
+
string.include?(':') ? string.split(':') : [nil, string]
|
73
|
+
end
|
74
|
+
|
75
|
+
def parse_attributes
|
76
|
+
@attributes ||= {}
|
77
|
+
@node.attributes.each_key do |name|
|
78
|
+
ns, tg = parse_tag(name)
|
79
|
+
@attributes[tg] = { alias_name: tg, namespace: ns }.compact
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def parse_children
|
84
|
+
@children = []
|
85
|
+
@subclasses = []
|
86
|
+
@node.nodes.each do |node|
|
87
|
+
@children << node.name.to_s
|
88
|
+
@subclasses << XmlToClass.new.parse(node)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -2,8 +2,7 @@
|
|
2
2
|
|
3
3
|
$LOAD_PATH.push "#{Dir.pwd}/lib"
|
4
4
|
require 'goldendocx'
|
5
|
-
|
6
|
-
docx = Goldendocx::Docx.new("#{Dir.pwd}/demo/templates/blankDocument.docx")
|
5
|
+
docx = Goldendocx::Docx.new.read_from("#{Dir.pwd}/demo/templates/blankDocument.docx")
|
7
6
|
|
8
7
|
# Register styles because default document without any style
|
9
8
|
docx.add_style(File.read("#{Dir.pwd}/demo/templates/styles/reportTitle"))
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.push "#{Dir.pwd}/lib"
|
4
|
+
require 'goldendocx'
|
5
|
+
|
6
|
+
docx = Goldendocx::Docx.new
|
7
|
+
|
8
|
+
docx.create_text('Hello World!')
|
9
|
+
docx.create_text('Hello World!', align: :center, color: 'FF8533', bold: true)
|
10
|
+
|
11
|
+
filename = 'plainTextDocument.docx'
|
12
|
+
system "rm -f ~/Desktop/#{filename}" # -f so that we don't have an error if the file doesn't exist
|
13
|
+
docx.write_to File.expand_path("~/Desktop/#{filename}")
|
14
|
+
exec "open ~/Desktop/#{filename}"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
require 'active_support/core_ext/array/access'
|
5
|
+
require 'active_support/core_ext/class/attribute'
|
6
|
+
require 'active_support/core_ext/class/subclasses'
|
7
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
8
|
+
require 'active_support/core_ext/string/inflections'
|
9
|
+
require 'active_support/core_ext/string/conversions'
|
10
|
+
|
11
|
+
ActiveSupport::Inflector.inflections do |inflect|
|
12
|
+
inflect.uncountable 'extents', 'image_data', 'data', 'defaults', 'latentStyles'
|
13
|
+
inflect.irregular 'axis', 'axes'
|
14
|
+
|
15
|
+
inflect.uncountable 'values' # TODO: Find better names
|
16
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
# FIXME: Temporarily here to provider syntactic sugar
|
6
|
+
module Nokogiri
|
7
|
+
module XML
|
8
|
+
class Document
|
9
|
+
def tag_name
|
10
|
+
root.tag_name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Node
|
15
|
+
def <<(node_or_tags)
|
16
|
+
# FIXME: Add this line to transform element implicitly
|
17
|
+
node_or_tags = node_or_tags.public_send(:to_element, parent: self) if node_or_tags.respond_to?(:to_element)
|
18
|
+
|
19
|
+
add_child(node_or_tags)
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def adapt?(klass)
|
24
|
+
adaptable_classes = [String, Integer, Time]
|
25
|
+
is_a?(Nokogiri::XML::Text) && adaptable_classes.include?(klass)
|
26
|
+
end
|
27
|
+
|
28
|
+
def tag_name
|
29
|
+
[namespace&.prefix, name].compact.join(':')
|
30
|
+
end
|
31
|
+
|
32
|
+
def unparsed_children
|
33
|
+
@unparsed_children ||= children.dup
|
34
|
+
end
|
35
|
+
|
36
|
+
def attributes_hash
|
37
|
+
attribute_nodes.reject { |node| node.tag_name == 'mc:Ignorable' }.to_h { |node| [node.tag_name, node.value] }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ox'
|
4
|
+
|
5
|
+
module Ox
|
6
|
+
class Document
|
7
|
+
def root
|
8
|
+
children.first
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Element
|
13
|
+
def <<(node)
|
14
|
+
# FIXME: Add this line to transform element implicitly
|
15
|
+
node = node.public_send(:to_element) if node.respond_to?(:to_element)
|
16
|
+
|
17
|
+
raise 'argument to << must be a String or Ox::Node.' unless node.is_a?(String) || node.is_a?(Node)
|
18
|
+
|
19
|
+
@nodes = [] if !instance_variable_defined?(:@nodes) || @nodes.nil?
|
20
|
+
@nodes << node
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
alias children nodes
|
25
|
+
alias tag_name name
|
26
|
+
|
27
|
+
def unparsed_children
|
28
|
+
@unparsed_children ||= children.dup
|
29
|
+
end
|
30
|
+
|
31
|
+
def attributes_hash
|
32
|
+
attributes.with_indifferent_access.reject { |k, _| k.to_s.start_with?('xmlns') || k.to_s == 'mc:Ignorable' }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class String
|
4
|
+
class << self
|
5
|
+
def adapt?(xml_node)
|
6
|
+
xml_node.is_a?(String) || xml_node.adapt?(String)
|
7
|
+
end
|
8
|
+
|
9
|
+
def read_from(xml_node)
|
10
|
+
xml_node.to_s
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Time
|
16
|
+
class << self
|
17
|
+
def adapt?(xml_node)
|
18
|
+
xml_node.is_a?(String) || xml_node.adapt?(Time)
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_from(xml_node)
|
22
|
+
xml_node.to_s.to_time
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_element(**_context)
|
27
|
+
strftime('%Y-%m-%dT%H:%M:%SZ')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Integer
|
32
|
+
class << self
|
33
|
+
def adapt?(xml_node)
|
34
|
+
xml_node.is_a?(String) || xml_node.adapt?(Integer)
|
35
|
+
end
|
36
|
+
|
37
|
+
def read_from(xml_node)
|
38
|
+
xml_node.to_s.to_i
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_element(**_context)
|
43
|
+
to_s
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Goldendocx
|
4
|
+
module Components
|
5
|
+
module Properties
|
6
|
+
class FontProperty
|
7
|
+
include Goldendocx::Element
|
8
|
+
|
9
|
+
namespace :w
|
10
|
+
tag :rFonts
|
11
|
+
|
12
|
+
attribute :ascii, namespace: :w, default: 'Times New Roman'
|
13
|
+
attribute :east_asia, alias_name: :eastAsia, namespace: :w, default: '宋体'
|
14
|
+
attribute :high_ansi, alias_name: :hAnsi, namespace: :w, default: 'Times New Roman'
|
15
|
+
attribute :complex, alias_name: :cs, namespace: :w, default: 'Times New Roman'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Goldendocx
|
4
|
+
module Components
|
5
|
+
module Properties
|
6
|
+
class LanguageProperty
|
7
|
+
include Goldendocx::Element
|
8
|
+
|
9
|
+
namespace :w
|
10
|
+
tag :lang
|
11
|
+
|
12
|
+
attribute :complex, alias_name: :bidi, namespace: :w, default: 'ar-SA'
|
13
|
+
attribute :east_asia, alias_name: :eastAsia, namespace: :w, default: 'zh-CN'
|
14
|
+
attribute :latin, alias_name: :val, namespace: :w, default: 'en-US'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -11,6 +11,8 @@ module Goldendocx
|
|
11
11
|
|
12
12
|
embeds_one :color, class_name: 'Goldendocx::Components::Properties::ColorProperty', auto_build: false
|
13
13
|
embeds_one :bold, class_name: 'Goldendocx::Components::Properties::BoldProperty', auto_build: false
|
14
|
+
embeds_one :language, class_name: 'Goldendocx::Components::Properties::LanguageProperty', auto_build: false
|
15
|
+
embeds_one :font, class_name: 'Goldendocx::Components::Properties::FontProperty', auto_build: false
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
@@ -6,13 +6,9 @@ module Goldendocx
|
|
6
6
|
include Goldendocx::Element
|
7
7
|
|
8
8
|
tag :Default
|
9
|
-
attribute :extension, alias_name: :Extension, readonly: true
|
10
|
-
attribute :content_type, alias_name: :ContentType, readonly: true
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
@content_type = content_type
|
15
|
-
end
|
10
|
+
attribute :extension, alias_name: :Extension
|
11
|
+
attribute :content_type, alias_name: :ContentType
|
16
12
|
|
17
13
|
def ==(other)
|
18
14
|
extension == other.extension && content_type == other.content_type
|
@@ -6,13 +6,9 @@ module Goldendocx
|
|
6
6
|
include Goldendocx::Element
|
7
7
|
|
8
8
|
tag :Override
|
9
|
-
attribute :part_name, alias_name: :PartName, readonly: true
|
10
|
-
attribute :content_type, alias_name: :ContentType, readonly: true
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
@content_type = content_type
|
15
|
-
end
|
10
|
+
attribute :part_name, alias_name: :PartName
|
11
|
+
attribute :content_type, alias_name: :ContentType
|
16
12
|
|
17
13
|
def ==(other)
|
18
14
|
part_name == other.part_name && content_type == other.content_type
|
data/lib/goldendocx/document.rb
CHANGED
@@ -3,49 +3,30 @@
|
|
3
3
|
# Just to mark a element as a root document node
|
4
4
|
module Goldendocx
|
5
5
|
module Document
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
include Goldendocx::Element
|
8
|
+
|
9
|
+
included do
|
10
|
+
class_attribute :concerned_namespaces, default: []
|
11
|
+
class_attribute :ignorable_namespaces, default: []
|
9
12
|
end
|
10
13
|
|
11
|
-
|
14
|
+
class_methods do
|
12
15
|
def concern_namespaces(*namespaces)
|
13
16
|
namespaces.each do |namespace|
|
14
17
|
concerned_namespaces << namespace unless concerned_namespaces.include?(namespace)
|
15
18
|
end
|
16
19
|
end
|
17
20
|
|
18
|
-
def concerned_namespaces
|
19
|
-
@concerned_namespaces ||= []
|
20
|
-
end
|
21
|
-
|
22
21
|
def ignore_namespaces(*namespaces)
|
23
22
|
namespaces.each do |namespace|
|
24
23
|
ignorable_namespaces << namespace.to_sym unless ignorable_namespaces.include?(namespace)
|
25
24
|
end
|
26
25
|
end
|
27
|
-
|
28
|
-
def ignorable_namespaces
|
29
|
-
@ignorable_namespaces ||= []
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def concerned_namespaces
|
34
|
-
self.class.concerning_ancestors.flat_map(&:concerned_namespaces)
|
35
|
-
end
|
36
|
-
|
37
|
-
def ignorable_namespaces
|
38
|
-
self.class.concerning_ancestors.flat_map(&:ignorable_namespaces)
|
39
26
|
end
|
40
27
|
|
41
|
-
def to_document_xml
|
42
|
-
Goldendocx.xml_serializer.build_document_xml(
|
43
|
-
attributes.each { |name, value| xml[name] = value }
|
44
|
-
|
45
|
-
yield(xml) if block_given?
|
46
|
-
|
47
|
-
children.each { |child| xml << child }
|
48
|
-
end
|
28
|
+
def to_document_xml(&block)
|
29
|
+
Goldendocx.xml_serializer.build_document_xml(tag_name, concerned_namespaces, ignorable_namespaces) { |xml| build_element(xml, &block) }
|
49
30
|
end
|
50
31
|
end
|
51
32
|
end
|
@@ -7,33 +7,42 @@ module Goldendocx
|
|
7
7
|
|
8
8
|
XML_PATH = 'word/document.xml'
|
9
9
|
|
10
|
-
attr_reader :components, :
|
10
|
+
attr_reader :components, :charts
|
11
11
|
|
12
12
|
namespace :w
|
13
13
|
tag :body
|
14
14
|
|
15
|
+
# TODO: Try to distinguish these paragraphs
|
15
16
|
embeds_many :texts, class_name: 'Goldendocx::Components::Text'
|
16
17
|
embeds_many :images, class_name: 'Goldendocx::Components::Image'
|
18
|
+
|
17
19
|
embeds_many :tables, class_name: 'Goldendocx::Components::Table'
|
18
20
|
|
21
|
+
embeds_one :section_property, class_name: 'Goldendocx::Documents::Properties::SectionProperty', auto_build: true
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def read_from(xml_node)
|
25
|
+
document = super(xml_node)
|
26
|
+
|
27
|
+
component_tags = %w[w:p w:tbl]
|
28
|
+
xml_node.children.map do |node|
|
29
|
+
document.components << node if component_tags.include?(node.tag_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
document
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
19
36
|
def initialize
|
20
37
|
@components = []
|
21
38
|
@charts = []
|
22
39
|
end
|
23
40
|
|
24
|
-
def read_from(docx_file)
|
25
|
-
Goldendocx.xml_serializer.parse(docx_file.read(XML_PATH), %w[w:document w:body *]).map do |node|
|
26
|
-
element = Goldendocx::Documents::Element.new(node)
|
27
|
-
@components << element if element.component?
|
28
|
-
@properties = element if element.properties?
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
41
|
# FIXME: Override for children not in correctly order
|
33
42
|
def to_element(**_context)
|
34
|
-
Goldendocx.xml_serializer.build_element(
|
43
|
+
Goldendocx.xml_serializer.build_element(tag_name) do |xml|
|
35
44
|
components.each { |component| xml << component }
|
36
|
-
xml <<
|
45
|
+
xml << section_property if section_property
|
37
46
|
|
38
47
|
yield(xml) if block_given?
|
39
48
|
end
|
@@ -5,7 +5,9 @@ module Goldendocx
|
|
5
5
|
class Document
|
6
6
|
include Goldendocx::Document
|
7
7
|
|
8
|
+
TYPE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument'
|
8
9
|
XML_PATH = 'word/document.xml'
|
10
|
+
CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml'
|
9
11
|
|
10
12
|
namespace :w
|
11
13
|
tag :document
|
@@ -16,10 +18,6 @@ module Goldendocx
|
|
16
18
|
|
17
19
|
embeds_one :body, class_name: 'Goldendocx::Documents::Body', auto_build: true
|
18
20
|
|
19
|
-
def read_from(docx_file)
|
20
|
-
body.read_from(docx_file)
|
21
|
-
end
|
22
|
-
|
23
21
|
def write_to(zos)
|
24
22
|
zos.put_next_entry XML_PATH
|
25
23
|
zos.write to_document_xml
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Goldendocx
|
4
|
+
module Documents
|
5
|
+
module Properties
|
6
|
+
class DefaultStyleProperty
|
7
|
+
include Goldendocx::Element
|
8
|
+
|
9
|
+
namespace :w
|
10
|
+
tag :docDefaults
|
11
|
+
|
12
|
+
embeds_one :run_default, class_name: 'Goldendocx::Documents::Properties::RunDefaultStyleProperty', auto_build: true
|
13
|
+
embeds_one :paragraph_default, class_name: 'Goldendocx::Documents::Properties::ParagraphDefaultStyleProperty', auto_build: true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|