docxtor 0.1.1 → 0.2.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 (43) hide show
  1. data/.ruby-version +1 -1
  2. data/Gemfile +1 -0
  3. data/README.md +88 -44
  4. data/lib/docxtor.rb +3 -0
  5. data/lib/docxtor/document/builder.rb +18 -3
  6. data/lib/docxtor/document/element.rb +9 -2
  7. data/lib/docxtor/generator.rb +7 -2
  8. data/lib/docxtor/package/builder.rb +2 -11
  9. data/lib/docxtor/reference_builder.rb +29 -0
  10. data/lib/docxtor/running_element.rb +69 -0
  11. data/lib/docxtor/running_elements_builder.rb +18 -0
  12. data/lib/docxtor/template_parser.rb +3 -10
  13. data/lib/docxtor/version.rb +1 -1
  14. data/spec/docxtor/document/builder_spec.rb +2 -4
  15. data/spec/docxtor/document/paragraph_spec.rb +8 -8
  16. data/spec/docxtor/document/root_spec.rb +21 -5
  17. data/spec/docxtor/document/table_of_contents_spec.rb +1 -5
  18. data/spec/docxtor/docxtor_spec.rb +6 -6
  19. data/spec/docxtor/package/builder_spec.rb +51 -11
  20. data/spec/docxtor/reference_builder_spec.rb +27 -0
  21. data/spec/docxtor/running_element_spec.rb +38 -0
  22. data/spec/docxtor/running_elements_builder_spec.rb +25 -0
  23. data/spec/docxtor/support/contexts/xml_builder_context.rb +0 -2
  24. data/spec/docxtor/template_parser_spec.rb +3 -3
  25. data/templates/default/[Content_Types].xml +1 -5
  26. data/templates/default/_rels/.rels +1 -4
  27. data/templates/default/docProps/app.xml +1 -0
  28. data/templates/default/docProps/core.xml +1 -0
  29. metadata +150 -154
  30. data/spec/docxtor/support/examples/.gitkeep +0 -0
  31. data/spec/docxtor/support/matchers/wordprocessingml_matchers.rb +0 -42
  32. data/spec/docxtor/support/matchers/xpath_matchers.rb +0 -46
  33. data/templates/default/word/_rels/document.xml.rels +0 -13
  34. data/templates/default/word/endnotes.xml +0 -17
  35. data/templates/default/word/fontTable.xml +0 -31
  36. data/templates/default/word/footer1.xml +0 -35
  37. data/templates/default/word/footer2.xml +0 -48
  38. data/templates/default/word/footnotes.xml +0 -17
  39. data/templates/default/word/numbering.xml +0 -212
  40. data/templates/default/word/settings.xml +0 -107
  41. data/templates/default/word/styles.xml +0 -368
  42. data/templates/default/word/theme/theme1.xml +0 -2
  43. data/templates/default/word/websettings.xml +0 -4
@@ -1 +1 @@
1
- ree-1.8.7-2012.02
1
+ 1.9.3-p448
data/Gemfile CHANGED
@@ -15,4 +15,5 @@ end
15
15
 
16
16
  group :development do
17
17
  gem "pry"
18
+ gem "pry-debugger"
18
19
  end
data/README.md CHANGED
@@ -9,12 +9,14 @@ docxtor
9
9
  ## Summary
10
10
 
11
11
  Ruby docx generator.
12
- Supported ruby versions: ree-1.8.7, mri 1.8.7
12
+ **Docxtor** is built to work with Ruby 1.8 as well as 1.9
13
13
 
14
14
  ## Features
15
15
 
16
- 1. Paragraphs and headings: bold, italic, custom style id, line breaks, page breaks, spacing
17
- 2. Table of contents
16
+ - Paragraphs and headings markup.
17
+ - Bold, underline & italic font styles.
18
+ - Document table of contents.
19
+ - Document headers & footers, page numbers.
18
20
 
19
21
  More to come, stay tuned!
20
22
 
@@ -34,7 +36,9 @@ Or install it yourself as:
34
36
 
35
37
  ## Usage
36
38
 
37
- ```
39
+ Here's the taste of what **Docxtor's** "markup language" may look like.
40
+
41
+ ```ruby
38
42
  package = Docxtor.generate do
