docxtor2 0.0.9
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 +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
|