docx-templater 0.0.6

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.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YjFlNTJkZDEzOTQyZWViMDJmMzVjNTljMjdkYmM0ODgxZDYxMjlkZQ==
5
+ data.tar.gz: !binary |-
6
+ ZTZjYTAwODdmMGM1ZGQzMGFmYTA3M2VjODAxYjQ2YjM2ODcxNTUxMQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ M2M0ZWUyMzI4ZWEwNGYzMjFmMThmY2ExNDRlMmIxODRkOGU4NmNkYWU1MDYw
10
+ OWI1ZGZjNmQ1NjQxZTljODAxYjc1Mzk2M2JlZDVlMDdlOTdmZDQxNzc3MTVl
11
+ NGNiMjBhOTZmYzcxZTg1YTFlNDQxNjI3OWY4M2ZhY2FjOTdjZWY=
12
+ data.tar.gz: !binary |-
13
+ MjU1OWNjOTkzNDMxNWNiYTY4MmQxM2NiMzFkMDBmNTk4ZWY5NDhjZGFjZWRk
14
+ ODcyYjYxMGQ3M2ZiNGQ4NTFlNGYxYWE0NGYzNTAzOTc3ZjI2Y2M0YWJhYTYz
15
+ MmMzNTgzNzRkMzQ5ZmEyZjJhODBiZDMwYjQwOTJiOTg1ODM0ZWQ=
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in docx-templater.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 DmitryKa
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,38 @@
1
+ # DocxTemplater
2
+
3
+ PURE RUBY GEM (works on Windows)! I tried use ready solutions, but some of them hasn't neccessary functions, other one didn't work in Win.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'docx-templater2'
10
+
11
+ And then execute:
12
+
13
+ $ bundle install
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install docx_templater
18
+
19
+ ## Usage
20
+
21
+ Can work with:
22
+ - $SOME_VALUE$ : replace template stub (if value for replacing will be provided);
23
+ - $REPEAT:SOME_COLLECTION$
24
+ (at least, one new row)
25
+ $EACH:SOME_VALUE_OF_COLLECTION_ITEM$
26
+ (at least, one new row)
27
+ $UNREPEAT:SOME_COLLECTION$;
28
+
29
+ Repeating commands must be at the separate paragraphs.
30
+ Note, that created template may look good but have bad structure for templater, so you should open docx as a zip archive, open "word/document.xml" and edit it
31
+
32
+ ## Contributing
33
+
34
+ 1. Fork it
35
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
36
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
37
+ 4. Push to the branch (`git push origin my-new-feature`)
38
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,6 @@
1
+ require "docx/version"
2
+ require 'docx/docx_handler'
3
+
4
+
5
+ module Docx
6
+ end
@@ -0,0 +1,49 @@
1
+ require 'zip'
2
+ require 'nokogiri'
3
+ require 'docx/template_handler'
4
+
5
+ module Docx
6
+ class DocxHandler
7
+ include TemplateHandler
8
+
9
+ DOC_XML_ARCHIVE_PATH = 'word/document.xml'
10
+
11
+ def self.open(path, &block)
12
+ self.new(path, &block)
13
+ end
14
+
15
+ def initialize(path, &block)
16
+ @replace = {}
17
+ if block_given?
18
+ @zip = Zip::File.open(path)
19
+ yield(self)
20
+ @zip.close
21
+ else
22
+ @zip = Zip::File.open(path)
23
+ end
24
+ end
25
+
26
+ def insert(arg_hash)
27
+ raise TypeError unless arg_hash.kind_of? Hash
28
+ xml = @zip.read(DOC_XML_ARCHIVE_PATH)
29
+ into_doc = Nokogiri::XML(xml) { |x| x.noent } #TODO what is noent?
30
+ TemplateHandler::insert arg_hash, into_doc
31
+ @replace[DOC_XML_ARCHIVE_PATH] = into_doc.serialize save_with: 0
32
+ end
33
+
34
+ def save(path)
35
+ Zip::File.open(path, Zip::File::CREATE) do |out|
36
+ @zip.each do |entry|
37
+ out.get_output_stream(entry.name) do |o|
38
+ if @replace[entry.name]
39
+ o.write(@replace[entry.name])
40
+ else
41
+ o.write(@zip.read(entry.name))
42
+ end
43
+ end
44
+ end
45
+ end
46
+ @zip.close
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,161 @@
1
+ module Docx
2
+ module TemplateHandler
3
+
4
+ START = 'REPEAT:'
5
+ FINISH = 'UNREPEAT:'
6
+ EACH = 'EACH:'
7
+ PARAGRAPH_XPATH = '//w:p'
8
+ TEXT_NODE_XPATH = './/w:t'
9
+ BUX = '$'
10
+ COLON = ':'
11
+
12
+ PR = 'Pr'
13
+ P = 'p'
14
+ W = 'w'
15
+
16
+ # Now supported only one-depth cycles and cycle begin/end must be in the separate rows
17
+ def self.insert(hash, doc)
18
+ paragraphs = doc.xpath(PARAGRAPH_XPATH)
19
+ paragraphs.each do |paragraph|
20
+ text_nodes = paragraph.xpath(TEXT_NODE_XPATH)
21
+ text_nodes.each do |text_node|
22
+ value = text_node.content
23
+ next unless value[0] == BUX && value[-1] == BUX
24
+ key = value[1..-2]
25
+ if key.include? COLON
26
+ if key.start_with? START
27
+ key.sub! START, FINISH
28
+ fin_value = value.sub START, FINISH
29
+ paragraph, repeat = bound_repeatable_piece(text_node, fin_value)
30
+ elems_for_repeating = key.sub FINISH, ''
31
+ if hash[elems_for_repeating] && (hash[elems_for_repeating].kind_of? Enumerable)
32
+ hash[elems_for_repeating].each do |repeated_elem|
33
+ repeat.each do |repeatable_paragraph|
34
+ dup_par = repeatable_paragraph.dup
35
+ handle_paragraph dup_par, repeated_elem
36
+ paragraph.add_next_sibling dup_par
37
+ paragraph = dup_par
38
+ end
39
+ end
40
+ end
41
+ repeat.each do |par|
42
+ par.remove
43
+ end
44
+ break
45
+ else
46
+ vars = key.split COLON
47
+ obj = hash[vars[0]]
48
+ text_node.content = if obj.is_a? Hash
49
+ obj[vars[1]]
50
+ else
51
+ begin
52
+ obj.send vars[1]
53
+ rescue NoMethodError => e
54
+ value
55
+ end
56
+ end
57
+ end
58
+ else
59
+ text_node.content = hash[key] || value
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+
68
+ def self.handle_paragraph(paragraph, hash)
69
+ paragraph.xpath(TEXT_NODE_XPATH).each do |text_node|
70
+ value = text_node.content
71
+ next unless value[0] == BUX && value[-1] == BUX
72
+ key = value[1..-2]
73
+ if key.include? COLON
74
+ if key.start_with? EACH
75
+ key.sub! EACH, ''
76
+ text_node.content = hash[key] || value
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def self.bound_repeatable_piece(node, fin_value)
83
+ # Tag name consists from prefix (usually, 'w') and name (i.e. 'r', 'rPr' and so on)
84
+ temp_key = paragraph_containing node
85
+ paragraph = temp_key.next_sibling
86
+ temp_key.remove
87
+ #next unless paragraph
88
+ repeat = []
89
+ while true
90
+ result = find_finish paragraph, fin_value
91
+ #if result.kind_of? Array
92
+ # new_children = Nokogiri::XML::NodeSet.new paragraph.document
93
+ # result.each do |node|
94
+ # new_children.push node
95
+ # end
96
+ # paragraph.children = new_children
97
+ # repeat.push paragraph
98
+ # break
99
+ #end
100
+ unless result
101
+ # Move 'paragraph' back and remove ending delimiter
102
+ temp_key = paragraph
103
+ paragraph = paragraph.previous_sibling
104
+ temp_key.remove
105
+ break
106
+ end
107
+ #paragraph.xpath('.//w:t')
108
+ repeat.push result
109
+ paragraph = paragraph.next_sibling
110
+ break unless paragraph
111
+ end
112
+ return paragraph, repeat
113
+ end
114
+
115
+ def self.find_finish(node, finish_str)
116
+
117
+ # Now think that repeating will be bounded by paragraphs
118
+
119
+ return node if node.name.end_with? PR
120
+ children = []
121
+ node.children.each do |child|
122
+ ## Be careful, result = nil in the case, when we found finish_str
123
+ #result = find_finish child, finish_str
124
+ #unless result # '$FINISH:...$' returns nil
125
+ # return nil if children.empty? # <w:t>$...$</w:t> returns nil here
126
+ # return nil if children.size == 1 && node.name+PR == children.first.name # <w:p><w:pPr>...</w:pPr><w:t>...</w:t> returns nil here
127
+ # return children
128
+ #else
129
+ # # If node is returned, then... If array, then...
130
+ # if result.kind_of? Nokogiri::XML::Node
131
+ # children.push child
132
+ # elsif result.kind_of? Array
133
+ # new_children = Nokogiri::XML::NodeSet.new child.document
134
+ # result.each do |res_node|
135
+ # new_children.push res_node
136
+ # end
137
+ # child.children = new_children
138
+ # children.push child
139
+ # return children
140
+ # end
141
+ #end
142
+ end
143
+
144
+ # WARNING: It seems, situation exists, when finish_str willn't contain in the children but will be in the node
145
+ if node.content == finish_str
146
+ return nil
147
+ else
148
+ return node
149
+ end
150
+ end
151
+
152
+
153
+ def self.paragraph_containing node
154
+ return nil unless node.namespace.prefix == W
155
+ return node if node.name == P
156
+ while (node = node.parent).name != P
157
+ end
158
+ node
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,3 @@
1
+ module Docx
2
+ VERSION = "0.0.6"
3
+ end
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
+ <w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 wp14"><w:body><w:p w:rsidR="003E7B44" w:rsidRDefault="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t xml:space="preserve">Dear </w:t></w:r><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$NAME$</w:t></w:r><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>!</w:t></w:r></w:p><w:p w:rsidR="00F622F5" w:rsidRDefault="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>You want to know my progress, so:</w:t></w:r></w:p><w:p w:rsidR="00F622F5" w:rsidRDefault="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$REPEAT:ELEMENTS$</w:t></w:r></w:p><w:p w:rsidR="00F622F5" w:rsidRDefault="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$EACH:INDEX$</w:t></w:r><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t xml:space="preserve">. </w:t></w:r><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$EACH:NAME$</w:t></w:r><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t xml:space="preserve"> - </w:t></w:r><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$EACH:NUMBER$</w:t></w:r></w:p><w:p w:rsidR="00F622F5" w:rsidRDefault="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$EACH:COMMENTS$</w:t></w:r></w:p><w:p w:rsidR="00F622F5" w:rsidRPr="00F622F5" w:rsidRDefault="00F622F5" w:rsidP="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$UNREPEAT:ELEMENTS$</w:t></w:r></w:p><w:p w:rsidR="00F622F5" w:rsidRPr="00F622F5" w:rsidRDefault="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>Good luck!</w:t></w:r></w:p><w:sectPr w:rsidR="00F622F5" w:rsidRPr="00F622F5"><w:pgSz w:w="11906" w:h="16838"/><w:pgMar w:top="1134" w:right="850" w:bottom="1134" w:left="1701" w:header="708" w:footer="708" w:gutter="0"/><w:cols w:space="708"/><w:docGrid w:linePitch="360"/></w:sectPr></w:body></w:document>
@@ -0,0 +1,14 @@
1
+ require 'docx'
2
+
3
+ SRC_FILE = 'template.docx'
4
+ DST_FILE = 'ready.docx'
5
+
6
+ # Note, in second element 'COMMENTS' is absent. Original stub in template file will be remainded
7
+ hash = {'NAME' => 'Ann', 'ELEMENTS' => [
8
+ { 'NAME' => 'World history', 'INDEX' => '1','NUMBER' => '3', 'COMMENTS' => "Yeah, I'm so stupid in it"},
9
+ {'INDEX' => '2', 'NAME' => 'Russian language', 'NUMBER' => '5'}
10
+ ]}
11
+
12
+ w = Docx::DocxHandler.open(SRC_FILE)
13
+ w.insert(hash)
14
+ w.save(DST_FILE)
Binary file
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: docx-templater
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6
5
+ platform: ruby
6
+ authors:
7
+ - DmitryKa
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.6.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.6.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubyzip
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.0
69
+ description: ! 'PURE RUBY GEM (works on Windows)! Can work with:
70
+
71
+ - $SOME_VALUE$;
72
+
73
+ - $REPEAT:SOME_COLLECTION$ (at least, new row) $EACH:SOME_VALUE_OF_COLLECTION_ITEM$
74
+ (at least, new row) $UNREPEAT:SOME_COLLECTION$;
75
+
76
+ Repeating commands must be at the separate paragraphs.
77
+
78
+ Note, that created template may look good but have bad structure for templater,
79
+ so you should open docx as a zip archive, open "word/document.xml" and edit it'
80
+ email:
81
+ - mail.of.dima.ka@gmail.com
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - lib/docx.rb
92
+ - lib/docx/docx_handler.rb
93
+ - lib/docx/template_handler.rb
94
+ - lib/docx/version.rb
95
+ - lib/example/document.xml
96
+ - lib/example/example.rb
97
+ - lib/example/template.docx
98
+ homepage: ''
99
+ licenses:
100
+ - MIT
101
+ metadata: {}
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ! '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.0.7
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Simple docx templater
122
+ test_files: []