nsf 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ /.redcar
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :gemcutter
2
+
3
+ # Specify your gem's dependencies in nsf.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ nsf (0.0.10)
5
+ clbustos-rtf (>= 0.1.0)
6
+ nokogiri (>= 1.4.4)
7
+ prawn (>= 0.0.0)
8
+ ruby-rtf (>= 0.0.0)
9
+
10
+ GEM
11
+ remote: http://rubygems.org/
12
+ specs:
13
+ Ascii85 (1.0.2)
14
+ afm (0.2.0)
15
+ clbustos-rtf (0.5.0)
16
+ hashery (2.1.1)
17
+ mini_portile (0.5.1)
18
+ nokogiri (1.6.0)
19
+ mini_portile (~> 0.5.0)
20
+ pdf-reader (1.3.3)
21
+ Ascii85 (~> 1.0.0)
22
+ afm (~> 0.2.0)
23
+ hashery (~> 2.0)
24
+ ruby-rc4
25
+ ttfunk
26
+ prawn (0.12.0)
27
+ pdf-reader (>= 0.9.0)
28
+ ttfunk (~> 1.0.2)
29
+ ruby-rc4 (0.1.5)
30
+ ruby-rtf (0.0.1)
31
+ ttfunk (1.0.3)
32
+
33
+ PLATFORMS
34
+ ruby
35
+
36
+ DEPENDENCIES
37
+ bundler (>= 1.0.0)
38
+ nsf!
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/nsf2html ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "nsf"
4
+
5
+ ARGV.each do |filename|
6
+ if !File.exist?(filename)
7
+ puts "Could not find #{filename}, skipping"
8
+ next
9
+ end
10
+
11
+ content = File.read(filename)
12
+
13
+ doc = Nsf::Document.from_nsf(content)
14
+
15
+ File.open("#{filename}.html", "w") do |f|
16
+ f << <<-EOF
17
+ <!doctype html>
18
+ <html>
19
+ <head>
20
+ <meta charset="utf-8">
21
+ <title>#{doc.title}</title>
22
+ </head>
23
+ <body>
24
+ #{doc.to_html}
25
+ </body>
26
+ </html>
27
+ EOF
28
+ end
29
+ end
data/lib/nsf.rb ADDED
@@ -0,0 +1,79 @@
1
+ require 'nsf/standard_extensions'
2
+
3
+ module Nsf
4
+ class Document
5
+ attr_accessor :nodes
6
+
7
+ def initialize(nodes)
8
+ @nodes = nodes
9
+ end
10
+
11
+ def title
12
+ title_node = nodes.detect { |n| n.is_a?(Heading) && n.level == 1 }
13
+ if title_node && title_node.text.present?
14
+ title_node.text
15
+ elsif nodes.first && (nodes.first.text.length < 100)
16
+ nodes.first.text
17
+ else
18
+ nil
19
+ end
20
+ end
21
+
22
+ def toc
23
+ nodes.select { |n| n.is_a?(Nsf::Heading) }
24
+ end
25
+
26
+ def self.from(text, format)
27
+ self.send("from_#{format}", text)
28
+ end
29
+
30
+ def self.from_blocks(blocks)
31
+ self.new(blocks.map do |block|
32
+ if block =~ /^#+ /
33
+ Heading.from_nsf(block)
34
+ elsif block =~ /^ /
35
+ Fixedblock.from_nsf(block)
36
+ else
37
+ Paragraph.from_nsf(block)
38
+ end
39
+ end)
40
+ end
41
+ end
42
+
43
+ class Paragraph
44
+ attr_reader :text
45
+
46
+ BOLD_ITALIC_REGEX = /(?:(\W|^)(\*)|(\*)(\W|$)|(\W|^)(_)|(_)(\W|$))/
47
+
48
+ def initialize(text)
49
+ @text = text
50
+ end
51
+
52
+ def self.from_nsf(text)
53
+ self.new(text.gsub(/[[:space:]]+/, ' ').strip)
54
+ end
55
+ end
56
+
57
+ class Fixedblock < Paragraph
58
+ end
59
+
60
+ class Heading
61
+ attr_reader :text, :level
62
+
63
+ def initialize(text, level)
64
+ @text = text
65
+ @level = level
66
+ end
67
+
68
+ def self.from_nsf(text)
69
+ text =~ /^(#+) (.*?)$/
70
+ self.new($2, $1.length)
71
+ end
72
+ end
73
+ end
74
+
75
+ require 'nsf/formats/nsf'
76
+ require 'nsf/formats/text'
77
+ require 'nsf/formats/html' #HTML depends on text support
78
+ require 'nsf/formats/pdf'
79
+ require 'nsf/formats/rtf'
@@ -0,0 +1,53 @@
1
+ Apache License
2
+
3
+ Version 2.0, January 2004
4
+
5
+ http://www.apache.org/licenses/
6
+
7
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
+
9
+ 1. Definitions.
10
+
11
+ &quot;License&quot; shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ &quot;Licensor&quot; shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
14
+
15
+ &quot;Legal Entity&quot; shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, &quot;control&quot; means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
16
+
17
+ &quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity exercising permissions granted by this License.
18
+
19
+ &quot;Source&quot; form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
20
+
21
+ &quot;Object&quot; form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
22
+
23
+ &quot;Work&quot; shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
24
+
25
+ &quot;Derivative Works&quot; shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
26
+
27
+ &quot;Contribution&quot; shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, &quot;submitted&quot; means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as &quot;Not a Contribution.&quot;
28
+
29
+ &quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
30
+
31
+ 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
32
+
33
+ 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
34
+
35
+ 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
36
+
37
+ You must give any other recipients of the Work or Derivative Works a copy of this License; and
38
+
39
+ You must cause any modified files to carry prominent notices stating that You changed the files; and
40
+
41
+ You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
42
+
43
+ If the Work includes a &quot;NOTICE&quot; text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
44
+
45
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
46
+
47
+ 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
48
+
49
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
50
+
51
+ 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
52
+
53
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
@@ -0,0 +1,161 @@
1
+ #HTML depends on text support
2
+
3
+ require 'cgi'
4
+ require 'nokogiri'
5
+
6
+ module Nsf
7
+ class Document
8
+ #These tags should be recursively replaced by their contents and the resulting content appended to the current paragraph
9
+ CONFORMING_TEXT_TAGS = %w(a abbr b bdi bdo cite code command datalist del dfn em i img ins kbd label mark math meter noscript output q ruby s samp small span strong sub sup textarea time var wbr)
10
+ NONCONFORMING_TEXT_TAGS = %w(acronym big center dir font listing plaintext spacer strike tt u xmp)
11
+ TEXT_TAGS = CONFORMING_TEXT_TAGS + NONCONFORMING_TEXT_TAGS
12
+
13
+ HEADING_TAGS = %w(h1 h2 h3 h4 h5 h6)
14
+
15
+ BLOCK_PASSTHROUGH_TAGS = %w(div dl form ol table tbody thead tfoot tr ul)
16
+
17
+ BLOCK_INITIATING_TAGS = %w(article aside body blockquote dd dt header li nav p pre section td th ul)
18
+
19
+ BLOCK_PLAIN_TEXT_TAGS = %w(pre plaintext listing xmp)
20
+
21
+ ENHANCERS = { %w(b strong) => "*", %(i em) => "_" }
22
+
23
+ def self.from_html(text)
24
+ iterate = lambda do |node, blocks, current_text|
25
+ just_appended_br = false
26
+ node_name = node.node_name.downcase
27
+ #puts "node_name: #{node_name}, current_text: #{current_text}"
28
+
29
+ return if node.attributes.key?("data-nsf-ignore") && node.attributes["data-nsf-ignore"].value == "true"
30
+
31
+ return if node_name == 'head'
32
+
33
+ if node.text?
34
+ text = node.inner_text
35
+ current_text << text
36
+ return
37
+ end
38
+
39
+ #Handle repeated brs by making a paragraph break
40
+ if node_name == 'br'
41
+ if just_appended_br
42
+ paragraph_text = current_text.gsub(/[[:space:]]+/, ' ').strip
43
+ blocks << Paragraph.new(paragraph_text) if paragraph_text.present?
44
+ current_text.replace("")
45
+ else
46
+ just_appended_br = true
47
+ end
48
+ return
49
+ end
50
+
51
+ #These tags terminate the current paragraph, if present, and start a new paragraph
52
+ if BLOCK_INITIATING_TAGS.include?(node_name)
53
+ #puts "initiated"
54
+ node.children.each { |n| iterate.call(n, blocks, current_text) }
55
+
56
+ paragraph_text = current_text.gsub(/[[:space:]]+/, ' ').strip
57
+ blocks << Paragraph.new(paragraph_text) if paragraph_text.present?
58
+ current_text.replace("")
59
+
60
+
61
+ # if BLOCK_PLAIN_TEXT_TAGS.include?(node_name)
62
+ # blocks.concat(Nsf::Document.from_text(current_text).nodes)
63
+ # current_text.replace("")
64
+ # end
65
+
66
+ return
67
+ end
68
+
69
+ if ENHANCERS.keys.flatten.include?(node_name)
70
+ ENHANCERS.each_pair do |tags, nsf_rep|
71
+ if tags.include?(node_name)
72
+ new_text = ""
73
+ node.children.each { |n| iterate.call(n, blocks, new_text) }
74
+ current_text << nsf_rep << new_text << nsf_rep
75
+ end
76
+ end
77
+ return
78
+ end
79
+
80
+ #Pretend that the children of this node were siblings of this node (move them one level up the tree)
81
+ if (TEXT_TAGS + BLOCK_PASSTHROUGH_TAGS).include?(node_name)
82
+ node.children.each { |n| iterate.call(n, blocks, current_text) }
83
+ return
84
+ end
85
+
86
+ if HEADING_TAGS.include?(node_name)
87
+ node.children.each { |n| iterate.call(n, blocks, current_text) }
88
+
89
+ heading_text = current_text.gsub(/[[:space:]]+/, ' ').strip
90
+ blocks << Heading.new(heading_text, node_name[1..-1].to_i) if heading_text.present?
91
+ current_text.replace("")
92
+ return
93
+ end
94
+
95
+ node.children.each { |n| iterate.call(n, blocks, current_text) }
96
+ end
97
+
98
+ blocks = []
99
+
100
+ doc = Nokogiri::HTML(text)
101
+
102
+ iterate.call(doc.root, blocks, "")
103
+
104
+ title_tag = doc.css("title").first
105
+ if title_tag && !blocks.detect { |b| b.is_a?(Heading) && b.level == 1 }
106
+ blocks.unshift(Heading.new(title_tag.inner_text, 1))
107
+ end
108
+
109
+ Document.new(blocks)
110
+ end
111
+
112
+ def to_html
113
+ nodes.map(&:to_html).join
114
+ end
115
+ end
116
+
117
+ class Paragraph
118
+ def to_html_fragment(escape = true)
119
+ out = (escape ? CGI.escapeHTML(@text) : @text).split(BOLD_ITALIC_REGEX)
120
+
121
+ if ((out.select { |element| element == "*" }).length % 2 == 0) && ((out.select { |element| element == "_" }).length % 2 == 0)
122
+ in_bold = false
123
+ in_italic = false
124
+
125
+ out.map! do |element|
126
+ if element == "*"
127
+ in_bold = !in_bold
128
+ in_bold ? "<b>" : "</b>" #Note that in_bold has been inverted, so this is inverted as well
129
+ elsif element == "_"
130
+ in_italic = !in_italic
131
+ in_italic ? "<i>" : "</i>" #Note that in_italic has been inverted, so this is inverted as well
132
+ else
133
+ element
134
+ end
135
+ end
136
+ end
137
+
138
+ out.join
139
+ end
140
+
141
+ def to_html
142
+ "<p>#{to_html_fragment}</p>"
143
+ end
144
+ end
145
+
146
+ class Fixedblock < Paragraph
147
+ def to_html
148
+ "<pre>#{CGI.escapeHTML(text.gsub(/^ /, ''))}</pre>"
149
+ end
150
+ end
151
+
152
+ class Heading
153
+ def to_html
154
+ "<h#{level} id=\"#{ref_html}\">#{CGI.escapeHTML(text)}</h#{level}>"
155
+ end
156
+
157
+ def ref_html
158
+ "heading_#{level}_#{CGI.escapeHTML(text)}"
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,35 @@
1
+ module Nsf
2
+ class Document
3
+ def self.from_nsf(text)
4
+ self.from_blocks(text.split("\n\n"))
5
+ end
6
+
7
+ def to_nsf
8
+ nodes.map(&:to_nsf).join("\n\n")
9
+ end
10
+ end
11
+
12
+ class Paragraph
13
+ def to_nsf
14
+ @text
15
+ # word_wrap(@text, 80)
16
+ end
17
+
18
+ private
19
+ #Sourced from ActionView::Helpers::TextHelper#word_wrap
20
+ def word_wrap(text, line_width)
21
+ o = text.split("\n").collect do |line|
22
+ line.length > line_width ? line.gsub(/(.{1,#{line_width}})([[:space:]]+|$)/, "\\1\n").strip : line
23
+ end * "\n"
24
+ end
25
+ end
26
+
27
+ class Fixedblock < Paragraph
28
+ end
29
+
30
+ class Heading
31
+ def to_nsf
32
+ "#{"#" * level} #{text}"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,48 @@
1
+ # PDF depends on HTML and text support
2
+
3
+ require 'prawn'
4
+
5
+ module Nsf
6
+ class Document
7
+ PDF_DEFAULT_FONT_SIZE = 10.5
8
+ PDF_LEADING = 0.4
9
+ def to_pdf(base_font_size = PDF_DEFAULT_FONT_SIZE)
10
+ pdf = Prawn::Document.new(:page_size => "A4", :margin => (base_font_size * 2.22222).round)
11
+
12
+ fd = "#{File.dirname(__FILE__)}/fonts"
13
+ pdf.font_families.update("Open Sans" => {
14
+ :normal => "#{fd}/OpenSans-Regular.ttf",
15
+ :bold => "#{fd}/OpenSans-Bold.ttf",
16
+ :italic => "#{fd}/OpenSans-Italic.ttf",
17
+ :bold_italic => "#{fd}/OpenSans-BoldItalic.ttf"
18
+ })
19
+ pdf.font "Open Sans"
20
+ pdf.font_size = base_font_size
21
+ pdf.default_leading = (PDF_LEADING * base_font_size).round
22
+
23
+ nodes.each { |n| n.to_pdf(pdf) }
24
+
25
+ pdf.render
26
+ end
27
+ end
28
+
29
+ class Paragraph
30
+ PDF_PARAGRAPH_LEADING = 1.2
31
+ def to_pdf(pdf)
32
+ pdf.text to_html_fragment(false), :inline_format => true
33
+ pdf.move_down (pdf.font_size * PDF_PARAGRAPH_LEADING).round
34
+ end
35
+ end
36
+
37
+ class Heading
38
+ PDF_FONT_RATIOS = [2.22222, 1.66666, 1.33333]
39
+ PDF_HEADING_LEADING = 0.6
40
+ def to_pdf(pdf)
41
+ size = (PDF_FONT_RATIOS[level - 1] * pdf.font_size).round
42
+ pdf.text text, :size => size, :leading => (size * Document::PDF_LEADING).round, :style => :bold
43
+ pdf.move_down (size * PDF_HEADING_LEADING).round
44
+ end
45
+ end
46
+ end
47
+
48
+
@@ -0,0 +1,69 @@
1
+ require 'rtf'
2
+ require 'ruby-rtf'
3
+
4
+ module Nsf
5
+ class Document
6
+ def self.from_rtf(text)
7
+ nodes = []
8
+ current_text = ""
9
+
10
+ (RubyRTF::Parser.new.parse(text).sections + [{ :text => '', :paragraph => true, :modifiers => {} }]).each do |sec|
11
+ puts sec.inspect
12
+ new_text = sec[:text]
13
+
14
+ unless new_text.gsub(/[[:space:]]+/, ' ').blank?
15
+ new_text = "*#{new_text}*" if sec[:modifiers][:bold]
16
+ new_text = "_#{new_text}_" if sec[:modifiers][:italic]
17
+ end
18
+
19
+ current_text << new_text
20
+
21
+ if sec[:modifiers][:paragraph]
22
+ paragraph_text = current_text.gsub(/[[:space:]]+/, ' ').strip
23
+ nodes << Paragraph.new(paragraph_text) if paragraph_text.present?
24
+ current_text = ""
25
+ end
26
+ end
27
+
28
+ Document.new(nodes)
29
+ end
30
+
31
+ def to_rtf
32
+ doc = RTF::Document.new(RTF::Font.new(RTF::Font::ROMAN, 'Times New Roman'))
33
+
34
+ nodes.each do |node|
35
+ doc.paragraph << node.to_rtf
36
+ end
37
+
38
+ doc.to_rtf
39
+ end
40
+ end
41
+
42
+ class Paragraph
43
+ #RTF_BOLD = RTF::CharacterStyle.new
44
+ #RTF_BOLD.bold = true
45
+
46
+ #RTF_ITALIC = RTF::CharacterStyle.new
47
+ #RTF_ITALIC.italic = true
48
+
49
+ def to_rtf
50
+ #cn = RTF::CommandNode.new('', '')
51
+ #cn.paragraph do |p|
52
+ # elements = @text.split(/[\*_]/)
53
+ #end
54
+ @text.gsub("\n", " ")
55
+ end
56
+ end
57
+
58
+ class Fixedblock < Paragraph
59
+ def to_rtf
60
+ @text.gsub("\n", " ")
61
+ end
62
+ end
63
+
64
+ class Heading
65
+ def to_rtf
66
+ @text.gsub("\n", " ")
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,43 @@
1
+ module Nsf
2
+ class Document
3
+ def self.from_text(text)
4
+ blocks = []
5
+
6
+ in_paragraph = false
7
+ first_line = true
8
+ current_text = ""
9
+ prev_line = ""
10
+ lines = text.split("\n")
11
+ lines.each do |line|
12
+ if line.blank? || line == lines.last || (current_text.present? && !first_line && (lsp(line) < lsp(prev_line)))
13
+ if in_paragraph || line == lines.last
14
+ in_paragraph = false
15
+
16
+ current_text << " " << line unless line.blank?
17
+
18
+ if current_text != ""
19
+ paragraph_text = current_text.gsub(/[[:space:]]+/, ' ').strip
20
+ blocks << paragraph_text if paragraph_text.present?
21
+ current_text = ""
22
+ end
23
+ end
24
+ elsif line =~ /^#+ /
25
+ blocks << line
26
+ else
27
+ first_line = !in_paragraph
28
+ in_paragraph = true
29
+ current_text << " " << line
30
+ prev_line = line
31
+ end
32
+ end
33
+
34
+ self.from_blocks(blocks)
35
+ end
36
+
37
+ # LSP == Leading SPaces
38
+ def self.lsp(str)
39
+ str =~ /^([[:space:]]+)/
40
+ $1 ? $1.length : 0
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,15 @@
1
+ unless Object.new.respond_to?(:blank?)
2
+ class Object
3
+ def blank?
4
+ respond_to?(:empty?) ? empty? : !self
5
+ end
6
+ end
7
+ end
8
+
9
+ unless Object.new.respond_to?(:present?)
10
+ class Object
11
+ def present?
12
+ !blank?
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module Nsf
2
+ VERSION = "0.0.10"
3
+ end
data/nsf.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/nsf/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "nsf"
6
+ s.version = Nsf::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Brenton Fletcher"]
9
+ s.email = ["i@bloople.net"]
10
+ s.homepage = "http://rubygems.org/gems/nsf"
11
+ s.summary = "The NSF gem is a reference implementation of the Normalized Story Format."
12
+ s.description = "The gem facilitates conversion of text and HTML documents into NSF, as well as converting NSF to HTML. The SPEC file contains the NSF specification."
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "nsf"
16
+
17
+ s.add_development_dependency "bundler", ">= 1.0.0"
18
+ s.add_dependency "nokogiri", ">= 1.4.4"
19
+ s.add_dependency "prawn", ">= 0.0.0"
20
+ s.add_dependency "clbustos-rtf", ">= 0.1.0"
21
+ s.add_dependency "ruby-rtf", ">= 0.0.0"
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
25
+ s.require_path = 'lib'
26
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nsf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.10
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brenton Fletcher
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-10-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: nokogiri
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.4.4
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.4.4
46
+ - !ruby/object:Gem::Dependency
47
+ name: prawn
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 0.0.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.0.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: clbustos-rtf
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 0.1.0
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 0.1.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: ruby-rtf
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: 0.0.0
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 0.0.0
94
+ description: The gem facilitates conversion of text and HTML documents into NSF, as
95
+ well as converting NSF to HTML. The SPEC file contains the NSF specification.
96
+ email:
97
+ - i@bloople.net
98
+ executables:
99
+ - nsf2html
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - .gitignore
104
+ - Gemfile
105
+ - Gemfile.lock
106
+ - Rakefile
107
+ - bin/nsf2html
108
+ - lib/nsf.rb
109
+ - lib/nsf/formats/fonts/Apache License Version 2.txt
110
+ - lib/nsf/formats/fonts/OpenSans-Bold.ttf
111
+ - lib/nsf/formats/fonts/OpenSans-BoldItalic.ttf
112
+ - lib/nsf/formats/fonts/OpenSans-Italic.ttf
113
+ - lib/nsf/formats/fonts/OpenSans-Regular.ttf
114
+ - lib/nsf/formats/html.rb
115
+ - lib/nsf/formats/nsf.rb
116
+ - lib/nsf/formats/pdf.rb
117
+ - lib/nsf/formats/rtf.rb
118
+ - lib/nsf/formats/text.rb
119
+ - lib/nsf/standard_extensions.rb
120
+ - lib/nsf/version.rb
121
+ - nsf.gemspec
122
+ homepage: http://rubygems.org/gems/nsf
123
+ licenses: []
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ none: false
136
+ requirements:
137
+ - - ! '>='
138
+ - !ruby/object:Gem::Version
139
+ version: 1.3.6
140
+ requirements: []
141
+ rubyforge_project: nsf
142
+ rubygems_version: 1.8.25
143
+ signing_key:
144
+ specification_version: 3
145
+ summary: The NSF gem is a reference implementation of the Normalized Story Format.
146
+ test_files: []