documatic 0.0.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.
- data/README +29 -0
- data/lib/documatic/component.rb +78 -0
- data/lib/documatic/helper.rb +53 -0
- data/lib/documatic/partial.rb +124 -0
- data/lib/documatic/template.rb +303 -0
- data/lib/documatic.rb +7 -0
- metadata +62 -0
data/README
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
= Documatic
|
2
|
+
|
3
|
+
Documatic is an OpenDocument processor for Ruby. It can be used to
|
4
|
+
produce attractive printable documents such as database reports,
|
5
|
+
invoices, letters, faxes and more. Both the data inputs and the
|
6
|
+
printable output are very flexible and easy to configure.
|
7
|
+
|
8
|
+
|
9
|
+
== Installation
|
10
|
+
|
11
|
+
Documatic can be installed via Rubygems.
|
12
|
+
|
13
|
+
% gem install documatic
|
14
|
+
|
15
|
+
|
16
|
+
== Licence
|
17
|
+
|
18
|
+
Documatic is (p) Public Domain 2007, no rights reserved.
|
19
|
+
|
20
|
+
Documatic comes with no warranty whatsoever.
|
21
|
+
|
22
|
+
|
23
|
+
== Support
|
24
|
+
|
25
|
+
Further information is available on the Documatic homepage at
|
26
|
+
http://documatic.240gl.org.
|
27
|
+
|
28
|
+
The Documatic project page (including tracker) is at
|
29
|
+
http://rubyforge.org/projects/documatic.
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Documatic
|
4
|
+
class Component
|
5
|
+
include ERB::Util
|
6
|
+
include Documatic::Helper
|
7
|
+
|
8
|
+
attr_accessor :erb
|
9
|
+
|
10
|
+
def initialize(erb_text)
|
11
|
+
@erb = ERB.new(erb_text)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Injects the provided assigns into this component and sends it through ERB.
|
15
|
+
def process(local_assigns)
|
16
|
+
if local_assigns.is_a? Binding
|
17
|
+
context = local_assigns
|
18
|
+
else # Hash
|
19
|
+
local_assigns.each do |key, val|
|
20
|
+
self.define_singleton_method(key) do val end
|
21
|
+
end
|
22
|
+
context = binding
|
23
|
+
end
|
24
|
+
|
25
|
+
@xml = nil ; @text = self.erb.result(context)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns a REXML::Document constructed from the text of this
|
29
|
+
# component. Note that this flushes self.text: subsequently if
|
30
|
+
# self.text is called it will be reconstructed from this XML
|
31
|
+
# document and self.xml will be flushed. Therefore the content of
|
32
|
+
# this partial is always stored either as XML or text, never both.
|
33
|
+
def xml
|
34
|
+
@xml ||= REXML::Document.new( remove_instance_variable(:@text) )
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the text of this component. Note that this flushes
|
38
|
+
# self.xml: subsequently if self.xml is called it will be
|
39
|
+
# reconstructed from this text and self.text will be flushed.
|
40
|
+
def text
|
41
|
+
@text ||= ( remove_instance_variable(:@xml) ).to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
# Merge the auto-styles from the cached partials into
|
45
|
+
# <office:automatic-styles> of this component. This method isn't
|
46
|
+
# intended to be called directly by client applications: it is
|
47
|
+
# called automatically by Documatic::Template after processing.
|
48
|
+
def merge_partial_styles
|
49
|
+
cache = Documatic::Partial.cache_by_prefix
|
50
|
+
if cache.length > 0
|
51
|
+
styles = self.xml.root.elements['office:automatic-styles']
|
52
|
+
if styles
|
53
|
+
cache.each_value do |partial|
|
54
|
+
partial.styles.each_element do |e|
|
55
|
+
styles << e
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# to_s() is a synonym for text()
|
63
|
+
alias_method :to_s, :text
|
64
|
+
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
# Adds methods to the singleton class representing the current
|
69
|
+
# instance. Useful for injecting faux local variables into an
|
70
|
+
# instance object.
|
71
|
+
# Courtesy John Hume,
|
72
|
+
# http://practicalruby.blogspot.com/2007/02/ruby-metaprogramming-introduction.html
|
73
|
+
def define_singleton_method(name, &body)
|
74
|
+
(class << self ; self ; end).send(:define_method, name, &body)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Documatic
|
4
|
+
module Helper
|
5
|
+
|
6
|
+
include ERB::Util
|
7
|
+
|
8
|
+
|
9
|
+
# Inserts a paragraph (<text:p>) containing the provided content;
|
10
|
+
# or nothing if no content is provided. This helper should be
|
11
|
+
# invoked from within "Ruby Block" because paragraphs are
|
12
|
+
# block-level elements.
|
13
|
+
#
|
14
|
+
# Note that the content is not escaped by default because you
|
15
|
+
# might want to include other tags in the content. You should use
|
16
|
+
# ERB::Util.h() to escape any content that could possibly contain
|
17
|
+
# XML characters.
|
18
|
+
def para(stylename, content = nil)
|
19
|
+
end_element = ( content ? ">#{content}</text:p>" : "/>" )
|
20
|
+
%Q(<text:p text:style-name="#{stylename}"#{end_element})
|
21
|
+
end
|
22
|
+
|
23
|
+
# Inserts a text span (<text:span>) containing the provided
|
24
|
+
# content. This helper should be invoked from within "Ruby
|
25
|
+
# Literal" because the tag is a text-level element that shouldn't
|
26
|
+
# be escaped. However the content is escaped.
|
27
|
+
def span(stylename, content)
|
28
|
+
%Q(<text:span text:style-name="#{stylename}">#{ERB::Util.h(content)}</text:span>)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# Inserts a partial into the document at the chosen position.
|
33
|
+
# This helper should be invoked from within "Ruby Block" because
|
34
|
+
# it inserts unescaped block-level material in the current
|
35
|
+
# template.
|
36
|
+
#
|
37
|
+
# The +assigns+ hash is passed through to the partial for binding.
|
38
|
+
#
|
39
|
+
# This method will add the provided partial to the
|
40
|
+
# Documatic::Partial cache if it hasn't yet been loaded; or if it
|
41
|
+
# has been loaded then the existing partial will be re-used.
|
42
|
+
def partial(filename, *assigns)
|
43
|
+
if Documatic::Partial.cache.has_key?(filename)
|
44
|
+
p = Documatic::Partial.cache[filename]
|
45
|
+
else
|
46
|
+
p = Documatic::Partial.new(filename)
|
47
|
+
Documatic::Partial.add_partial(filename, p)
|
48
|
+
end
|
49
|
+
p.process(*assigns)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
module Documatic
|
4
|
+
class Partial < Documatic::Template
|
5
|
+
|
6
|
+
attr_accessor :content
|
7
|
+
attr_accessor :filename
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def add_partial(name, partial)
|
11
|
+
self.cache[name] = partial
|
12
|
+
end
|
13
|
+
|
14
|
+
def cache
|
15
|
+
@cache ||= Hash.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def cache_by_prefix
|
19
|
+
by_prefix = Hash.new
|
20
|
+
self.cache.each_value do |partial|
|
21
|
+
by_prefix[partial.prefix] = partial
|
22
|
+
end
|
23
|
+
by_prefix
|
24
|
+
end
|
25
|
+
|
26
|
+
def flush_cache
|
27
|
+
@cache = Hash.new
|
28
|
+
end
|
29
|
+
|
30
|
+
end # class << self
|
31
|
+
|
32
|
+
def initialize(filename, prefix_name = nil)
|
33
|
+
super filename
|
34
|
+
@prefix = prefix_name || self.prefix
|
35
|
+
end
|
36
|
+
|
37
|
+
def process(local_assigns = {})
|
38
|
+
self.zip_file.find_entry('documatic/partial') || self.compile
|
39
|
+
@content = Documatic::Component.new( self.content_erb )
|
40
|
+
@content.process(local_assigns)
|
41
|
+
end
|
42
|
+
|
43
|
+
def compile
|
44
|
+
doc = REXML::Document.new( self.zip_file.read('content.xml') )
|
45
|
+
style_names = Hash.new
|
46
|
+
|
47
|
+
# Gather all auto style names from <office:automatic-styles>
|
48
|
+
doc.root.each_element('office:automatic-styles/*') do |e|
|
49
|
+
attr = e.attributes.get_attribute('style:name')
|
50
|
+
attr && style_names[attr.value] = attr
|
51
|
+
end
|
52
|
+
|
53
|
+
# Replace all auto styles in the document's attributes with the prefixed form.
|
54
|
+
doc.each_element('//*') do |e|
|
55
|
+
e.attributes.each_attribute do |attr|
|
56
|
+
if style_names.has_key? attr.value
|
57
|
+
e.add_attribute(attr.expanded_name, "#{self.prefix}_#{attr.value}")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Create 'documatic/partial/' in zip file
|
63
|
+
self.zip_file.find_entry('documatic/partial') || self.zip_file.mkdir('documatic/partial')
|
64
|
+
|
65
|
+
# Save the prefix in documatic/partial.txt
|
66
|
+
self.zip_file.get_output_stream('documatic/partial/partial.txt') do |f|
|
67
|
+
f.write self.prefix
|
68
|
+
end
|
69
|
+
|
70
|
+
# Set @styles, & save it in documatic/styles.xml
|
71
|
+
@styles = doc.root.elements['office:automatic-styles']
|
72
|
+
self.zip_file.get_output_stream('documatic/partial/styles.xml') do |f|
|
73
|
+
f.write @styles.to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get body text, erbify it, keep it in @content and save it in documatic/content.erb
|
77
|
+
body_text = doc.root.elements['office:body/office:text']
|
78
|
+
body_text.elements.delete('text:sequence-decls')
|
79
|
+
body_text.elements.delete('office:forms')
|
80
|
+
@content_erb = self.erbify( (body_text.elements.to_a.collect do |e| e.to_s ; end ).join )
|
81
|
+
self.zip_file.get_output_stream('documatic/partial/content.erb') do |f|
|
82
|
+
f.write @content_erb
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
# Partials aren't saved in the same way that templates are: this is a no-op.
|
88
|
+
def save ; end
|
89
|
+
|
90
|
+
def prefix
|
91
|
+
if not @prefix
|
92
|
+
if self.zip_file.find_entry('documatic/partial/partial.txt')
|
93
|
+
@prefix = self.zip_file.read('documatic/partial/partial.txt')
|
94
|
+
else
|
95
|
+
@prefix = File.basename(self.filename, '.odt').gsub(/[^A-Za-z0-9_]/, '_')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
return @prefix
|
99
|
+
end
|
100
|
+
|
101
|
+
def content_erb
|
102
|
+
if not @content_erb
|
103
|
+
if self.zip_file.find_entry('documatic/partial/content.erb')
|
104
|
+
self.zip_file.read('documatic/partial/content.erb')
|
105
|
+
else
|
106
|
+
self.compile
|
107
|
+
end
|
108
|
+
end
|
109
|
+
@content_erb
|
110
|
+
end
|
111
|
+
|
112
|
+
def styles
|
113
|
+
if not @styles
|
114
|
+
if self.zip_file.find_entry('documatic/partial/styles.xml')
|
115
|
+
@styles = REXML::Document.new( self.zip_file.read('documatic/partial/styles.xml') ).root
|
116
|
+
else
|
117
|
+
self.compile
|
118
|
+
end
|
119
|
+
end
|
120
|
+
@styles
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,303 @@
|
|
1
|
+
require 'rexml/text'
|
2
|
+
require 'rexml/attribute'
|
3
|
+
require 'zip/zip'
|
4
|
+
require 'erb'
|
5
|
+
|
6
|
+
module Documatic
|
7
|
+
class Template
|
8
|
+
include ERB::Util
|
9
|
+
|
10
|
+
attr_accessor :content
|
11
|
+
attr_accessor :styles
|
12
|
+
attr_accessor :zip_file
|
13
|
+
# The raw contents of 'content.xml'.
|
14
|
+
attr_accessor :content_raw
|
15
|
+
# Compiled text, to be written to 'content.erb'
|
16
|
+
attr_accessor :content_erb
|
17
|
+
# The raw contents of 'styles.xml'
|
18
|
+
attr_accessor :styles_raw
|
19
|
+
# Compiled text, to be written to 'styles.erb'
|
20
|
+
attr_accessor :styles_erb
|
21
|
+
|
22
|
+
# RE_STYLES match positions
|
23
|
+
STYLE_NAME = 1
|
24
|
+
STYLE_TYPE = 2
|
25
|
+
|
26
|
+
# RE_ERB match positions
|
27
|
+
TYPE = 5
|
28
|
+
ERB_CODE = 6
|
29
|
+
|
30
|
+
ROW_START = 1
|
31
|
+
ROW_END = 11
|
32
|
+
|
33
|
+
ITEM_START = 2
|
34
|
+
ITEM_END = 10
|
35
|
+
|
36
|
+
PARA_START = 3
|
37
|
+
PARA_END = 9
|
38
|
+
|
39
|
+
SPAN_END = 4
|
40
|
+
SPAN_START = 8
|
41
|
+
|
42
|
+
# Match types:
|
43
|
+
TABLE_ROW = 1
|
44
|
+
PARAGRAPH = 2
|
45
|
+
INLINE_CODE = 3
|
46
|
+
VALUE = 4
|
47
|
+
|
48
|
+
class << self
|
49
|
+
|
50
|
+
# Includes the number and text helpers from Rails' ActionPack.
|
51
|
+
# Requires that the Rails gems be installed.
|
52
|
+
def include_rails_helpers
|
53
|
+
require 'action_pack'
|
54
|
+
require 'action_controller'
|
55
|
+
require 'action_view'
|
56
|
+
|
57
|
+
require 'action_view/helpers/number_helper'
|
58
|
+
require 'action_view/helpers/text_helper'
|
59
|
+
|
60
|
+
[self, Documatic::Partial].each do |klass|
|
61
|
+
klass.class_eval do
|
62
|
+
include ActionView::Helpers::NumberHelper
|
63
|
+
include ActionView::Helpers::TextHelper
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end # class << self
|
69
|
+
|
70
|
+
def initialize(filename)
|
71
|
+
@filename = filename
|
72
|
+
@zip_file = Zip::ZipFile.open(@filename)
|
73
|
+
return true
|
74
|
+
end
|
75
|
+
|
76
|
+
def process(local_assigns = {})
|
77
|
+
# Compile this template, if not compiled already.
|
78
|
+
self.zip_file.find_entry('documatic/master') || self.compile
|
79
|
+
# Process the styles (incl. headers and footers).
|
80
|
+
# This is conditional because partials don't need styles.erb.
|
81
|
+
@styles = Documatic::Component.new( self.zip_file.read('documatic/master/styles.erb') )
|
82
|
+
@styles.process(local_assigns)
|
83
|
+
# Process the main (body) content.
|
84
|
+
@content = Documatic::Component.new( self.zip_file.read('documatic/master/content.erb') )
|
85
|
+
@content.process(local_assigns)
|
86
|
+
@content.merge_partial_styles
|
87
|
+
end
|
88
|
+
|
89
|
+
def save
|
90
|
+
# Gather all the styles from the partials, add them to the master's styles.
|
91
|
+
# Put the body into the document.
|
92
|
+
self.zip_file.get_output_stream('content.xml') do |f|
|
93
|
+
f.write self.content.to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
if self.styles
|
97
|
+
self.zip_file.get_output_stream('styles.xml') do |f|
|
98
|
+
f.write self.styles.to_s
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def close
|
104
|
+
self.zip_file.close
|
105
|
+
end
|
106
|
+
|
107
|
+
def compile
|
108
|
+
# Read the raw files
|
109
|
+
@content_raw = self.zip_file.read('content.xml')
|
110
|
+
@styles_raw = self.zip_file.read('styles.xml')
|
111
|
+
|
112
|
+
@content_erb = self.erbify(@content_raw)
|
113
|
+
@styles_erb = self.erbify(@styles_raw)
|
114
|
+
|
115
|
+
# Create 'documatic/master/' in zip file
|
116
|
+
self.zip_file.find_entry('documatic/master') || self.zip_file.mkdir('documatic/master')
|
117
|
+
|
118
|
+
self.zip_file.get_output_stream('documatic/master/content.erb') do |f|
|
119
|
+
f.write @content_erb
|
120
|
+
end
|
121
|
+
self.zip_file.get_output_stream('documatic/master/styles.erb') do |f|
|
122
|
+
f.write @styles_erb
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
protected
|
128
|
+
|
129
|
+
# Change OpenDocument line breaks and tabs in the ERb code to regular characters.
|
130
|
+
def unnormalize(code)
|
131
|
+
code = code.gsub(/<text:line-break\/>/, "\n")
|
132
|
+
code = code.gsub(/<text:tab\/>/, "\t")
|
133
|
+
return REXML::Text.unnormalize(code)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Massage OpenDocument XML into ERb. (This is the heart of the compiler.)
|
137
|
+
def erbify(code, escape = true)
|
138
|
+
# First gather all the ERb-related derived styles
|
139
|
+
remaining = code
|
140
|
+
styles = {'Ruby_20_Code' => 'Code', 'Ruby_20_Value' => 'Value',
|
141
|
+
'Ruby_20_Block' => 'Block', 'Ruby_20_Literal' => 'Literal'}
|
142
|
+
re_styles = /<style:style style:name="([^"]+)" style:family="text" style:parent-style-name="Ruby_20_(Code|Value|Block|Literal)">/
|
143
|
+
|
144
|
+
while remaining.length > 0
|
145
|
+
md = re_styles.match remaining
|
146
|
+
if md
|
147
|
+
styles[md[STYLE_NAME]] = md[STYLE_TYPE]
|
148
|
+
remaining = md.post_match
|
149
|
+
else
|
150
|
+
remaining = ""
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
remaining = code
|
155
|
+
result = String.new
|
156
|
+
|
157
|
+
# Then make a RE that includes the ERb-related styles.
|
158
|
+
# Match positions:
|
159
|
+
#
|
160
|
+
# 1. ROW_START Begin table row ?
|
161
|
+
# 2. ITEM_START Begin list item ?
|
162
|
+
# 3. PARA_START Begin paragraph ?
|
163
|
+
# 4. SPAN_END Another text span ends immediately before ERb ?
|
164
|
+
# 5. TYPE ERb text style type
|
165
|
+
# 6. ERB_CODE ERb code
|
166
|
+
# 7. (ERb inner brackets)
|
167
|
+
# 8. SPAN_START Another text span begins immediately after ERb ?
|
168
|
+
# 9. PARA_END End paragraph ?
|
169
|
+
# 10. ITEM_END End list item ?
|
170
|
+
# 11. ROW_END End table row (incl. covered rows) ?
|
171
|
+
#
|
172
|
+
# "?": optional, might not occur every time
|
173
|
+
re_erb = /(<table:table-row><table:table-cell [^>]+>)?(<text:list-item>)?(<text:p [^>]+>)?(<\/text:span>)?<text:span text:style-name="(#{styles.keys.join '|'})">(([^<]*|<text:line-break\/>|<text:tab\/>)+)<\/text:span>(<text:span [^>]+>)?(<\/text:p>)?(<\/text:list-item>)?(<\/table:table-cell>(<table:covered-table-cell\/>)*<\/table:table-row>)?/
|
174
|
+
|
175
|
+
# Then search for all text using those styles
|
176
|
+
while remaining.length > 0
|
177
|
+
|
178
|
+
md = re_erb.match remaining
|
179
|
+
|
180
|
+
if md
|
181
|
+
|
182
|
+
### TESTING ONLY
|
183
|
+
# puts "#{md[ERB_CODE]}:"
|
184
|
+
# for i in 1 ... md.size do
|
185
|
+
# puts "\t#{i}: #{md[i]}"
|
186
|
+
# end
|
187
|
+
# puts ""
|
188
|
+
|
189
|
+
result += md.pre_match
|
190
|
+
|
191
|
+
match_code = false
|
192
|
+
match_row = false
|
193
|
+
match_item = false
|
194
|
+
match_para = false
|
195
|
+
match_span = false
|
196
|
+
|
197
|
+
if styles[md[TYPE]] == 'Code'
|
198
|
+
match_code = true
|
199
|
+
delim_start = '<% ' ; delim_end = ' %>'
|
200
|
+
if md[PARA_START] and md[PARA_END]
|
201
|
+
match_para = true
|
202
|
+
if md[ITEM_START] and md[ITEM_END]
|
203
|
+
match_item = true
|
204
|
+
end
|
205
|
+
if md[ROW_START] and md[ROW_END]
|
206
|
+
match_row = true
|
207
|
+
end
|
208
|
+
end
|
209
|
+
elsif styles[md[TYPE]] == 'Block'
|
210
|
+
delim_start = '<%= ' ; delim_end = ' %>'
|
211
|
+
if md[PARA_START] and md[PARA_END]
|
212
|
+
match_para = true
|
213
|
+
end
|
214
|
+
else # style is Value or Literal
|
215
|
+
if (not escape) || (styles[md[TYPE]] == 'Literal')
|
216
|
+
delim_start = '<%= ' ; delim_end = ' %>'
|
217
|
+
else
|
218
|
+
delim_start = '<%= ERB::Util.h(' ; delim_end = ') %>'
|
219
|
+
end
|
220
|
+
|
221
|
+
if md[SPAN_END] and md[SPAN_START]
|
222
|
+
match_span = true
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
if md[ROW_START] and not match_row
|
227
|
+
result += md[ROW_START]
|
228
|
+
end
|
229
|
+
|
230
|
+
if md[ITEM_START] and not match_item
|
231
|
+
result += md[ITEM_START]
|
232
|
+
end
|
233
|
+
|
234
|
+
if md[PARA_START] and not match_para
|
235
|
+
result += md[PARA_START]
|
236
|
+
end
|
237
|
+
|
238
|
+
# Text formatting before ERb
|
239
|
+
if match_code
|
240
|
+
if md[SPAN_END]
|
241
|
+
result += md[SPAN_END]
|
242
|
+
end
|
243
|
+
else
|
244
|
+
if md[SPAN_START] and not md[SPAN_END]
|
245
|
+
result += md[SPAN_START]
|
246
|
+
end
|
247
|
+
end
|
248
|
+
# if md[SPAN_START] and not match_span
|
249
|
+
# result += md[SPAN_START]
|
250
|
+
# end
|
251
|
+
|
252
|
+
result += "#{delim_start}#{self.unnormalize md[ERB_CODE]}#{delim_end}"
|
253
|
+
|
254
|
+
# Text formatting after ERb
|
255
|
+
if match_code
|
256
|
+
if md[SPAN_START]
|
257
|
+
result += md[SPAN_START]
|
258
|
+
end
|
259
|
+
else
|
260
|
+
if md[SPAN_END]
|
261
|
+
result += md[SPAN_END]
|
262
|
+
if md[SPAN_START]
|
263
|
+
result += md[SPAN_START]
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# if md[SPAN_END] and not match_span
|
269
|
+
# result += md[SPAN_END]
|
270
|
+
# end
|
271
|
+
|
272
|
+
if md[PARA_END] and not match_para
|
273
|
+
result += md[PARA_END]
|
274
|
+
end
|
275
|
+
|
276
|
+
if md[ITEM_END] and not match_item
|
277
|
+
result += md[ITEM_END]
|
278
|
+
end
|
279
|
+
|
280
|
+
if md[ROW_END] and not match_row
|
281
|
+
result += md[ROW_END]
|
282
|
+
end
|
283
|
+
|
284
|
+
remaining = md.post_match
|
285
|
+
|
286
|
+
else # no further matches
|
287
|
+
result += remaining
|
288
|
+
remaining = ""
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
return result
|
293
|
+
end
|
294
|
+
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# Force REXML to use double-quotes (consistent with OOo).
|
299
|
+
REXML::Attribute.class_eval do
|
300
|
+
def to_string
|
301
|
+
%Q[#@expanded_name="#{to_s().gsub(/"/, '"')}"]
|
302
|
+
end
|
303
|
+
end
|
data/lib/documatic.rb
ADDED
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: documatic
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2007-05-16 00:00:00 +10:00
|
8
|
+
summary: Documatic is a Ruby OpenDocument processor for preparing documents and reports.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: urbanus@240gl.org
|
12
|
+
homepage: http://documatic.240gl.org
|
13
|
+
rubyforge_project: documatic
|
14
|
+
description:
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors: []
|
30
|
+
|
31
|
+
files:
|
32
|
+
- lib
|
33
|
+
- lib/documatic
|
34
|
+
- lib/documatic.rb
|
35
|
+
- lib/documatic/template.rb
|
36
|
+
- lib/documatic/component.rb
|
37
|
+
- lib/documatic/helper.rb
|
38
|
+
- lib/documatic/partial.rb
|
39
|
+
- tests
|
40
|
+
- README
|
41
|
+
test_files: []
|
42
|
+
|
43
|
+
rdoc_options: []
|
44
|
+
|
45
|
+
extra_rdoc_files: []
|
46
|
+
|
47
|
+
executables: []
|
48
|
+
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
requirements: []
|
52
|
+
|
53
|
+
dependencies:
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: rubyzip
|
56
|
+
version_requirement:
|
57
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.9.1
|
62
|
+
version:
|