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