markdown_prawn 0.0.1.pre

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/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.pattern = './test/*.rb'
7
+ t.verbose = true
8
+ end
9
+ Rake::Task['test'].comment = "Test Markdown Prawn"
10
+
11
+ task :default => [:test]
data/bin/md2pdf ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Markdown to PDF by Ryan Stenhouse <ryan@ryanstenhouse.eu>
4
+ # November 1st 2010
5
+ #
6
+ # Takes input from standard in, expected to be a markdown document
7
+ # and renders a PDF to standard out.
8
+ #
9
+ root = File.expand_path(File.dirname(__FILE__)+ '/../')
10
+ load root + '/markdown_prawn.rb'
11
+ content = $stdin.read
12
+ puts MarkdownPrawn::StringParser.new(content).to_pdf.render
@@ -0,0 +1,20 @@
1
+ class HeadingFragment < MarkdownFragment
2
+ attr_accessor :level
3
+
4
+ def render_on(pdf_object, options = {})
5
+ arguments = _default_render_options.merge(options)
6
+ pdf_object.move_down(@level * 2)
7
+ pdf_object.text @content.join(' '), arguments
8
+ pdf_object.move_down(@level * 2)
9
+ end
10
+
11
+ private
12
+
13
+ def _default_render_options
14
+ options = { :size => (40 - (8*@level)), :align => :left, :leading => 2, :weight => :bold }
15
+ if Prawn::VERSION =~ /^0.1/ || Prawn::VERSION =~ /^1/
16
+ options.merge({:inline_format => true})
17
+ end
18
+ options
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ class HorizontalRuleFragment < MarkdownFragment
2
+
3
+ def render_on(pdf_object, options = {})
4
+ pdf_object.move_down(3)
5
+ old_width = pdf_object.line_width
6
+ pdf_object.line_width = 3
7
+ pdf_object.horizontal_rule
8
+ pdf_object.stroke
9
+ pdf_object.line_width = old_width
10
+ pdf_object.move_down(3)
11
+ end
12
+
13
+ private
14
+
15
+ def _default_render_options
16
+ options = { :size => 12, :align => :left, :leading => 2 }
17
+ if Prawn::VERSION =~ /^0.1/ || Prawn::VERSION =~ /^1/
18
+ options = options.merge({:inline_format => true})
19
+ else
20
+ options = options.merge({:inline_format => false})
21
+ end
22
+ options
23
+ end
24
+
25
+ end
@@ -0,0 +1,25 @@
1
+ require 'net/http'
2
+ require 'tmpdir'
3
+
4
+ class ImageFragment < MarkdownFragment
5
+ def render_on(pdf_object)
6
+ if is_remote_uri?
7
+ filename = @content.first.split('/').last
8
+ file_path = "#{Dir.tmpdir}/#{filename}"
9
+ content = Net::HTTP.get(URI.parse(@content.first))
10
+ File.open(file_path, 'w') do |f|
11
+ f.puts content
12
+ end
13
+ else
14
+ file_path = File.expand_path(@content.first)
15
+ end
16
+ pdf_object.image file_path
17
+ end
18
+
19
+ private
20
+
21
+ def is_remote_uri?
22
+ !/^(http:|https:)/.match(@content.first).nil?
23
+ end
24
+
25
+ end
@@ -0,0 +1,17 @@
1
+ class LinksReferenceFragment < MarkdownFragment
2
+
3
+ def render_on(pdf_object)
4
+
5
+ pdf_object.move_down(10)
6
+ pdf_object.horizontal_rule
7
+ pdf_object.stroke
8
+ pdf_object.move_down(4)
9
+ pdf_object.text "Hyperlink References:"
10
+ pdf_object.move_down(4)
11
+ pdf_object.table @content do
12
+ cells.borders = []
13
+ end
14
+ pdf_object.move_down(4)
15
+ end
16
+
17
+ end
@@ -0,0 +1,40 @@
1
+ class ListFragment < MarkdownFragment
2
+ attr_accessor :ordered
3
+
4
+ def render_on(pdf_object, options = {})
5
+ bullet = '• '
6
+ arguments = _default_render_options.merge(options)
7
+ width = ((pdf_object.bounds.width / 100) * 90)
8
+ data = []
9
+
10
+ @content.each_with_index do |item, i|
11
+ # Strip any un-needed white space
12
+ #
13
+ item = item.gsub(/\s\s+/,' ')
14
+ if ordered?
15
+ bullet = "#{i+1}."
16
+ end
17
+ data << [bullet,item]
18
+ end
19
+
20
+ pdf_object.table data, arguments.merge({:width => width}) do
21
+ cells.borders = []
22
+ end
23
+ pdf_object.move_down(5)
24
+ end
25
+
26
+ def ordered?
27
+ @ordered == true
28
+ end
29
+
30
+ private
31
+
32
+ def _default_render_options
33
+ options = {}
34
+ if Prawn::VERSION =~ /^0.1/ || Prawn::VERSION =~ /^1/
35
+ options = options.merge({:cell_style => { :inline_format => true}})
36
+ end
37
+ options
38
+ end
39
+
40
+ end
@@ -0,0 +1,13 @@
1
+ class MarkdownFragment
2
+ attr_accessor :content
3
+
4
+ def initialize(content = [])
5
+ @content = content
6
+ end
7
+
8
+ # Renders the current fragment on the supplied prawn PDF Object. By Default,
9
+ # it will just join content and add it as text - not too useful.
10
+ def render_on(pdf_object)
11
+ pdf_object.text @content.join(' ')
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ class ParagraphFragment < MarkdownFragment
2
+
3
+ def render_on(pdf_object, options = {})
4
+ arguments = _default_render_options.merge(options)
5
+ pdf_object.move_down(3)
6
+ pdf_object.text @content.join(' '), arguments
7
+ end
8
+
9
+ private
10
+
11
+ def _default_render_options
12
+ options = { :size => 12, :align => :left, :leading => 2 }
13
+ if Prawn::VERSION =~ /^0.1/ || Prawn::VERSION =~ /^1/
14
+ options = options.merge({:inline_format => true})
15
+ else
16
+ options = options.merge({:inline_format => false})
17
+ end
18
+ options
19
+ end
20
+
21
+ end
@@ -0,0 +1,7 @@
1
+ require File.dirname(__FILE__) + '/markdown_fragments/markdown_fragment.rb'
2
+ require File.dirname(__FILE__) + '/markdown_fragments/paragraph_fragment.rb'
3
+ require File.dirname(__FILE__) + '/markdown_fragments/heading_fragment.rb'
4
+ require File.dirname(__FILE__) + '/markdown_fragments/list_fragment.rb'
5
+ require File.dirname(__FILE__) + '/markdown_fragments/image_fragment.rb'
6
+ require File.dirname(__FILE__) + '/markdown_fragments/horizontal_rule_fragment.rb'
7
+ require File.dirname(__FILE__) + '/markdown_fragments/links_reference_fragment.rb'
@@ -0,0 +1,18 @@
1
+ module MarkdownPrawn
2
+ class FileParser < Parser
3
+
4
+ # Give this the path to a file and (if it exists), it'll generate
5
+ # a PDF version of the markdown there.
6
+ #
7
+ def initialize(file_path)
8
+ file_path = File.expand_path(file_path)
9
+ if !File.exist?(file_path)
10
+ raise Errno::ENOENT.new("#{file_path} could not be found") and return
11
+ else
12
+ @content = detab(IO.read(file_path).gsub(/\r\n?/, "\n")).split("\n")
13
+ end
14
+ super
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,213 @@
1
+ require File.dirname(__FILE__) + '/../markdown_fragments.rb'
2
+
3
+ module MarkdownPrawn
4
+
5
+ # Horribly bodgy and wrong markdown parser which should just about do
6
+ # for a proof of concept. Some of the code comes from mislav's original
7
+ # BlueCloth since I cant find the source of a newer versoin.
8
+ #
9
+ class Parser
10
+ attr_accessor :links_list, :images_list, :document_structure
11
+
12
+ def initialize(document_structure = [])
13
+ @links_list = { :urls_seen => [], :object => LinksReferenceFragment.new }
14
+ @document_structure = []
15
+ @images_list = []
16
+ end
17
+
18
+ # Returns a Prawn::Document, accepts the same options as Prawn does when creating
19
+ # a new document, but defaults to creating pages in Portrait and at A4 size.
20
+ #
21
+ # Uses parse! rather than parse to ensure that the slate is always clean when
22
+ # generating the PDF.
23
+ #
24
+ def to_pdf(options = {})
25
+ parse!
26
+ options = options.merge({ :page_layout => :portrait, :page_size => 'A4' })
27
+ pdf = Prawn::Document.new(options)
28
+ @document_structure.each { |markdown_fragment| markdown_fragment.render_on(pdf) }
29
+ pdf
30
+ end
31
+
32
+ # Clears out the current sate of +@document_structure+ and then parses +@content+ content
33
+ #
34
+ def parse!
35
+ @document_structure = []
36
+ parse
37
+ end
38
+
39
+ def parse
40
+ paragraph = ParagraphFragment.new
41
+ list = ListFragment.new
42
+ in_list = false
43
+ @content.each_with_index do |line, index|
44
+ line = process_inline_formatting(line)
45
+
46
+ # Assume everything is part of a paragraph by default and
47
+ # add its content to the current in-scope paragraph object.
48
+ #
49
+ paragraph.content << line
50
+ if line == ""
51
+ unless paragraph.content.empty?
52
+ @document_structure << paragraph
53
+ paragraph = ParagraphFragment.new
54
+ end
55
+ end
56
+
57
+ # Deal with inline headings
58
+ #
59
+ unless /^(#+)(\s?)\S/.match(line).nil?
60
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
61
+ hashes = $1.dup
62
+ heading = HeadingFragment.new([line.gsub(hashes,'')])
63
+ heading.level = hashes.length
64
+ @document_structure << heading
65
+ end
66
+
67
+ # Deal with Level 1 Headings
68
+ #
69
+ if !/^(=)+$/.match(line).nil?
70
+ paragraph.content = paragraph.content.delete_if do |item|
71
+ item == line || item == @content[index - 1]
72
+ end
73
+ heading = HeadingFragment.new([@content[index - 1]])
74
+ heading.level = 1
75
+ @document_structure << heading
76
+ end
77
+
78
+ # Deal with Level 2 Headings or horizontal rules.
79
+ #
80
+ if !/^(-)+$/.match(line).nil?
81
+ if @content[index - 1].strip == ''
82
+ # Assume it's a horizontal rule
83
+ #
84
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
85
+ @document_structure << HorizontalRuleFragment.new
86
+ else
87
+ paragraph.content = paragraph.content.delete_if do |item|
88
+ item == line || item == @content[index - 1]
89
+ end
90
+ heading = HeadingFragment.new([@content[index - 1]])
91
+ heading.level = 2
92
+ @document_structure << heading
93
+ end
94
+ end
95
+
96
+ # Deal with all other kinds of horizontal rules
97
+ #
98
+ if !/^(\*+)(\s)?(\*+)(\s)?(\*+)/.match(line).nil? || !/^(-+)(\s)(-+)(\s)(-+)/.match(line).nil?
99
+ if @content[index - 1].strip == ''
100
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
101
+ @document_structure << HorizontalRuleFragment.new
102
+ end
103
+ end
104
+
105
+ # Try to deal with lists.
106
+ #
107
+ if in_list
108
+ # We're in a list just now.
109
+ #
110
+
111
+ # Remove the content from the paragraph where it will have
112
+ # automatically been appended
113
+ #
114
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
115
+
116
+ # Check to see if we've got a new list item.
117
+ #
118
+ if (!/^\s+\*\s/.match(line).nil? || !/^\s+\d+\.\s/.match(line).nil?)
119
+
120
+ # Find out if this new list item is for a different type of list
121
+ # and deal with that before adding the new list item.
122
+ #
123
+ if list.ordered? && !/^\s+\*\s/.match(line).nil?
124
+ @document_structure << list
125
+ list = ListFragment.new
126
+ elsif !list.ordered? && !/^\s+\d+\.\s/.match(line).nil?
127
+ @document_structure << list
128
+ list = ListFragment.new
129
+ list.ordered = true
130
+ end
131
+
132
+ # Remove the list style and add the new list item.
133
+ #
134
+ list.content << line.sub(/^\s+\*\s/,'').sub(/^\s+\d+\.\s/,'')
135
+
136
+ else
137
+ # If this line isn't a new list item, then it's a continuation for the current
138
+ # list item.
139
+ #
140
+ list.content[-1] += line
141
+ end
142
+
143
+ # If the current line is empty, then we're done with the list.
144
+ #
145
+ if line == ''
146
+ @document_structure << list
147
+ list = ListFragment.new
148
+ in_list = false
149
+ end
150
+ else
151
+ # Not currently in a list, but we've detected a list item
152
+ #
153
+ if (!/^\s+\*\s/.match(line).nil? || !/^\s+\d+\.\s/.match(line).nil?)
154
+ ordered = false
155
+ ordered = true if !/^\s+\d+\.\s/.match(line).nil?
156
+ list = ListFragment.new
157
+ list.ordered = ordered
158
+ list.content << line.sub(/^\s+\*\s/,'').sub(/^\s+\d+\.\s/,'')
159
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
160
+ in_list = true
161
+ end
162
+ end
163
+
164
+
165
+ # Deal with a link reference by adding it ot the list of references
166
+ #
167
+ if !/^(\[\S+\]){1}:\s(\S+)\s?(.+)?/.match(line).nil?
168
+ reference, url, title = $1, $2, $3
169
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
170
+ @links_list[:urls_seen] << url
171
+ @links_list[:object].content << [ reference, url, "#{title}" ]
172
+ end
173
+
174
+ # Deal with inline images
175
+ #
176
+ line.scan(/(?:^|\s)?(\!\[(?:.+?)\]\((.+?)\))/) do |val|
177
+ line.gsub(val[0],'')
178
+ @document_structure << ImageFragment.new([val[1]])
179
+ end
180
+ end
181
+ if !list.content.empty? && ! @document_structure.include?(list)
182
+ @document_structure << list
183
+ list = ListFragment.new
184
+ end
185
+ @document_structure << paragraph unless paragraph.content == ''
186
+ @document_structure << @links_list[:object] if !@links_list[:urls_seen].empty?
187
+ end
188
+
189
+ private
190
+
191
+ def detab(string, tabwidth = 2)
192
+ string.split("\n").collect { |line|
193
+ line.gsub(/(.*?)\t/) do
194
+ $1 + ' ' * (tabwidth - $1.length % tabwidth)
195
+ end
196
+ }.join("\n")
197
+ end
198
+
199
+ # Only do Inline formatting for versions of Prawn which support it.
200
+ #
201
+ def process_inline_formatting(str)
202
+ breg = [ %r{ \b(\_\_) (\S|\S.*?\S) \1\b }x, %r{ (\*\*) (\S|\S.*?\S) \1 }x ]
203
+ ireg = [ %r{ (\*) (\S|\S.*?\S) \1 }x, %r{ \b(_) (\S|\S.*?\S) \1\b }x ]
204
+ if Prawn::VERSION =~ /^0.1/ || Prawn::VERSION =~ /^1/
205
+ str.gsub(breg[0], %{<b>\\2</b>} ).gsub(breg[1], %{<b>\\2</b>} ).gsub(ireg[0], %{<i>\\2</i>} ).gsub(ireg[1], %{<i>\\2</i>} )
206
+ else
207
+ str.gsub(breg[0], %{\\2} ).gsub(breg[1], %{\\2} ).gsub(ireg[0], %{\\2} ).gsub(ireg[1], %{\\2} )
208
+ end
209
+ end
210
+
211
+ end
212
+
213
+ end
@@ -0,0 +1,11 @@
1
+ module MarkdownPrawn
2
+ class StringParser < Parser
3
+ # Take a string or an object which responds to to_s and attemps to
4
+ # parse markdown in it.
5
+ def initialize(string_to_convert)
6
+ string_to_convert = string_to_convert.to_s
7
+ @content = detab(string_to_convert.gsub(/\r\n?/, "\n")).split("\n")
8
+ super
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/markdown_fragments.rb'
2
+ require File.dirname(__FILE__) + '/markdown_parser/parser.rb'
3
+ require File.dirname(__FILE__) + '/markdown_parser/file_parser.rb'
4
+ require File.dirname(__FILE__) + '/markdown_parser/string_parser.rb'
5
+
@@ -0,0 +1,6 @@
1
+ module MarkdownPrawn
2
+ module Exceptions
3
+ class InvalidMarkdownFile < Exception; end
4
+ class InvalidDestinationPath < Exception; end
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ description = "Markdown Parawn is a library and an executable strip which allow you to generate a PDF from any valid Markdown."
2
+ Gem::Specification.new do |spec|
3
+ spec.name = "markdown_prawn"
4
+ spec.version = '0.0.1.pre'
5
+ spec.platform = Gem::Platform::RUBY
6
+ spec.files = Dir.glob("{bin,lib,test}/**/**/*") +
7
+ ["Rakefile", "markdown_prawn.gemspec"]
8
+ spec.require_path = "lib"
9
+ spec.required_ruby_version = '>= 1.8.7'
10
+ spec.required_rubygems_version = ">= 1.3.6"
11
+
12
+ spec.test_files = Dir[ "test/*_test.rb" ]
13
+ spec.has_rdoc = false
14
+ spec.author = "Ryan Stenhouse"
15
+ spec.email = "ryan@ryanstenhouse.eu"
16
+ spec.rubyforge_project = "markdown_prawn"
17
+ spec.add_dependency('prawn', '~>0.10')
18
+ spec.homepage = "http://ryanstenhouse.eu"
19
+ spec.summary = description
20
+ spec.description = description
21
+ end
@@ -0,0 +1,3 @@
1
+ 1. One Item
2
+ 2. Two Item
3
+ 3. Three Item
@@ -0,0 +1,8 @@
1
+ Paragaphs are a fundamental part of Markdown and
2
+ this makes sure that I can process them properly
3
+ even with
4
+ strange wrapping and
5
+ other
6
+ annoyances
7
+
8
+ This should be seen as the second paragraph.
@@ -0,0 +1,3 @@
1
+ * Bulleted
2
+ * List
3
+ * Support
data/test/helper.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'prawn'
3
+ require 'test/unit'
4
+ require File.dirname(__FILE__) + '/../markdown_prawn.rb'
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + '/helper.rb'
2
+
3
+ class TestListsHandling < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @unordered = MarkdownPrawn::FileParser.new(File.expand_path(File.dirname(__FILE__) + '/fixtures/unordered_lists.mdown'))
7
+ @ordered = MarkdownPrawn::FileParser.new(File.expand_path(File.dirname(__FILE__) + '/fixtures/ordered_lists.mdown'))
8
+ end
9
+
10
+ def test_document_structure_is_correct_for_unordered_lists
11
+ @unordered.parse
12
+ assert !@unordered.document_structure.empty?
13
+ assert_equal ListFragment, @unordered.document_structure[0].class
14
+ assert_equal false, @unordered.document_structure[0].ordered?
15
+ end
16
+
17
+ def test_document_structure_is_correct_for_ordered_lists
18
+ @ordered.parse
19
+ assert !@ordered.document_structure.empty?
20
+ assert_equal ListFragment, @ordered.document_structure[0].class
21
+ assert_equal true, @ordered.document_structure[0].ordered?
22
+ end
23
+
24
+ end
@@ -0,0 +1,18 @@
1
+ require File.dirname(__FILE__) + '/helper.rb'
2
+
3
+ class TestParagraphHandling < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @paragraphs = MarkdownPrawn::FileParser.new(File.expand_path(File.dirname(__FILE__) + '/fixtures/paragraphs.mdown'))
7
+ end
8
+
9
+ def test_document_structure_is_correct_for_paragraphs
10
+ @paragraphs.parse
11
+ assert !@paragraphs.document_structure.empty?
12
+ assert_equal 2, @paragraphs.document_structure.nitems
13
+ @paragraphs.document_structure.each do |fragment|
14
+ assert_equal ParagraphFragment, fragment.class
15
+ end
16
+ end
17
+
18
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: markdown_prawn
3
+ version: !ruby/object:Gem::Version
4
+ hash: 961915968
5
+ prerelease: true
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ - pre
11
+ version: 0.0.1.pre
12
+ platform: ruby
13
+ authors:
14
+ - Ryan Stenhouse
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-11-01 00:00:00 +00:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: prawn
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ hash: 31
31
+ segments:
32
+ - 0
33
+ - 10
34
+ version: "0.10"
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: Markdown Parawn is a library and an executable strip which allow you to generate a PDF from any valid Markdown.
38
+ email: ryan@ryanstenhouse.eu
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files: []
44
+
45
+ files:
46
+ - bin/md2pdf
47
+ - lib/markdown_fragments.rb
48
+ - lib/markdown_parser/parser.rb
49
+ - lib/markdown_parser/file_parser.rb
50
+ - lib/markdown_parser/string_parser.rb
51
+ - lib/markdown_parser.rb
52
+ - lib/markdown_fragments/paragraph_fragment.rb
53
+ - lib/markdown_fragments/markdown_fragment.rb
54
+ - lib/markdown_fragments/image_fragment.rb
55
+ - lib/markdown_fragments/heading_fragment.rb
56
+ - lib/markdown_fragments/links_reference_fragment.rb
57
+ - lib/markdown_fragments/list_fragment.rb
58
+ - lib/markdown_fragments/horizontal_rule_fragment.rb
59
+ - lib/markdown_prawn_exceptions.rb
60
+ - test/test_lists_handling.rb
61
+ - test/test_paragraph_handling.rb
62
+ - test/fixtures/ordered_lists.mdown
63
+ - test/fixtures/paragraphs.mdown
64
+ - test/fixtures/unordered_lists.mdown
65
+ - test/helper.rb
66
+ - Rakefile
67
+ - markdown_prawn.gemspec
68
+ has_rdoc: true
69
+ homepage: http://ryanstenhouse.eu
70
+ licenses: []
71
+
72
+ post_install_message:
73
+ rdoc_options: []
74
+
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ hash: 57
83
+ segments:
84
+ - 1
85
+ - 8
86
+ - 7
87
+ version: 1.8.7
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ hash: 23
94
+ segments:
95
+ - 1
96
+ - 3
97
+ - 6
98
+ version: 1.3.6
99
+ requirements: []
100
+
101
+ rubyforge_project: markdown_prawn
102
+ rubygems_version: 1.3.7
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Markdown Parawn is a library and an executable strip which allow you to generate a PDF from any valid Markdown.
106
+ test_files: []
107
+