39
43
  table_of_contents "Contents"
40
44
  h 1, "heading1"
@@ -63,51 +67,91 @@ end
63
67
  package.save('test.docx')
64
68
  ```
65
69
 
66
- more usage examples:
70
+ ### Entry point
67
71
 
72
+ ```ruby
73
+ Docxtor.generate do
74
+ ...
75
+ end
68
76
  ```
69
- # Usage sample:
70
- # elements - any collection of your elements, so in this example i have:
71
- # class Element
72
- # attr_reader :title1, :title2, :content
73
- # end
74
- # elements = Array.new(5, Element)
75
-
76
- # Somewhere in your controller:
77
-
78
- def action_method
79
- @stream = Docxtor.generate do
80
- p 'Paragraph text', :align => 'center', :b => true
81
- table_of_contents 'Table of contents'
82
- page_break
83
-
84
- elements.each do |el|
85
- h 1, el.title do
86
- spacing :before => 120, :after => 120
87
-
88
- write el.title1
89
- write ", blahblah "
90
- br
91
- write el.title2
92
- end
93
-
94
- p do
95
- el.content.lines.map(&:chomp).each do |line|
96
- write line; br
97
- end
98
- end
99
-
100
- end
101
- end
102
-
103
- send_data(
104
- @stream.read,
105
- :filename => 'yourfilename.docx',
106
- :type =>'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
107
- )
77
+
78
+ ### Paragraphs
79
+
80
+ The main building block of `docx` document is a paragraph. There're couple of ways of defining a paragraph.
81
+
82
+ ```ruby
83
+ p "Hi there!", :bold => true
84
+ ```
85
+
86
+ Is the same as
87
+
88
+ ```ruby
89
+ p do
90
+ w "Hi there!"
91
+ b
92
+ end
93
+ ```
94
+
95
+ or
96
+
97
+ ```ruby
98
+ p do
99
+ write "Hi there!"
100
+ bold
101
+ end
102
+ ```
103
+
104
+ Everything styling-related that is written either inside the block or in options hash will be applied to the whole parapraph.
105
+
106
+ You can mix and match different ways to define text & options.
107
+
108
+ ```ruby
109
+ p "I'm a first string in this paragraph", :bold => true do
110
+ write "I'm a second one."
111
+ u
112
+ w "And we all will be bold and underlined in the same time!"
108
113
  end
