docxtor 0.1.1

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.
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