notroff 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.
- data/bin/notroff +47 -0
- data/lib/notroff/code_scrubber.rb +9 -0
- data/lib/notroff/code_typer.rb +37 -0
- data/lib/notroff/command_processor.rb +28 -0
- data/lib/notroff/composite_processor.rb +52 -0
- data/lib/notroff/content.xml.erb +110 -0
- data/lib/notroff/docbook_renderer.rb +155 -0
- data/lib/notroff/embedded.rb +132 -0
- data/lib/notroff/filter.rb +15 -0
- data/lib/notroff/formatter.rb +54 -0
- data/lib/notroff/html_renderer.rb +92 -0
- data/lib/notroff/io.rb +18 -0
- data/lib/notroff/logger.rb +13 -0
- data/lib/notroff/odt_renderer.rb +154 -0
- data/lib/notroff/odt_replacer.rb +22 -0
- data/lib/notroff/paragraph_joiner.rb +53 -0
- data/lib/notroff/processor.rb +192 -0
- data/lib/notroff/skel.odt +0 -0
- data/lib/notroff/string_extensions.rb +6 -0
- data/lib/notroff/template_expander.rb +15 -0
- data/lib/notroff/text.rb +67 -0
- data/lib/notroff/text_replacer.rb +65 -0
- data/lib/notroff/tokenize.rb +64 -0
- data/lib/notroff/type_assigner.rb +25 -0
- data/lib/notroff.rb +23 -0
- data/readme.nr +58 -0
- data/spec/command_processor_spec.rb +15 -0
- data/spec/composite_processor_spec.rb +35 -0
- data/spec/filter_spec.rb +16 -0
- data/spec/formatter_spec.rb +22 -0
- data/spec/hello.rb +1 -0
- data/spec/hello.rb.out +1 -0
- data/spec/paragraph_joiner_spec.rb +26 -0
- data/spec/simple.nr +3 -0
- data/spec/string_extensions_spec.rb +22 -0
- data/spec/text_spec.rb +55 -0
- data/spec/type_assigner_spec.rb +39 -0
- data/spec/with_commands.nr +6 -0
- metadata +83 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
class HtmlRenderer < Processor
|
5
|
+
include Tokenize
|
6
|
+
include REXML
|
7
|
+
|
8
|
+
def process( paragraphs )
|
9
|
+
body = Element.new('body')
|
10
|
+
paragraphs.each do |paragraph|
|
11
|
+
new_element = format( paragraph )
|
12
|
+
body.add new_element if new_element
|
13
|
+
end
|
14
|
+
body
|
15
|
+
end
|
16
|
+
|
17
|
+
def format( p )
|
18
|
+
type = p[:type]
|
19
|
+
text = p.string
|
20
|
+
|
21
|
+
return nil if text.empty? and :type != :code
|
22
|
+
|
23
|
+
if type == :code
|
24
|
+
code_element(type, text)
|
25
|
+
else
|
26
|
+
text_element(type, text)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def text_element(type, text)
|
31
|
+
element = Element.new(tag_for(type))
|
32
|
+
add_body_text(text, element)
|
33
|
+
element
|
34
|
+
end
|
35
|
+
|
36
|
+
def tag_for(type)
|
37
|
+
case type
|
38
|
+
when :body
|
39
|
+
'p'
|
40
|
+
when :text
|
41
|
+
'p'
|
42
|
+
when :author
|
43
|
+
'h3'
|
44
|
+
when :section
|
45
|
+
'h3'
|
46
|
+
when :sec
|
47
|
+
'h3'
|
48
|
+
when :chapter
|
49
|
+
'h2'
|
50
|
+
when :title
|
51
|
+
'h1'
|
52
|
+
else
|
53
|
+
raise "Dont know what to do with #{type}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_body_text( text, element )
|
58
|
+
tokens = tokenize_body_text( text )
|
59
|
+
tokens.each {|token| add_span( token, element ) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_span( token, element )
|
63
|
+
case token[:type]
|
64
|
+
when :italic
|
65
|
+
element.add(span_for(token.string, "em"))
|
66
|
+
when :code
|
67
|
+
element.add(span_for(token.string, "code"))
|
68
|
+
when :bold
|
69
|
+
element.add(span_for(token.string, "b"))
|
70
|
+
when :normal
|
71
|
+
element.add_text(token.string)
|
72
|
+
when :footnote
|
73
|
+
add_body_text(" [#{token.string}] ", element)
|
74
|
+
else
|
75
|
+
raise "Dont know what to do with type #{token[:type]} - #{token}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def code_element(type, text)
|
80
|
+
element = Element.new('code')
|
81
|
+
pre_element = Element.new('pre')
|
82
|
+
element.add pre_element
|
83
|
+
pre_element.add_text text
|
84
|
+
element
|
85
|
+
end
|
86
|
+
|
87
|
+
def span_for( text, style )
|
88
|
+
span = Element.new( style )
|
89
|
+
span.text = remove_escapes(text)
|
90
|
+
span
|
91
|
+
end
|
92
|
+
end
|
data/lib/notroff/io.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
class FileProcessor
|
2
|
+
def initialize(path)
|
3
|
+
@path = path
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class FileReader < FileProcessor
|
8
|
+
def process(ignored)
|
9
|
+
paras = File.readlines(@path)
|
10
|
+
paras.map! {|p| p.rstrip}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class FileWriter < FileProcessor
|
15
|
+
def process(output)
|
16
|
+
File.open(@path, 'w') {|f| f.write(output)}
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
|
5
|
+
class OdtRenderer < Processor
|
6
|
+
include Tokenize
|
7
|
+
include REXML
|
8
|
+
|
9
|
+
LONG_DASH_CODE = 0xe1.chr + 0x80.chr + 0x93.chr
|
10
|
+
|
11
|
+
PARAGRAPH_STYLES = {
|
12
|
+
:body => 'BodyNoIndent',
|
13
|
+
:title => 'HB',
|
14
|
+
:section => 'HC',
|
15
|
+
:sec => 'HC',
|
16
|
+
:first_code => 'CDT1',
|
17
|
+
:middle_code => 'CDT',
|
18
|
+
:end_code => 'CDTX',
|
19
|
+
:author => 'AU',
|
20
|
+
:quote => 'Quotation',
|
21
|
+
:single_code => 'C1',
|
22
|
+
:pn => 'PN',
|
23
|
+
:pt => 'PT',
|
24
|
+
:cn => 'HA',
|
25
|
+
:chapter => 'HA',
|
26
|
+
:ct => 'HB' }
|
27
|
+
|
28
|
+
@@footnote_number = 1
|
29
|
+
|
30
|
+
def process( paragraphs )
|
31
|
+
elements = []
|
32
|
+
paragraphs.each do |paragraph|
|
33
|
+
new_element = format( paragraph )
|
34
|
+
elements << new_element if new_element
|
35
|
+
end
|
36
|
+
elements
|
37
|
+
end
|
38
|
+
|
39
|
+
def format( para )
|
40
|
+
Logger.log "Format: #{para.inspect}"
|
41
|
+
type = para[:type]
|
42
|
+
text = para
|
43
|
+
|
44
|
+
return nil if text.empty? and ! code_type?( type )
|
45
|
+
|
46
|
+
result = new_text_element( type )
|
47
|
+
|
48
|
+
if [ :author, :section, :sec, :title, :pn, :pt, :chapter ].include?( type )
|
49
|
+
result.add_text( text.string )
|
50
|
+
elsif [:body, :quote].include?(type)
|
51
|
+
add_body_text( text, result )
|
52
|
+
elsif code_type?(type)
|
53
|
+
add_code_text( text, result )
|
54
|
+
else
|
55
|
+
raise "Dont know what to do with type [#{type}]"
|
56
|
+
end
|
57
|
+
result
|
58
|
+
end
|
59
|
+
|
60
|
+
def code_type?( type )
|
61
|
+
[ :first_code, :middle_code, :end_code, :single_code ].include?(type)
|
62
|
+
end
|
63
|
+
|
64
|
+
def new_text_element( type )
|
65
|
+
result = Element.new( "text:p" )
|
66
|
+
result.attributes["text:style-name"] = PARAGRAPH_STYLES[type]
|
67
|
+
result
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_body_text( text, element )
|
71
|
+
tokens = tokenize_body_text( text )
|
72
|
+
tokens.each {|token| add_span( token, element ) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def add_code_text( text, element )
|
76
|
+
text = text.dup
|
77
|
+
re = /\S+|\s+/
|
78
|
+
until text.empty?
|
79
|
+
chunk = text.slice!( re )
|
80
|
+
if chunk !~ /^ /
|
81
|
+
element.add_text( chunk.string )
|
82
|
+
else
|
83
|
+
space_element = Element.new( 'text:s' )
|
84
|
+
space_element.attributes['text:c'] = chunk.size.to_s
|
85
|
+
element.add( space_element )
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def add_span( token, element )
|
91
|
+
case token[:type]
|
92
|
+
when :italic
|
93
|
+
element.add( span_for( token.string, "T1" ))
|
94
|
+
when :code
|
95
|
+
element.add( span_for( token.string, "CD1" ))
|
96
|
+
when :bold
|
97
|
+
element.add( span_for( token.string, "T2" ))
|
98
|
+
when :normal
|
99
|
+
element.add_text( token.string )
|
100
|
+
when :footnote
|
101
|
+
element.add( footnote_for( token.string ) )
|
102
|
+
else
|
103
|
+
raise "Dont know what to do with #{token}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def span_for( text, style )
|
108
|
+
span = Element.new( "text:span" )
|
109
|
+
span.attributes['text:style-name'] = style
|
110
|
+
span.text = remove_escapes(text)
|
111
|
+
span
|
112
|
+
end
|
113
|
+
|
114
|
+
def remove_escapes( text )
|
115
|
+
text = text.clone
|
116
|
+
|
117
|
+
results = ''
|
118
|
+
|
119
|
+
until text.empty?
|
120
|
+
match = /\\(.)/.match( text )
|
121
|
+
if match.nil?
|
122
|
+
results << text
|
123
|
+
text = ''
|
124
|
+
else
|
125
|
+
unless match.pre_match.empty?
|
126
|
+
results << match.pre_match
|
127
|
+
end
|
128
|
+
results << match[1]
|
129
|
+
text = match.post_match
|
130
|
+
end
|
131
|
+
end
|
132
|
+
results
|
133
|
+
end
|
134
|
+
|
135
|
+
def footnote_for(text )
|
136
|
+
note_element = Element.new( "text:note" )
|
137
|
+
note_element.attributes["text:id"] ="ftn#{@@footnote_number}"
|
138
|
+
note_element.attributes["text:note-class"] ="footnote"
|
139
|
+
|
140
|
+
cit = Element.new( "text:note-citation" )
|
141
|
+
cit.add_text( "#{@@footnote_number}" )
|
142
|
+
note_element.add( cit )
|
143
|
+
|
144
|
+
note_body = Element.new( "text:note-body" )
|
145
|
+
note_paragraph = Element.new( "text:p" )
|
146
|
+
note_paragraph.attributes['text:style-name'] = 'FTN'
|
147
|
+
add_body_text(text, note_paragraph)
|
148
|
+
|
149
|
+
note_body.add( note_paragraph )
|
150
|
+
note_element.add( note_body )
|
151
|
+
@@footnote_number += 1
|
152
|
+
note_element
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'zip/zipfilesystem'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
SKEL = File.expand_path('../skel.odt', __FILE__)
|
6
|
+
|
7
|
+
class OdtReplacer < Processor
|
8
|
+
|
9
|
+
def initialize( output_path )
|
10
|
+
@output_path = output_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def process( new_content )
|
14
|
+
FileUtils.cp( SKEL, @output_path )
|
15
|
+
|
16
|
+
Zip::ZipFile.open( @output_path ) do |zipfile|
|
17
|
+
zipfile.file.open("content.xml", "w") do |content|
|
18
|
+
content.print( new_content )
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class ParagraphJoiner
|
2
|
+
def process( paragraphs )
|
3
|
+
processed_paragraphs = []
|
4
|
+
new_p = nil
|
5
|
+
paragraphs.each do |paragraph|
|
6
|
+
do_join = join?(paragraph)
|
7
|
+
|
8
|
+
if join?(paragraph)
|
9
|
+
if new_p
|
10
|
+
new_p.string += " "
|
11
|
+
new_p.string += paragraph
|
12
|
+
else
|
13
|
+
new_p = paragraph
|
14
|
+
end
|
15
|
+
else
|
16
|
+
if new_p
|
17
|
+
processed_paragraphs << new_p
|
18
|
+
new_p = nil
|
19
|
+
end
|
20
|
+
processed_paragraphs << paragraph unless skip?(paragraph)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
processed_paragraphs << new_p if new_p
|
24
|
+
processed_paragraphs
|
25
|
+
end
|
26
|
+
|
27
|
+
def join?(paragraph)
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def skip?(paragraph)
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class BodyParagraphJoiner < ParagraphJoiner
|
37
|
+
def join?(paragraph)
|
38
|
+
return false unless paragraph[:type] == :body
|
39
|
+
return false if paragraph.empty?
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def skip?(paragraph)
|
44
|
+
paragraph[:type] == :body && paragraph.empty?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class CodeParagraphJoiner < ParagraphJoiner
|
49
|
+
def join?(paragraph)
|
50
|
+
return false unless paragraph[:type] == :code
|
51
|
+
true
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
class Processor
|
2
|
+
end
|
3
|
+
|
4
|
+
class TextPrinter
|
5
|
+
def process( text )
|
6
|
+
puts text
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class FileWriter
|
11
|
+
def initialize(output_file)
|
12
|
+
@output_file = output_file
|
13
|
+
end
|
14
|
+
|
15
|
+
def process(content)
|
16
|
+
File.open(@output_file, 'w') do |f|
|
17
|
+
f.print(content.to_s)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Printer
|
23
|
+
def process( paragraphs )
|
24
|
+
paragraphs.each {|p| puts "#{p.type}: #{p.text}"}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class TextReader < Processor
|
29
|
+
def initialize( path )
|
30
|
+
@path = path
|
31
|
+
end
|
32
|
+
|
33
|
+
def process( ignored )
|
34
|
+
lines = File.open( @path ).readlines
|
35
|
+
lines.map! { |line| line.rstrip }
|
36
|
+
lines
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
#class CommandProcessor
|
41
|
+
#
|
42
|
+
# def process( lines )
|
43
|
+
# paragraphs = []
|
44
|
+
# lines.each do |line|
|
45
|
+
# cmd, text = parse_line( line )
|
46
|
+
# if cmd.empty?
|
47
|
+
# cmd = :text
|
48
|
+
# else
|
49
|
+
# cmd = cmd.sub(/^./, '').to_sym
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# text.attr(:type, cmd)
|
53
|
+
# text.attr(:original_text, line)
|
54
|
+
# paragraphs << text
|
55
|
+
# end
|
56
|
+
# paragraphs
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# def parse_line( line )
|
60
|
+
# match_data = /^(\.\w+ ?)(.*)/.match(line)
|
61
|
+
# if match_data
|
62
|
+
# cmd = match_data[1].strip
|
63
|
+
# text = match_data[2]
|
64
|
+
# else
|
65
|
+
# cmd = ''
|
66
|
+
# text = line
|
67
|
+
# end
|
68
|
+
# [ cmd.strip, text ]
|
69
|
+
# end
|
70
|
+
#end
|
71
|
+
#
|
72
|
+
#class ParagraphTypeAssigner
|
73
|
+
# def process( paragraphs )
|
74
|
+
# processed_paragraphs = []
|
75
|
+
#
|
76
|
+
# current_type = :body
|
77
|
+
#
|
78
|
+
# paragraphs.each do |paragraph|
|
79
|
+
# type = paragraph | :type
|
80
|
+
# if (type == :body) or (type == :code) or (type == :quote)
|
81
|
+
# current_type = type
|
82
|
+
#
|
83
|
+
# elsif type == :text
|
84
|
+
# new_p = paragraph.clone
|
85
|
+
# new_p.attr(:type, current_type)
|
86
|
+
# processed_paragraphs << new_p
|
87
|
+
#
|
88
|
+
# else
|
89
|
+
# processed_paragraphs << paragraph
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# current_type = :body if [ :section, :title, :code1 ].include?(type)
|
93
|
+
# end
|
94
|
+
# processed_paragraphs
|
95
|
+
# end
|
96
|
+
#end
|
97
|
+
#
|
98
|
+
#class CodeTypeRefiner
|
99
|
+
# def process( paragraphs )
|
100
|
+
# processed_paragraphs = []
|
101
|
+
#
|
102
|
+
# previous_type = nil
|
103
|
+
#
|
104
|
+
# paragraphs.each_with_index do |paragraph, i|
|
105
|
+
# type = paragraph | :type
|
106
|
+
# previous_type = ( paragraphs.first == paragraph) ? nil : paragraphs[i-1] | :type
|
107
|
+
# next_type = ( paragraphs.last == paragraph) ? nil : paragraphs[i+1] | :type
|
108
|
+
# new_type = code_type_for( previous_type, type, next_type )
|
109
|
+
# new_p = paragraph.clone
|
110
|
+
# new_p.attr(:type, new_type)
|
111
|
+
# processed_paragraphs << new_p
|
112
|
+
# end
|
113
|
+
# processed_paragraphs
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# def code_type_for( previous_type, type, next_type )
|
117
|
+
# if type != :code
|
118
|
+
# new_type = type
|
119
|
+
#
|
120
|
+
# elsif previous_type == :code and next_type == :code
|
121
|
+
# new_type = :middle_code
|
122
|
+
#
|
123
|
+
# elsif previous_type == :code
|
124
|
+
# new_type = :end_code
|
125
|
+
#
|
126
|
+
# elsif next_type == :code
|
127
|
+
# new_type = :first_code
|
128
|
+
#
|
129
|
+
# else
|
130
|
+
# new_type = :single_code
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# new_type
|
134
|
+
# end
|
135
|
+
#end
|
136
|
+
#
|
137
|
+
#class SimilarParagraphJoiner
|
138
|
+
# def initialize(target_type)
|
139
|
+
# @target_type = target_type
|
140
|
+
# end
|
141
|
+
#
|
142
|
+
# def process( paragraphs )
|
143
|
+
# processed_paragraphs = []
|
144
|
+
# new_p = nil
|
145
|
+
# paragraphs.each do |paragraph|
|
146
|
+
# if (paragraph | :type) != @target_type
|
147
|
+
# processed_paragraphs << new_p if new_p
|
148
|
+
# new_p = nil
|
149
|
+
# processed_paragraphs << paragraph
|
150
|
+
#
|
151
|
+
# elsif new_p
|
152
|
+
# new_p += "\n"
|
153
|
+
# new_p += paragraph
|
154
|
+
#
|
155
|
+
# else
|
156
|
+
# new_p = paragraph
|
157
|
+
# end
|
158
|
+
# end
|
159
|
+
# processed_paragraphs << new_p if new_p
|
160
|
+
# processed_paragraphs
|
161
|
+
# end
|
162
|
+
#end
|
163
|
+
#
|
164
|
+
#class TextParagraphJoiner
|
165
|
+
# def process( paragraphs )
|
166
|
+
# processed_paragraphs = []
|
167
|
+
#
|
168
|
+
# new_p = nil
|
169
|
+
#
|
170
|
+
# paragraphs.each do |paragraph|
|
171
|
+
# if (paragraph | type != :body) and (paragraph | type != :quote)
|
172
|
+
# processed_paragraphs << new_p if new_p
|
173
|
+
# new_p = nil
|
174
|
+
# processed_paragraphs << paragraph
|
175
|
+
#
|
176
|
+
# elsif paragraph.blank?
|
177
|
+
# processed_paragraphs << new_p if new_p
|
178
|
+
# new_p = nil
|
179
|
+
#
|
180
|
+
# elsif new_p
|
181
|
+
# new_p += paragraph.text
|
182
|
+
#
|
183
|
+
# else
|
184
|
+
# new_p = paragraph
|
185
|
+
# end
|
186
|
+
# end
|
187
|
+
# processed_paragraphs << new_p if new_p
|
188
|
+
# processed_paragraphs
|
189
|
+
# end
|
190
|
+
#end
|
191
|
+
#
|
192
|
+
#
|
Binary file
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
TEMPLATE = File.expand_path('../content.xml.erb', __FILE__)
|
4
|
+
|
5
|
+
class TemplateExpander < Processor
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@template = ERB.new(File.read(TEMPLATE))
|
9
|
+
end
|
10
|
+
|
11
|
+
def process( elements )
|
12
|
+
content = elements.join( "\n" )
|
13
|
+
@template.result(binding)
|
14
|
+
end
|
15
|
+
end
|
data/lib/notroff/text.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
class Text
|
4
|
+
attr_accessor :string, :attrs
|
5
|
+
|
6
|
+
def initialize(initial_string, initial_attrs={})
|
7
|
+
@string = initial_string.to_str.clone
|
8
|
+
@attrs = initial_attrs.clone
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.wrap_method(method_name)
|
12
|
+
define_method method_name do |*args|
|
13
|
+
result = @string.send(method_name, *args)
|
14
|
+
return Text.new(result, attrs) if result.kind_of? String
|
15
|
+
result
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.wrap_methods(method_names)
|
20
|
+
method_names.each {|name| wrap_method(name)}
|
21
|
+
end
|
22
|
+
|
23
|
+
wrap_methods( String.instance_methods(false) )
|
24
|
+
|
25
|
+
def [](name)
|
26
|
+
@attrs[name]
|
27
|
+
end
|
28
|
+
|
29
|
+
def []=(name, value)
|
30
|
+
@attrs[name] = value
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(other)
|
34
|
+
return false unless other.kind_of? Text
|
35
|
+
@string == other.string && @attrs == other.attrs
|
36
|
+
end
|
37
|
+
|
38
|
+
def clone
|
39
|
+
Text.new(string, attrs)
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_str
|
43
|
+
#puts "To string: #{@string.class} #{@string}"
|
44
|
+
@string
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
@string
|
49
|
+
end
|
50
|
+
|
51
|
+
def inspect
|
52
|
+
"#{@string.inspect} :: #{@attrs.inspect}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class String
|
57
|
+
def to_text
|
58
|
+
Text.new(self)
|
59
|
+
end
|
60
|
+
|
61
|
+
alias_method :old_double_equals, :'=='
|
62
|
+
|
63
|
+
def ==(other)
|
64
|
+
return self == other.string if other.kind_of?(Text)
|
65
|
+
old_double_equals(other)
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#require 'rexml/document'
|
2
|
+
require 'pp'
|
3
|
+
#require 'zip/zipfilesystem'
|
4
|
+
#require 'fileutils'
|
5
|
+
#require 'erb'
|
6
|
+
|
7
|
+
FormatOdtDir = File.dirname(__FILE__)
|
8
|
+
|
9
|
+
require "#{FormatOdtDir}/utils"
|
10
|
+
require "#{FormatOdtDir}/processor"
|
11
|
+
require "#{FormatOdtDir}/code_processors"
|
12
|
+
require "#{FormatOdtDir}/odt_renderer"
|
13
|
+
require "#{FormatOdtDir}/odt_replacer"
|
14
|
+
require "#{FormatOdtDir}/template_expander"
|
15
|
+
|
16
|
+
|
17
|
+
class Editor
|
18
|
+
def initialize( input, output )
|
19
|
+
@input = input
|
20
|
+
@output = output
|
21
|
+
@processors = []
|
22
|
+
@processors << TextReader.new( @input )
|
23
|
+
@processors << CommandProcessor.new
|
24
|
+
@processors << ParagraphTypeAssigner.new
|
25
|
+
@processors << ProgramOutputInserter.new
|
26
|
+
@processors << C1Inserter.new
|
27
|
+
@processors << IncInserter.new
|
28
|
+
@processors << CodeInserter.new
|
29
|
+
@processors << LastOutputInserter.new
|
30
|
+
@processors << CodeTagFilter.new
|
31
|
+
@processors << CodeTypeRefiner.new
|
32
|
+
@processors << OdtRenderer.new
|
33
|
+
@processors << TemplateExpander.new( File.read( ContentTemplate ) )
|
34
|
+
@processors << OdtReplacer.new( OdtSkeleton, @output )
|
35
|
+
end
|
36
|
+
|
37
|
+
def process
|
38
|
+
result = nil
|
39
|
+
@processors.each do |processor|
|
40
|
+
result = processor.process( result )
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
f = Editor.new( ARGV[0], ARGV[1] )
|
47
|
+
f.process
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
|