109
114
  ```
110
115
 
116
+ ### Headings
117
+
118
+ **Currently not working as expected! Will be fixed soon!**
119
+
120
+ Generally the same as paragraphs, but have additional argument - the level of heading and styled by default.
121
+
122
+ ```ruby
123
+ h 1, "Chapter 1"
124
+ h 2, "Where our hero goes for an adventure"
125
+ ```
126
+
127
+ ### Table of Contents
128
+
129
+ **Currently not working as expected! Will be fixed soon!**
130
+
131
+ Inserts the ToC for a document with a caption.
132
+
133
+ ```ruby
134
+ table_of_contents "Table of Contents"
135
+ ```
136
+
137
+ ### Footers & Headers
138
+
139
+ You can specify following properties for footers and headers:
140
+
141
+ - on which pages footer or header will appear (`:odd`, `:even` or only `:first`);
142
+ - alignment (`:center`, `:left`, `:right`)
143
+ - contents of a footer/header, including special keyword `:pagenum` which inserts page numbers
144
+
145
+ ```ruby
146
+ footer :pagenum, :align => :center
147
+ header "Proudly made by me", :pages => :odd
148
+ footer "2013", :pages => :first, :align => :right
149
+ ```
150
+
151
+ ### Breaks
152
+
153
+ You can insert `page_break` or newline inside a paragraph with `br`.
154
+
111
155
  ## Contributing
112
156
 
113
157
  1. Fork it
@@ -6,6 +6,9 @@ require "docxtor/version"
6
6
  module Docxtor
7
7
  autoload :Generator, 'docxtor/generator'
8
8
  autoload :TemplateParser, 'docxtor/template_parser'
9
+ autoload :RunningElementsBuilder, 'docxtor/running_elements_builder'
10
+ autoload :RunningElement, 'docxtor/running_element'
11
+ autoload :ReferenceBuilder, 'docxtor/reference_builder'
9
12
 
10
13
  module Document
11
14
  autoload :Builder, 'docxtor/document/builder'
@@ -1,12 +1,17 @@
1
1
  module Docxtor
2
2
  module Document
3
3
  class Builder
4
- attr_accessor :content
4
+ attr_accessor :content, :running_elements
5
5
 
6
- def initialize(&block)
6
+ def initialize(running_elements, &block)
7
+ @running_elements = running_elements
7
8
  @content = render(&block)
8
9
  end
9
10
 
11
+ def filename
12
+ "word/document.xml"
13
+ end
14
+
10
15
  def render(&block)
11
16
  xml = ::Builder::XmlMarkup.new
12
17
 
@@ -21,12 +26,22 @@ module Docxtor
21
26
  "xmlns:w" => "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
22
27
  "xmlns:wne" => "http://schemas.microsoft.com/office/word/2006/wordml" do
23
28
  xml.w :body do
24
- xml << Document::Root.new(&block).render(xml)
29
+ xml << Document::Root.new(&block).render(::Builder::XmlMarkup.new)
30
+
31
+ render_running_elements xml
25
32
  end
26
33
  end
27
34
 
28
35
  xml.target!
29
36
  end
37
+
38
+ def render_running_elements xml
39
+ xml.w :sectPr do |xml|
40
+ running_elements.each do |re|
41
+ xml.w re.reference_name.to_sym, "r:id" => re.reference_id, "w:type" => "#{re.pages}"
42
+ end
43
+ end
44
+ end
30
45
  end
31
46
  end
32
47
  end
@@ -11,6 +11,10 @@ module Docxtor
11
11
  end
12
12
  end
13
13
 
14
+ def method_missing name, *args
15
+ super unless [:header, :footer].include? name
16
+ end
17
+
14
18
  def initialize(*args, &block)
15
19
  @elements = []
16
20
 
@@ -82,8 +86,11 @@ module Docxtor
82
86
  def get_properties_for(el)
83
87
  props = properties[el]
84
88
  if props
85
- pairs = @params.
86
- map { |k, v| props[k] && [props[k][:name] || props[k], v] }
89
+ pairs = @params.map do |k, v|
90
+ unless props[k].nil?
91
+ props[k].is_a?(Hash) ? [props[k][:name], v] : [props[k], v]
92
+ end
93
+ end
87
94
  Hash[pairs]
88
95
  end
89
96
  end
@@ -5,8 +5,13 @@ module Docxtor
5
5
  template_parser = TemplateParser.new(template)
6
6
  parts = template_parser.parts
7
7
 
8
- document = Document::Builder.new(&block)
9
- Package::Builder.new(parts, document)
8
+ running_elements = RunningElementsBuilder.new(&block).elements
9
+ parts += running_elements
10
+
11
+ parts << ReferenceBuilder.new(running_elements)
12
+ parts << Document::Builder.new(running_elements, &block)
13
+
14
+ Package::Builder.new(parts)
10
15
  end
11
16
  end
12
17
  end
@@ -3,9 +3,8 @@ module Docxtor
3
3
  class Builder
4
4
  attr_reader :parts
5
5
 
6
- def initialize(parts, document)
6
+ def initialize(parts)
7
7
  @parts = parts
8
- @parts['document'] = Part.new("word/document.xml", document.content)
9
8
  end
10
9
 
11
10
  def save(filepath)
@@ -14,18 +13,10 @@ module Docxtor
14
13
  end
15
14
  end
16
15
 
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
16
  private
26
17
 
27
18
  def write_parts(ostream)
28
- @parts.each do |name, part|
19
+ @parts.each do |part|
29
20
  ostream.put_next_entry(part.filename)
30
21
  ostream.puts part.content
31
22
  end
@@ -0,0 +1,29 @@
1
+ module Docxtor
2
+ class ReferenceBuilder
3
+ attr_accessor :elements
4
+
5
+ def initialize elements
6
+ @elements = elements
7
+ @elements.each_with_index { |el, i| el.reference_id = "rId#{i+1}" }
8
+ end
9
+
10
+ def filename
11
+ "word/_rels/document.xml.rels"
12
+ end
13
+
14
+ def content
15
+ xml = ::Builder::XmlMarkup.new
16
+
17
+ xml.instruct! :xml, :version => "1.0", :encoding=>"UTF-8", :standalone => "yes"
18
+ xml.Relationships "xmlns" => "http://schemas.openxmlformats.org/package/2006/relationships" do |xml|
19
+ elements.each do |element|
20
+ xml.Relationship "Id" => element.reference_id,
21
+ "Target" => element.name,
22
+ "Type" => element.reference_type
23
+ end
24
+ end
25
+
26
+ xml.target!
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,69 @@
1
+ module Docxtor
2
+ class RunningElement
3
+ attr_accessor :pages, :type, :reference_id
4
+
5
+ def initialize type, num, contents, options = {}
6
+ @type = type
7
+ @contents = contents
8
+ @align = options[:align]
9
+ @pages = options[:pages] || :default
10
+ @num = num
11
+ end
12
+
13
+ def reference_name
14
+ "#{type}Reference"
15
+ end
16
+
17
+ def name
18
+ "#{type}#{@num}.xml"
19
+ end
20
+
21
+ def filename
22
+ "word/#{name}"
23
+ end
24
+
25
+ def reference_type
26
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/#{type}"
27
+ end
28
+
29
+ def content
30
+ xml = ::Builder::XmlMarkup.new
31
+
32
+ xml.instruct! :xml, :version => "1.0", :encoding=>"UTF-8", :standalone => "yes"
33
+ xml.w :ftr, "xmlns:o" => "urn:schemas-microsoft-com:office:office",
34
+ "xmlns:r" => "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
35
+ "xmlns:v" => "urn:schemas-microsoft-com:vml",
36
+ "xmlns:w" => "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
37
+ "xmlns:w10" => "urn:schemas-microsoft-com:office:word",
38
+ "xmlns:wp" => "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" do |xml|
39
+
40
+ xml.w :p do |xml|
41
+ xml.w :pPr do |xml|
42
+ xml.w :jc, "w:val" => "#{@align}" if @align
43
+ end
44
+ if @contents == :pagenum
45
+ xml.w :r do |xml|
46
+ xml.w :fldChar, "w:fldCharType" => "begin"
47
+ end
48
+ xml.w :r do |xml|
49
+ xml.w :instrText, "PAGE"
50
+ end
51
+ xml.w :r do |xml|
52
+ xml.w :fldChar, "w:fldCharType" => "separate"
53
+ end
54
+ xml.w :r do |xml|
55
+ xml.w :t, "i"
56
+ end
57
+ xml.w :r do |xml|
58
+ xml.w :fldChar, "w:fldCharType" => "end"
59
+ end
60
+ else
61
+ xml.w :r do |xml|
62
+ xml.w :t, @contents
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,18 @@
1
+ module Docxtor
2
+ class RunningElementsBuilder
3
+ attr_accessor :elements
4
+
5
+ def method_missing method_name, *args
6
+ num = 0
7
+ if [:header, :footer].include? method_name
8
+ num += 1
9
+ @elements << RunningElement.new(method_name, num, *args)
10
+ end
11
+ end
12
+
13
+ def initialize &block
14
+ @elements = []
15
+ instance_eval &block
16
+ end
17
+ end
18
+ end
@@ -4,19 +4,13 @@ module Docxtor
4
4
 
5
5
  FILES_PATTERN = File.join('**', '{*,.}{xml,rels}')
6
6
 
7
- def initialize(template)
7
+ def initialize(template = nil)
8
8
  @template = template || File.join(File.dirname(__FILE__), "..", "..", "templates", "default")
9
9
  end
10
10
 
11
11
  def parts
12
- @parts ||= parse
13
- end
14
-
15
- private
16
-
17
- def parse
18
12
  Dir.chdir(template) do
19
- Hash[create_parts]
13
+ create_parts
20
14
  end
21
15
  end
22
16
 
@@ -29,9 +23,8 @@ module Docxtor
29
23
  def create_part(file)
30
24
  content = File.read(file)
31
25
  part = Package::Part.new(file, content)
32
- key = File.basename(file, '.xml')
33
26
 
34
- [key, part]
27
+ part
35
28
  end
36
29
 
37
30
  end