docxtor 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.coveralls.yml +1 -0
  2. data/.gitignore +21 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +4 -0
  6. data/.wrong +1 -0
  7. data/Gemfile +18 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +117 -0
  10. data/Rakefile +7 -0
  11. data/TODO.md +5 -0
  12. data/docxtor.gemspec +32 -0
  13. data/lib/docxtor.rb +30 -0
  14. data/lib/docxtor/document/builder.rb +32 -0
  15. data/lib/docxtor/document/element.rb +96 -0
  16. data/lib/docxtor/document/heading.rb +13 -0
  17. data/lib/docxtor/document/page_break.rb +14 -0
  18. data/lib/docxtor/document/paragraph.rb +125 -0
  19. data/lib/docxtor/document/root.rb +17 -0
  20. data/lib/docxtor/document/table_of_contents.rb +95 -0
  21. data/lib/docxtor/generator.rb +13 -0
  22. data/lib/docxtor/package/builder.rb +35 -0
  23. data/lib/docxtor/package/part.rb +12 -0
  24. data/lib/docxtor/template_parser.rb +38 -0
  25. data/lib/docxtor/version.rb +3 -0
  26. data/spec/docxtor/document/builder_spec.rb +16 -0
  27. data/spec/docxtor/document/heading_spec.rb +9 -0
  28. data/spec/docxtor/document/paragraph_spec.rb +56 -0
  29. data/spec/docxtor/document/root_spec.rb +20 -0
  30. data/spec/docxtor/document/table_of_contents_spec.rb +18 -0
  31. data/spec/docxtor/docxtor_spec.rb +13 -0
  32. data/spec/docxtor/package/builder_spec.rb +37 -0
  33. data/spec/docxtor/package/part_spec.rb +16 -0
  34. data/spec/docxtor/prerequisites_spec.rb +9 -0
  35. data/spec/docxtor/support/contexts/integration_context.rb +6 -0
  36. data/spec/docxtor/support/contexts/xml_builder_context.rb +11 -0
  37. data/spec/docxtor/support/examples/.gitkeep +0 -0
  38. data/spec/docxtor/support/matchers/wordprocessingml_matchers.rb +42 -0
  39. data/spec/docxtor/support/matchers/xpath_matchers.rb +46 -0
  40. data/spec/docxtor/template_parser_spec.rb +18 -0
  41. data/spec/spec_helper.rb +35 -0
  42. data/templates/.gitkeep +0 -0
  43. data/templates/default/[Content_Types].xml +16 -0
  44. data/templates/default/_rels/.rels +4 -0
  45. data/templates/default/word/_rels/document.xml.rels +13 -0
  46. data/templates/default/word/endnotes.xml +17 -0
  47. data/templates/default/word/fontTable.xml +31 -0
  48. data/templates/default/word/footer1.xml +35 -0
  49. data/templates/default/word/footer2.xml +48 -0
  50. data/templates/default/word/footnotes.xml +17 -0
  51. data/templates/default/word/numbering.xml +212 -0
  52. data/templates/default/word/settings.xml +107 -0
  53. data/templates/default/word/styles.xml +368 -0
  54. data/templates/default/word/theme/theme1.xml +2 -0
  55. data/templates/default/word/websettings.xml +4 -0
  56. metadata +265 -0
