docxtor 0.1.1 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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