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 ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ require 'pp'
3
+ require "notroff"
4
+
5
+ options={}
6
+ formatter_class = HtmlFormatter
7
+
8
+ while true
9
+ if ARGV.first == '-o'
10
+ formatter_class = OdtFormatter
11
+ ARGV.shift
12
+ elsif ARGV.first == '-d'
13
+ formatter_class = DocbookFormatter
14
+ ARGV.shift
15
+ elsif ARGV.first == '-v'
16
+ Logger.verbose = true
17
+ ARGV.shift
18
+ else
19
+ break
20
+ end
21
+ end
22
+
23
+ unless ARGV.size == 2
24
+ raise "Usage: notroff [-d] <nr file> <output file>"
25
+ end
26
+
27
+
28
+ f = formatter_class.new( ARGV[0], ARGV[1] )
29
+ f.process
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
@@ -0,0 +1,9 @@
1
+ class CodeScrubber
2
+ def process( paragraphs )
3
+ paragraphs.each do |p|
4
+ next unless p[:type] == :code
5
+ p.string.gsub!(/##.*$/, '')
6
+ end
7
+ paragraphs
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ class CodeTypeRefiner
2
+ def process( paragraphs )
3
+ processed_paragraphs = []
4
+
5
+ previous_type = nil
6
+
7
+ paragraphs.each_with_index do |paragraph, i|
8
+ type = paragraph[:type]
9
+ previous_type = ( paragraphs.first == paragraph) ? nil : paragraphs[i-1][:type]
10
+ next_type = ( paragraphs.last == paragraph) ? nil : paragraphs[i+1][:type]
11
+ new_type = code_type_for( previous_type, type, next_type )
12
+ paragraph[:type] = new_type
13
+ processed_paragraphs << paragraph
14
+ end
15
+ processed_paragraphs
16
+ end
17
+
18
+ def code_type_for( previous_type, type, next_type )
19
+ if type != :code
20
+ new_type = type
21
+
22
+ elsif previous_type == :code and next_type == :code
23
+ new_type = :middle_code
24
+
25
+ elsif previous_type == :code
26
+ new_type = :end_code
27
+
28
+ elsif next_type == :code
29
+ new_type = :first_code
30
+
31
+ else
32
+ new_type = :single_code
33
+ end
34
+
35
+ new_type
36
+ end
37
+ end
@@ -0,0 +1,28 @@
1
+ class CommandProcessor
2
+ def process( lines )
3
+ paragraphs = []
4
+ lines.each do |line|
5
+ cmd, text = parse_line( line )
6
+ if cmd.empty?
7
+ cmd = :text
8
+ else
9
+ cmd = cmd.sub(/^./, '').to_sym
10
+ end
11
+ para = Text.new(text, :type => cmd, :original_text => line.to_s)
12
+ paragraphs << para
13
+ end
14
+ paragraphs
15
+ end
16
+
17
+ def parse_line( line )
18
+ match_data = /^(\.\w+ ?)(.*)/.match(line)
19
+ if match_data
20
+ cmd = match_data[1].strip
21
+ text = match_data[2]
22
+ else
23
+ cmd = ''
24
+ text = line
25
+ end
26
+ [ cmd.strip, text ]
27
+ end
28
+ end
@@ -0,0 +1,52 @@
1
+ class CompositeProcessor
2
+ def initialize(*args)
3
+ @processors = args
4
+ end
5
+
6
+ def add_processor(p)
7
+ @processors << p
8
+ end
9
+
10
+ def prepend_processor(p)
11
+ @processors = [p] + @processors
12
+ end
13
+
14
+ def process(paras=[])
15
+ @processors.each do |processor|
16
+ Logger.log "Applying processor #{processor.class} to #{paras.size} paragraphs"
17
+ dump(paras)
18
+ Logger.log
19
+ paras = processor.process( paras )
20
+ Logger.log "After processor #{processor.class}"
21
+ Logger.log
22
+ end
23
+ paras
24
+ end
25
+
26
+ def dump(data)
27
+ return unless Verbose
28
+ Logger.log "======="
29
+ if data.nil?
30
+ Logger.log "data: nil"
31
+ elsif data.kind_of?(Array)
32
+ Logger.log "data with #{data.size} items:"
33
+ data.each_with_index {|item, i| Logger.log "[#{i}] - #{item.inspect}" }
34
+ else
35
+ Logger.log data
36
+ end
37
+ Logger.log "======="
38
+ end
39
+ end
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
@@ -0,0 +1,110 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
3
+ xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
4
+ xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
5
+ xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
6
+ xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"
7
+ xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
8
+ xmlns:xlink="http://www.w3.org/1999/xlink"
9
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
10
+ xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0"
11
+ xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"
12
+ xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
13
+ xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0"
14
+ xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"
15
+ xmlns:math="http://www.w3.org/1998/Math/MathML"
16
+ xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0"
17
+ xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0"
18
+ xmlns:ooo="http://openoffice.org/2004/office"
19
+ xmlns:ooow="http://openoffice.org/2004/writer"
20
+ xmlns:oooc="http://openoffice.org/2004/calc"
21
+ xmlns:dom="http://www.w3.org/2001/xml-events"
22
+ xmlns:xforms="http://www.w3.org/2002/xforms"
23
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
24
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
25
+ xmlns:field="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:field:1.0"
26
+ office:version="1.1">
27
+ <office:scripts />
28
+ <office:font-face-decls>
29
+ <style:font-face style:name="Wingdings"
30
+ svg:font-family="Wingdings" style:font-pitch="variable"
31
+ style:font-charset="x-symbol" />
32
+ <style:font-face style:name="Symbol" svg:font-family="Symbol"
33
+ style:font-family-generic="roman" style:font-pitch="variable"
34
+ style:font-charset="x-symbol" />
35
+ <style:font-face style:name="Courier New"
36
+ svg:font-family="'Courier New'"
37
+ style:font-family-generic="modern" />
38
+ <style:font-face style:name="Courier"
39
+ svg:font-family="Courier, 'Courier New'"
40
+ style:font-family-generic="modern" />
41
+ <style:font-face style:name="Nimbus Mono L"
42
+ svg:font-family="'Nimbus Mono L', 'Courier New'"
43
+ style:font-family-generic="modern" />
44
+ <style:font-face style:name="Nimbus Roman No9 L"
45
+ svg:font-family="'Nimbus Roman No9 L'"
46
+ style:font-family-generic="roman"
47
+ style:font-pitch="variable" />
48
+ <style:font-face style:name="Times" svg:font-family="Times"
49
+ style:font-family-generic="roman"
50
+ style:font-pitch="variable" />
51
+ <style:font-face style:name="Times New Roman"
52
+ svg:font-family="'Times New Roman'"
53
+ style:font-family-generic="roman"
54
+ style:font-pitch="variable" />
55
+ <style:font-face style:name="Arial" svg:font-family="Arial"
56
+ style:font-family-generic="swiss"
57
+ style:font-pitch="variable" />
58
+ <style:font-face style:name="Arial Unicode MS"
59
+ svg:font-family="'Arial Unicode MS'"
60
+ style:font-family-generic="swiss"
61
+ style:font-pitch="variable" />
62
+ <style:font-face style:name="Helvetica"
63
+ svg:font-family="Helvetica" style:font-family-generic="swiss"
64
+ style:font-pitch="variable" />
65
+ <style:font-face style:name="Tahoma" svg:font-family="Tahoma"
66
+ style:font-family-generic="swiss"
67
+ style:font-pitch="variable" />
68
+ <style:font-face style:name="Verdana" svg:font-family="Verdana"
69
+ style:font-family-generic="swiss"
70
+ style:font-pitch="variable" />
71
+ <style:font-face style:name="DejaVu Sans"
72
+ svg:font-family="'DejaVu Sans'"
73
+ style:font-family-generic="system"
74
+ style:font-pitch="variable" />
75
+ </office:font-face-decls>
76
+ <office:automatic-styles>
77
+ <style:style style:name="P1" style:family="paragraph"
78
+ style:parent-style-name="Standard">
79
+ <style:paragraph-properties fo:text-align="end"
80
+ style:justify-single-word="false" />
81
+ </style:style>
82
+ <style:style style:name="P2" style:family="paragraph"
83
+ style:parent-style-name="HA" style:master-page-name="Standard">
84
+ <style:paragraph-properties style:page-number="1" />
85
+ </style:style>
86
+ <style:style style:name="P3" style:family="paragraph"
87
+ style:parent-style-name="FTN">
88
+ <style:paragraph-properties fo:margin-left="0.1598in"
89
+ fo:margin-right="0in" fo:margin-top="0.0417in"
90
+ fo:margin-bottom="0.0417in"
91
+ style:line-height-at-least="0.1807in"
92
+ fo:text-indent="-0.1598in" style:auto-text-indent="false" />
93
+ </style:style>
94
+ <style:style style:name="T1" style:family="text">
95
+ <style:text-properties fo:font-style="italic"
96
+ style:font-style-asian="italic"
97
+ style:font-style-complex="italic" />
98
+ </style:style>
99
+ <style:style style:name="T2" style:family="text">
100
+ <style:text-properties fo:font-weight="bold"
101
+ style:font-weight-asian="bold"
102
+ style:font-weight-complex="bold" />
103
+ </style:style>
104
+ </office:automatic-styles>
105
+ <office:body>
106
+ <office:text>
107
+ <%=content%>
108
+ </office:text>
109
+ </office:body>
110
+ </office:document-content>
@@ -0,0 +1,155 @@
1
+ require 'rexml/document'
2
+ require 'pp'
3
+
4
+ class DocbookRenderer < Processor
5
+ include Tokenize
6
+ include REXML
7
+
8
+ def process( paragraphs )
9
+ @author = 'John Smith'
10
+ @title = 'Wealth of Nations'
11
+ @chapters = []
12
+ @chapter = nil
13
+ @section = nil
14
+
15
+ paragraphs.each do |paragraph|
16
+ format( paragraph )
17
+ end
18
+ @book = Element.new('book')
19
+ @book.add_namespace('http://docbook.org/ns/docbook')
20
+ @book.add_attribute('version', '5.0')
21
+ @book.add_element element_for('title', @title)
22
+ @book.add_element element_for('author', @author)
23
+ @chapters.each {|ch| @book << ch}
24
+ doc = Document.new
25
+ decl = XMLDecl.new
26
+ decl.version = '1.0'
27
+ doc << decl
28
+ doc << @book
29
+ doc
30
+ end
31
+
32
+ def element_for(type, text)
33
+ result = Element.new(type)
34
+ result.add_text(text)
35
+ result
36
+ end
37
+
38
+ def format( p )
39
+ type = p[:type]
40
+ text = p.string
41
+
42
+ return nil if text.empty? and :type != :code
43
+
44
+ case type
45
+ when :title
46
+ @title = text
47
+ when :author
48
+ @author = text
49
+ when :chapter
50
+ Logger.log "adding chapter #{text}"
51
+ @chapter = Element.new('chapter')
52
+ @chapters << @chapter
53
+ @section = nil
54
+ title_element = Element.new('title')
55
+ add_body_text(title_element, text)
56
+ @chapter.add_element(title_element)
57
+ when :section
58
+ Logger.log "adding section #{text}"
59
+ @section = Element.new('section')
60
+ @chapter.add(@section)
61
+ title_element = Element.new('title')
62
+ add_body_text(title_element, text)
63
+ @section.add_element(title_element)
64
+ when :body
65
+ Logger.log "adding body #{text[0..5]}"
66
+ paragraph = Element.new('para')
67
+ add_body_text(paragraph, text)
68
+ add_content_element(paragraph)
69
+ when :code
70
+ add_content_element(code_element(type, text))
71
+ else
72
+ raise "#{type}???"
73
+ end
74
+ end
75
+
76
+ def add_content_element(el)
77
+ if @section
78
+ @section.add_element(el)
79
+ elsif @chapter
80
+ @chapter.add_element(el)
81
+ else
82
+ raise "No chapter to add #{el} to"
83
+ end
84
+ end
85
+
86
+ def text_element(type, text)
87
+ element = [ tag_for(type) ]
88
+ add_body_text(element, text)
89
+ element
90
+ end
91
+
92
+ def tag_for(type)
93
+ case type
94
+ when :body
95
+ 'para'
96
+ when :text
97
+ 'p'
98
+ when :author
99
+ 'h3'
100
+ when :section
101
+ 'h3'
102
+ when :chapter
103
+ 'chapter'
104
+ when :sec
105
+ 'h3'
106
+ when :title
107
+ 'h2'
108
+ else
109
+ raise "Dont know what to do with #{type}"
110
+ end
111
+ end
112
+
113
+ def add_body_text( element, text )
114
+ tokens = tokenize_body_text( text )
115
+ tokens.each {|token| add_span( token, element ) }
116
+ end
117
+
118
+ def add_span( token, element )
119
+ Logger.log "Add span: token: #{token} element: #{element}"
120
+ case token[:type]
121
+ when :italic
122
+ element.add( span_for( token.string, "emphasis" ))
123
+ when :code
124
+ element.add( span_for( token.string, "code" ))
125
+ when :bold
126
+ element.add( span_for( token.string, "emphasis" ))
127
+ when :normal
128
+ element.add_text( token.string )
129
+ when :footnote
130
+ element.add(footnote_for(token.string))
131
+ else
132
+ raise "Dont know what to do with #{token}"
133
+ end
134
+ end
135
+
136
+ def footnote_for( text )
137
+ fn = Element.new('footnote')
138
+ fn.add_element(element_for('para', text))
139
+ fn
140
+ end
141
+
142
+ def code_element(type, text)
143
+ element = Element.new('informalexample')
144
+ prog_element = element_for('programlisting', text)
145
+ prog_element.add_attribute('xml:space', 'preserve')
146
+ element.add_element(prog_element)
147
+ element
148
+ end
149
+
150
+ def span_for( text, style )
151
+ span = Element.new(style)
152
+ span.text = remove_escapes(text)
153
+ span
154
+ end
155
+ end
@@ -0,0 +1,132 @@
1
+ class BetweenFilter
2
+ def initialize(re1, re2=re1)
3
+ @re1 = re1
4
+ @re2 = re2
5
+ end
6
+
7
+ def process(paras)
8
+ state = :before_first
9
+ paras.each do |para|
10
+ if state == :before_first and @re1 =~ para
11
+ para[:included] = true
12
+ state = :after_first
13
+ break if para =~ @re2
14
+ elsif state == :after_first
15
+ para[:included] = true
16
+ break if para =~ @re2
17
+ end
18
+ end
19
+ paras
20
+ end
21
+ end
22
+
23
+ class DefinitionFilter
24
+ def initialize(def_re, include_body=true)
25
+ @def_re = def_re
26
+ @include_body = include_body
27
+ end
28
+
29
+ def process(paras)
30
+ state = :before_def
31
+ end_re = nil
32
+ paras.each do |para|
33
+ Logger.log para, (@def_re =~ para)
34
+ if state == :before_def and @def_re =~ para
35
+ para[:included] = true
36
+ end_re = Regexp.new( "^#{' ' * para.string.indent_depth}end" )
37
+ state = :after_def
38
+ elsif state == :after_def and end_re =~ para.string
39
+ para[:included] = true
40
+ break
41
+ elsif state == :after_def and @include_body
42
+ para[:included] = true
43
+ end
44
+ end
45
+ paras
46
+ end
47
+ end
48
+
49
+ class MethodFilter < DefinitionFilter
50
+ def initialize(method_name, include_body)
51
+ super /^ *def +#{method_name}(\(|$| )/, include_body
52
+ end
53
+ end
54
+
55
+ class ClassFilter < DefinitionFilter
56
+ def initialize(class_name, include_body)
57
+ super /^ *class +#{class_name}(\(|$| )/, include_body
58
+ end
59
+ end
60
+
61
+ class ModuleFilter < DefinitionFilter
62
+ def initialize(module_name, include_body)
63
+ super /^ *module +#{module_name}(\(|$| )/, include_body
64
+ end
65
+ end
66
+
67
+ class EmbeddedRubyProcessor
68
+ def process(paragraphs)
69
+ new_paragraphs = []
70
+ paragraphs.each do |p|
71
+ if p[:type] == :x
72
+ Logger.log p
73
+ results = process_command(p.string)
74
+ new_paragraphs << results if results
75
+ else
76
+ new_paragraphs << p
77
+ end
78
+ end
79
+ new_paragraphs.flatten
80
+ end
81
+
82
+ def process_command(ruby_expression)
83
+ Logger.log "Ruby expression: #{ruby_expression}"
84
+ lines = eval(ruby_expression, binding)
85
+ end
86
+
87
+ def embed(*filters, &block)
88
+ paras = block.call.map {|line| line.rstrip}
89
+ paras.map! {|p| Text.new(p, :type => :code)}
90
+ Logger.log "EMBED: #{paras}"
91
+ unless filters.empty?
92
+ filters.each {|f| paras = f.process(paras)}
93
+ paras = paras.find_all {|p| p[:included]}
94
+ end
95
+ paras
96
+ end
97
+
98
+ def inc(path, *filters)
99
+ embed(*filters) {File.readlines(path)}
100
+ end
101
+
102
+ def run(command, *filters)
103
+ embed(*filters) {File.popen(command).readlines}
104
+ end
105
+
106
+ def matches(re1, re2=re1)
107
+ BetweenFilter.new(re1, re2)
108
+ end
109
+
110
+ def method(name, include_body=true)
111
+ MethodFilter.new(name, include_body)
112
+ end
113
+
114
+ def clazz(name, include_body=true)
115
+ ClassFilter.new(name, include_body)
116
+ end
117
+
118
+ def mod(name, include_body=true)
119
+ ModuleFilter.new(name, include_body)
120
+ end
121
+
122
+ def indent(delta_indent, paragraphs)
123
+ paragraphs.map do |p|
124
+ if delta_indent > 0
125
+ p.string = (" " * delta_indent) + p.string
126
+ elsif delta_indent < 0
127
+ p.string.sub!( ' ' * delta_indent.abs, '' )
128
+ end
129
+ end
130
+ paragraphs
131
+ end
132
+ end
@@ -0,0 +1,15 @@
1
+ class Filter
2
+ def process(paragraphs)
3
+ paragraphs.find_all {|p| included?(p)}
4
+ end
5
+
6
+ def included?(paragraph)
7
+ true
8
+ end
9
+ end
10
+
11
+ class IncludedFilter < Filter
12
+ def included?(paragraph)
13
+ paragraph[:included]
14
+ end
15
+ end
@@ -0,0 +1,54 @@
1
+ class Formatter < CompositeProcessor
2
+ def initialize()
3
+ super
4
+ add_processor CommandProcessor.new
5
+ add_processor TypeAssigner.new
6
+ add_processor EmbeddedRubyProcessor.new
7
+ add_processor CodeScrubber.new
8
+ add_processor BodyParagraphJoiner.new
9
+ end
10
+ end
11
+
12
+ class HtmlFormatter < Formatter
13
+ def initialize(input, output)
14
+ super()
15
+ prepend_processor FileReader.new(input)
16
+ add_processor CodeParagraphJoiner.new
17
+ add_processor HtmlRenderer.new
18
+ add_processor FileWriter.new(output)
19
+ end
20
+ end
21
+
22
+ class DocbookFormatter < Formatter
23
+ def initialize(input, output)
24
+ super()
25
+ prepend_processor FileReader.new(input)
26
+ add_processor CodeParagraphJoiner.new
27
+ add_processor DocbookRenderer.new
28
+ add_processor FileWriter.new(output)
29
+ end
30
+ end
31
+
32
+ class OdtFormatter < Formatter
33
+ def initialize(input, output)
34
+ super()
35
+ prepend_processor FileReader.new(input)
36
+ add_processor CodeTypeRefiner.new
37
+ add_processor OdtRenderer.new
38
+ add_processor TemplateExpander.new
39
+ add_processor OdtReplacer.new(output)
40
+ end
41
+ end
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+