@@ -0,0 +1,17 @@
1
+ module Docxtor
2
+ module Document
3
+ class Root < Element
4
+ map({ :table_of_contents => Document::TableOfContents,
5
+ :p => Document::Paragraph,
6
+ :h => Document::Heading,
7
+ :page_break => Document::PageBreak
8
+ })
9
+
10
+ def render(xml)
11
+ super
12
+ write_elements
13
+ xml.target!
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,95 @@
1
+ module Docxtor
2
+ module Document
3
+ class TableOfContents < Element
4
+ STYLE = 'defprnRUSSelStyle'
5
+ PARAGRAPH_STYLE = '10'
6
+
7
+ def after_initialize(text)
8
+ create_params({:style => STYLE})
9
+ @text = text
10
+ end
11
+
12
+ # TODO: Add support for extended styling & properties
13
+
14
+ def render(xml)
15
+ super
16
+
17
+ xml.w :sdt, "xmlns:w" => "http://schemas.openxmlformats.org/wordprocessingml/2006/main" do
18
+
19
+ write_properties
20
+
21
+ xml.w :sdtContent do
22
+
23
+ write_heading
24
+
25
+ xml.w :p do
26
+ xml.w :pPr do
27
+ xml.w :pStyle, "w:val" => PARAGRAPH_STYLE
28
+ xml.w :tabs do
29
+ xml.w :tab, "w:val" => "right", "w:leader" => "dot", "w:pos" => 9350
30
+ end
31
+ xml.w :rPr do
32
+ xml.w :noProof
33
+ end
34
+ end
35
+ xml.w :r do
36
+ xml.w :fldChar, "w:fldCharType" => "begin", "w:dirty" => true
37
+ end
38
+
39
+ write_instruct
40
+
41
+ xml.w :r do
42
+ xml.w :fldChar, "w:fldCharType" => "separate"
43
+ end
44
+ end
45
+
46
+ xml.w :p do
47
+ xml.w :r do
48
+ # xml.w :rPr do
49
+ # xml.w :b
50
+ # xml.w :bCs
51
+ # xml.w :noProof
52
+ # end
53
+ xml.w :fldChar, "w:fldCharType" => "end"
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def write_properties
64
+ xml.w :sdtPr do
65
+ xml.w :docPartObj do
66
+ xml.w :docPartGallery, "w:val" => "Table of Contents"
67
+ xml.w :docPartUnique
68
+ end
69
+ end
70
+ end
71
+
72
+ def write_heading
73
+ xml.w :p do
74
+ xml.w :pPr do
75
+ xml.w :pStyle, "w:val" => @params[:style]
76
+ end
77
+ xml.w :r do
78
+ xml.w :t do
79
+ xml.text! @text
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ def write_instruct
86
+ xml.w :r do
87
+ xml.w :instrText, "xml:space" => "preserve" do
88
+ xml.text! " TOC \\o '1-3' \\h \\z \\u "
89
+ end
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,13 @@
1
+ module Docxtor
2
+ class Generator
3
+ class << self
4
+ def generate(template, &block)
5
+ template_parser = TemplateParser.new(template)
6
+ parts = template_parser.parts
7
+
8
+ document = Document::Builder.new(&block)
9
+ Package::Builder.new(parts, document)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,35 @@
1
+ module Docxtor
2
+ module Package
3
+ class Builder
4
+ attr_reader :parts
5
+
6
+ def initialize(parts, document)
7
+ @parts = parts
8
+ @parts['document'] = Part.new("word/document.xml", document.content)
9
+ end
10
+
11
+ def save(filepath)
12
+ Zip::ZipOutputStream.open(filepath) do |ostream|
13
+ write_parts(ostream)
14
+ end
15
+ end
16
+
17
+ def to_stream
18
+ ostream = Zip::ZipOutputStream.new("streamed", true)
19
+ write_parts(ostream)
20
+ string_io = ostream.close_buffer
21
+ string_io.rewind
22
+ string_io
23
+ end
24
+
25
+ private
26
+
27
+ def write_parts(ostream)
28
+ @parts.each do |name, part|
29
+ ostream.put_next_entry(part.filename)
30
+ ostream.puts part.content
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,12 @@
1
+ module Docxtor
2
+ module Package
3
+ class Part
4
+ attr_reader :filename, :content
5
+
6
+ def initialize(filename, content)
7
+ @filename = filename
8
+ @content = content
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ module Docxtor
2
+ class TemplateParser
3
+ attr_accessor :template
4
+
5
+ FILES_PATTERN = File.join('**', '{*,.}{xml,rels}')
6
+
7
+ def initialize(template)
8
+ @template = template || File.join(File.dirname(__FILE__), "..", "..", "templates", "default")
9
+ end
10
+
11
+ def parts
12
+ @parts ||= parse
13
+ end
14
+
15
+ private
16
+
17
+ def parse
18
+ Dir.chdir(template) do
19
+ Hash[create_parts]
20
+ end
21
+ end
22
+
23
+ def create_parts
24
+ Dir[FILES_PATTERN].
25
+ delete_if { |file| File.directory?(file) }.
26
+ map { |file| create_part(file) }
27
+ end
28
+
29
+ def create_part(file)
30
+ content = File.read(file)
31
+ part = Package::Part.new(file, content)
32
+ key = File.basename(file, '.xml')
33
+
34
+ [key, part]
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Docxtor
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ module Docxtor
4
+ describe Document::Builder do
5
+ include WordprocessingMLMatchers
6
+
7
+ subject { Document::Builder.new do
8
+ p "Hi"
9
+ end
10
+ }
11
+
12
+ it 'contains body' do
13
+ subject.content.should contain_body
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ module Docxtor
4
+ describe Document::Heading do
5
+ include_context 'xmlbuilder' do
6
+ #it 'renders heading style'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ module Docxtor
4
+ describe Document::Paragraph do
5
+ include_context 'xmlbuilder' do
6
+
7
+ subject {
8
+ render(Document::Paragraph, "text1", :b => true) do
9
+ style 123
10
+ spacing :before => 80, :after => 240
11
+ font_size 32
12
+ font_size_complex 30
13
+ indent :start => 720
14
+ i; u
15
+
16
+ w "text2"; br; w "text3"
17
+ end
18
+ }
19
+
20
+ it 'contains reference to style' do
21
+ subject.should contain_element_style(:p)
22
+ end
23
+
24
+ it 'contains spacing property' do
25
+ subject.should contain_element_property(:p, :spacing)
26
+ end
27
+
28
+ it 'contains font size property' do
29
+ subject.should contain_element_property(:p, :sz)
30
+ end
31
+
32
+ it 'contains complex font size property' do
33
+ subject.should contain_element_property(:p, :szCs)
34
+ end
35
+
36
+ it 'contains indentiation property' do
37
+ subject.should contain_element_property(:p, :ind)
38
+ end
39
+
40
+ context 'nested run' do
41
+ it 'is bold' do
42
+ subject.should contain_element_property(:r, :b)
43
+ end
44
+
45
+ it 'is italic' do
46
+ subject.should contain_element_property(:r, :i)
47
+ end
48
+
49
+ it 'is underlined' do
50
+ subject.should contain_element_property(:r, :u)
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ module Docxtor
4
+ describe Document::Root do
5
+ include_context "xmlbuilder" do
6
+
7
+ subject {
8
+ Document::Root.new(:style => 1) do
9
+ p do
10
+ w "Hi"
11
+ end
12
+ end
13
+ }
14
+
15
+ it 'appends element to elements' do
16
+ subject.elements.first.should be_instance_of Docxtor::Document::Paragraph
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ module Docxtor
4
+ describe Document::TableOfContents do
5
+ include_context 'xmlbuilder' do
6
+
7
+ subject { render(Document::TableOfContents, 'Contents') }
8
+
9
+ it 'contains gallery of document parts' do
10
+ subject.should contain_gallery_of_document_parts
11
+ end
12
+
13
+ it 'contains heading text' do
14
+ subject.should contain_sdt_content_heading
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Docxtor, "#generate" do
4
+ context "when supplied all the same parameters that Generator takes" do
5
+ it 'does not throw errors' do
6
+ deny { rescuing {
7
+ Docxtor.generate do
8
+ p "Paragraph"
9
+ end
10
+ } }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ module Docxtor
4
+ describe Package::Builder do
5
+ subject { Package::Builder.new({}, Package::Part.new("word/document.xml", 'content')) }
6
+
7
+ include_context 'integration' do
8
+ context "given filepath and source document" do
9
+ it 'saves to file' do
10
+ subject.save(docx)
11
+
12
+ expect { File.exists?(docx) }
13
+ deny { rescuing { File.delete(docx) } }
14
+ end
15
+ end
16
+
17
+ context 'after serialization' do
18
+ it 'contains data' do
19
+ string_io = subject.to_stream
20
+ expect { string_io.size > 0 }
21
+ end
22
+
23
+ it 'contains document part' do
24
+ string_io = subject.to_stream
25
+ istream = Zip::ZipInputStream.open_buffer(string_io)
26
+
27
+ deny { rescuing {
28
+ document_entry = istream.get_next_entry
29
+
30
+ expect { document_entry != nil }
31
+ expect { document_entry.name == "word/document.xml" }
32
+ } }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ module Docxtor
4
+ describe Package::Part do
5
+ context "given filename and content" do
6
+ subject { Package::Part.new("filename", "content") }
7
+
8
+ it 'returns filename' do
9
+ expect { subject.filename == "filename" }
10
+ end
11
+ it 'returns content' do
12
+ expect { subject.content == "content" }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Docxtor do
4
+ context 'prerequisites' do
5
+ it 'has a version number' do
6
+ expect { Docxtor::VERSION != nil }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module Docxtor
2
+ shared_context 'integration' do
3
+ let(:template) { File.join(File.dirname(__FILE__), "..", "..", "..", "..", "templates", "default") }
4
+ let(:docx) { File.join(File.dirname(__FILE__), "..", "..", "tmp", 'test.docx') }
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ module Docxtor
2
+ shared_context 'xmlbuilder' do
3
+ include WordprocessingMLMatchers
4
+
5
+ def render(klass, *args, &block)
6
+ builder = Builder::XmlMarkup.new
7
+ klass.new(*args, &block).render(builder)
8
+ builder.target!
9
+ end
10
+ end
11
+ end