docxtor2 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/.wrong +1 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +7 -0
- data/TODO.md +2 -0
- data/docxtor2.gemspec +32 -0
- data/lib/docxtor2.rb +39 -0
- data/lib/docxtor2/block_evaluator.rb +13 -0
- data/lib/docxtor2/constants.rb +4 -0
- data/lib/docxtor2/content_builder.rb +29 -0
- data/lib/docxtor2/document_builder.rb +19 -0
- data/lib/docxtor2/element_list.rb +26 -0
- data/lib/docxtor2/generator.rb +12 -0
- data/lib/docxtor2/known/aliases.rb +11 -0
- data/lib/docxtor2/known/mappings.rb +31 -0
- data/lib/docxtor2/known/parts.rb +9 -0
- data/lib/docxtor2/known/path.rb +11 -0
- data/lib/docxtor2/known/styles.rb +10 -0
- data/lib/docxtor2/known/templates.rb +7 -0
- data/lib/docxtor2/object_utils.rb +7 -0
- data/lib/docxtor2/package.rb +33 -0
- data/lib/docxtor2/package/document.rb +28 -0
- data/lib/docxtor2/package/document/element.rb +89 -0
- data/lib/docxtor2/package/document/heading.rb +11 -0
- data/lib/docxtor2/package/document/page_break.rb +16 -0
- data/lib/docxtor2/package/document/paragraph.rb +90 -0
- data/lib/docxtor2/package/document/table_of_contents.rb +90 -0
- data/lib/docxtor2/package/part.rb +10 -0
- data/lib/docxtor2/template_parser.rb +42 -0
- data/lib/docxtor2/version.rb +3 -0
- data/spec/docxtor2/content_builder_spec.rb +24 -0
- data/spec/docxtor2/docxtor2_spec.rb +13 -0
- data/spec/docxtor2/package/document/element_spec.rb +20 -0
- data/spec/docxtor2/package/document/heading_spec.rb +9 -0
- data/spec/docxtor2/package/document/paragraph_spec.rb +56 -0
- data/spec/docxtor2/package/document/table_of_contents_spec.rb +18 -0
- data/spec/docxtor2/package/document_spec.rb +13 -0
- data/spec/docxtor2/package/part_spec.rb +16 -0
- data/spec/docxtor2/package_spec.rb +37 -0
- data/spec/docxtor2/prerequisites_spec.rb +9 -0
- data/spec/docxtor2/support/contexts/integration_context.rb +6 -0
- data/spec/docxtor2/support/contexts/xml_builder_context.rb +11 -0
- data/spec/docxtor2/support/examples/.gitkeep +0 -0
- data/spec/docxtor2/support/matchers/wordprocessingml_matchers.rb +42 -0
- data/spec/docxtor2/support/matchers/xpath_matchers.rb +46 -0
- data/spec/docxtor2/template_parser_spec.rb +18 -0
- data/spec/spec_helper.rb +35 -0
- data/templates/.gitkeep +0 -0
- data/templates/default/[Content_Types].xml +16 -0
- data/templates/default/_rels/.rels +4 -0
- data/templates/default/word/_rels/document.xml.rels +13 -0
- data/templates/default/word/endnotes.xml +17 -0
- data/templates/default/word/fontTable.xml +31 -0
- data/templates/default/word/footer1.xml +35 -0
- data/templates/default/word/footer2.xml +48 -0
- data/templates/default/word/footnotes.xml +17 -0
- data/templates/default/word/numbering.xml +212 -0
- data/templates/default/word/settings.xml +107 -0
- data/templates/default/word/styles.xml +368 -0
- data/templates/default/word/theme/theme1.xml +2 -0
- data/templates/default/word/websettings.xml +4 -0
- metadata +212 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
module Docxtor2
|
2
|
+
class Package
|
3
|
+
attr_reader :parts
|
4
|
+
|
5
|
+
def initialize(parts, document)
|
6
|
+
@parts = parts
|
7
|
+
@parts[Known::Parts::DOCUMENT] = document
|
8
|
+
end
|
9
|
+
|
10
|
+
def save(filepath)
|
11
|
+
Zip::ZipOutputStream.open(filepath) do |ostream|
|
12
|
+
write_parts(ostream)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_stream
|
17
|
+
ostream = Zip::ZipOutputStream.new("streamed", true)
|
18
|
+
write_parts(ostream)
|
19
|
+
string_io = ostream.close_buffer
|
20
|
+
string_io.rewind
|
21
|
+
string_io
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def write_parts(ostream)
|
27
|
+
@parts.each do |name, part|
|
28
|
+
ostream.put_next_entry(part.filename)
|
29
|
+
ostream.puts part.content
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Docxtor2
|
2
|
+
class Package::Document < Package::Part
|
3
|
+
def initialize(content, filepath = DOCUMENT_XML_PATH)
|
4
|
+
super(filepath, self.class.render(content))
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.render(content)
|
8
|
+
xml = Builder::XmlMarkup.new
|
9
|
+
|
10
|
+
xml.instruct! :xml, :version => "1.0", :encoding => "UTF-8", :standalone => "yes"
|
11
|
+
xml.w :document, "xmlns:ve" => "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
12
|
+
"xmlns:o" => "urn:schemas-microsoft-com:office:office",
|
13
|
+
"xmlns:r" => "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
14
|
+
"xmlns:m" => "http://schemas.openxmlformats.org/officeDocument/2006/math",
|
15
|
+
"xmlns:v" => "urn:schemas-microsoft-com:vml",
|
16
|
+
"xmlns:wp" => "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
17
|
+
"xmlns:w10" => "urn:schemas-microsoft-com:office:word",
|
18
|
+
"xmlns:w" => "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
19
|
+
"xmlns:wne" => "http://schemas.microsoft.com/office/word/2006/wordml" do
|
20
|
+
xml.w :body do
|
21
|
+
xml << content
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
xml.target!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Docxtor2
|
2
|
+
class Package::Document::Element < ElementList
|
3
|
+
include BlockEvaluator
|
4
|
+
include ObjectUtils
|
5
|
+
|
6
|
+
def initialize(*args, &block)
|
7
|
+
@params = create_attributes(args)
|
8
|
+
@block = block
|
9
|
+
super()
|
10
|
+
end
|
11
|
+
|
12
|
+
def render(xml)
|
13
|
+
@xml = xml
|
14
|
+
evaluate &@block
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def mappings
|
20
|
+
{}
|
21
|
+
end
|
22
|
+
|
23
|
+
def aliases
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
|
27
|
+
def write_element(name, &block)
|
28
|
+
@xml.tag!("w:#{name}") do
|
29
|
+
write_properties(name)
|
30
|
+
evaluate &block
|
31
|
+
write_elements(@xml)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def write_properties(el)
|
36
|
+
@properties = get_properties_for(el)
|
37
|
+
unless @properties.nil?
|
38
|
+
@xml.tag!("w:#{el}Pr") do
|
39
|
+
@properties.each { |k, v| write_property(k, v) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def write_property(key, val)
|
45
|
+
if self_closing? val
|
46
|
+
@xml.tag!("w:#{key}")
|
47
|
+
elsif !val.nil?
|
48
|
+
if val.is_a?(Hash) && !val.empty?
|
49
|
+
@xml.tag!("w:#{key}", prefixize(val))
|
50
|
+
elsif !val.to_s.empty?
|
51
|
+
@xml.tag!("w:#{key}", 'w:val' => val)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def create_attributes(args)
|
59
|
+
hash = find_argument(args, Hash, {})
|
60
|
+
pairs = hash.map do |k, v|
|
61
|
+
aliases.key?(k) ? [aliases[k], v] : [k, v]
|
62
|
+
end
|
63
|
+
Hash[pairs]
|
64
|
+
end
|
65
|
+
|
66
|
+
def self_closing?(val)
|
67
|
+
!!val == val && val
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_properties_for(el)
|
71
|
+
map = mappings[el]
|
72
|
+
unless map.nil?
|
73
|
+
pairs = @params.
|
74
|
+
reject { |k, v| !map.key?(k) }.
|
75
|
+
map { |k, v|
|
76
|
+
element = map[k]
|
77
|
+
element.is_a?(Hash) ?
|
78
|
+
[element[:name], v] :
|
79
|
+
[element, v]
|
80
|
+
}
|
81
|
+
Hash[pairs]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def prefixize(attrs)
|
86
|
+
Hash[attrs.map { |k, v| ["w:#{k}", v] }]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Docxtor2
|
2
|
+
class Package::Document::PageBreak < Package::Document::Element
|
3
|
+
def initialize(*args, &block)
|
4
|
+
super(*args, &block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def render(xml)
|
8
|
+
super(xml)
|
9
|
+
write_element(:p) do
|
10
|
+
write_element(:r) do
|
11
|
+
@xml.w :br, 'w:type' => 'page'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Docxtor2
|
2
|
+
class Package::Document::Paragraph < Package::Document::Element
|
3
|
+
def initialize(*args, &block)
|
4
|
+
super(*args, &block)
|
5
|
+
Known::Mappings::PARAGRAPH_COMPLEX.each do |name, element|
|
6
|
+
@params[name] ||= {}
|
7
|
+
end
|
8
|
+
@params[:space] ||= 'default'
|
9
|
+
@contents = create_contents(args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def render(xml)
|
13
|
+
super(xml)
|
14
|
+
write_element(:p) do
|
15
|
+
write_element(:r) do
|
16
|
+
write_contents
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Known::Mappings::PARAGRAPH_SIMPLE.each do |name, element|
|
22
|
+
define_method(name) { |val| @params[name] = val }
|
23
|
+
end
|
24
|
+
Known::Mappings::RUN.each do |name, element|
|
25
|
+
define_method(name) { @params[name] = true }
|
26
|
+
end
|
27
|
+
Known::Mappings::PARAGRAPH_COMPLEX.each do |name, element|
|
28
|
+
define_method(name) { |attrs| @params[name].merge!(attrs) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def line_break
|
32
|
+
@contents << :br
|
33
|
+
end
|
34
|
+
|
35
|
+
def preserve_whitespace
|
36
|
+
@params[:space] = 'preserve'
|
37
|
+
end
|
38
|
+
|
39
|
+
def write(text)
|
40
|
+
@contents << text
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
def mappings
|
46
|
+
super.merge({
|
47
|
+
:p => Known::Mappings::PARAGRAPH,
|
48
|
+
:r => Known::Mappings::RUN
|
49
|
+
})
|
50
|
+
end
|
51
|
+
|
52
|
+
def aliases
|
53
|
+
super.merge(Known::Aliases::PARAGRAPH)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def write_contents
|
59
|
+
@contents.each { |c| write_content(c) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def write_content(content)
|
63
|
+
content == :br ?
|
64
|
+
write_line_break :
|
65
|
+
write_text(content)
|
66
|
+
end
|
67
|
+
|
68
|
+
def write_line_break
|
69
|
+
@xml.w :br
|
70
|
+
end
|
71
|
+
|
72
|
+
def write_text(text)
|
73
|
+
@xml.w :t, 'xml:space' => @params[:space] do
|
74
|
+
@xml.text! text
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def create_contents(args)
|
79
|
+
str = find_argument(args, String)
|
80
|
+
str.nil? ? [] : [str]
|
81
|
+
end
|
82
|
+
|
83
|
+
alias_method :b, :bold
|
84
|
+
alias_method :i, :italic
|
85
|
+
alias_method :u, :underline
|
86
|
+
alias_method :br, :line_break
|
87
|
+
alias_method :w, :write
|
88
|
+
alias_method :ind, :indent
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Docxtor2
|
2
|
+
class Package::Document::TableOfContents < Package::Document::Element
|
3
|
+
def initialize(text, &block)
|
4
|
+
super({ :style => Known::Styles::TOC }, &block)
|
5
|
+
@text = text
|
6
|
+
end
|
7
|
+
|
8
|
+
# TODO: Add support for extended styling & properties
|
9
|
+
|
10
|
+
def render(xml)
|
11
|
+
super(xml)
|
12
|
+
|
13
|
+
@xml.w :sdt, "xmlns:w" => "http://schemas.openxmlformats.org/wordprocessingml/2006/main" do
|
14
|
+
|
15
|
+
write_properties
|
16
|
+
|
17
|
+
@xml.w :sdtContent do
|
18
|
+
|
19
|
+
write_heading
|
20
|
+
|
21
|
+
@xml.w :p do
|
22
|
+
@xml.w :pPr do
|
23
|
+
@xml.w :pStyle, "w:val" => Known::Styles::TOC_PARAGRAPH
|
24
|
+
@xml.w :tabs do
|
25
|
+
@xml.w :tab, "w:val" => "right", "w:leader" => "dot", "w:pos" => 9350
|
26
|
+
end
|
27
|
+
@xml.w :rPr do
|
28
|
+
@xml.w :noProof
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@xml.w :r do
|
32
|
+
@xml.w :fldChar, "w:fldCharType" => "begin", "w:dirty" => true
|
33
|
+
end
|
34
|
+
|
35
|
+
write_instruct
|
36
|
+
|
37
|
+
@xml.w :r do
|
38
|
+
@xml.w :fldChar, "w:fldCharType" => "separate"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@xml.w :p do
|
43
|
+
@xml.w :r do
|
44
|
+
# @xml.w :rPr do
|
45
|
+
# @xml.w :b
|
46
|
+
# @xml.w :bCs
|
47
|
+
# @xml.w :noProof
|
48
|
+
# end
|
49
|
+
@xml.w :fldChar, "w:fldCharType" => "end"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def write_properties
|
60
|
+
@xml.w :sdtPr do
|
61
|
+
@xml.w :docPartObj do
|
62
|
+
@xml.w :docPartGallery, "w:val" => "Table of Contents"
|
63
|
+
@xml.w :docPartUnique
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def write_heading
|
69
|
+
@xml.w :p do
|
70
|
+
@xml.w :pPr do
|
71
|
+
@xml.w :pStyle, "w:val" => @params[:style]
|
72
|
+
end
|
73
|
+
@xml.w :r do
|
74
|
+
@xml.w :t do
|
75
|
+
@xml.text! @text
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def write_instruct
|
82
|
+
@xml.w :r do
|
83
|
+
@xml.w :instrText, "xml:space" => "preserve" do
|
84
|
+
@xml.text! " TOC \\o '1-3' \\h \\z \\u "
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Docxtor2
|
2
|
+
class TemplateParser
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def parse(template)
|
6
|
+
instance = new(template)
|
7
|
+
instance.parts
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(template)
|
12
|
+
@parts = parse(template)
|
13
|
+
end
|
14
|
+
|
15
|
+
def parts
|
16
|
+
@parts
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def parse(template)
|
22
|
+
Dir.chdir(template) do
|
23
|
+
Hash[create_parts]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_parts
|
28
|
+
Dir[SEARCH_PATTERN].
|
29
|
+
delete_if { |file| File.directory?(file) }.
|
30
|
+
map { |file| create_part(file) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_part(file)
|
34
|
+
content = File.read(file)
|
35
|
+
part = Package::Part.new(file, content)
|
36
|
+
key = File.basename(file, '.xml')
|
37
|
+
|
38
|
+
[key, part]
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Docxtor2
|
4
|
+
describe ContentBuilder do
|
5
|
+
include WordprocessingMLMatchers
|
6
|
+
|
7
|
+
subject {
|
8
|
+
ContentBuilder.build do
|
9
|
+
table_of_contents 'Contents'
|
10
|
+
p :style => 3 do
|
11
|
+
write "content 1"
|
12
|
+
line_break
|
13
|
+
write "content 2"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
}
|
17
|
+
|
18
|
+
it 'renders given DSL instructions' do
|
19
|
+
subject.should contain_paragraph_text
|
20
|
+
subject.should contain_element_style(:p)
|
21
|
+
subject.should contain_table_of_contents
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|