show_off_pdf 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING.md ADDED
@@ -0,0 +1,13 @@
1
+ Copying / License Permissions
2
+ =============================
3
+ showoff2pdf was created by Ryan Stenhouse <ryan@ryanstenhouse.eu>.
4
+
5
+ This work is made available under the terms of the Creative Commons by
6
+ Attibution, Non Commercial Share-Alike (UK:Scotland) license.
7
+
8
+ (Yes, that is quite a long winded title).
9
+
10
+ You can [read the copy of the licence][1] on the [creative commons][1]
11
+ website.
12
+
13
+ [1]: http://creativecommons.org/licenses/by-nc-sa/2.5/scotland/
data/HACKING.md ADDED
@@ -0,0 +1,15 @@
1
+ Hacking on showoff2pdf
2
+ =========================
3
+
4
+ Hacking on showoff2pdf is strongly encouraged. There are
5
+ numerous areas (particularly the parser) which could do with
6
+ a lot of improvement.
7
+
8
+ The process for accepting patches is as follows:
9
+
10
+ 1. Fork the repository
11
+ 2. Make your changes
12
+ 3. Have some tests for your changes
13
+ 4. Submit a pull request
14
+
15
+
data/LICENSE ADDED
@@ -0,0 +1,115 @@
1
+ Creative Commons
2
+
3
+
4
+ Attribution - Non-commercial - Share-Alike 2.5 Scotland
5
+
6
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENCE DOES NOT CREATE AN AGENT-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
7
+ Licence
8
+
9
+ This Creative Commons Scotland public licence enables You to Use the Work worldwide for non-commercial purposes provided that You keep to the terms of this Licence.
10
+
11
+ 'The Licensor' (the Person offering the Work under the terms and conditions of this Licence)
12
+
13
+ and
14
+
15
+ 'You' (the Person acquiring rights under this Licence)
16
+
17
+ agree as follows:
18
+
19
+ 1. Definitions of Capitalised Words
20
+
21
+ "Collective Work" means a work made up of the Work in its entirety in unmodified form, along with other work, assembled into a collective whole.
22
+ "Derivative Work" means a work that Uses the Work (or any substantial part of the Work) in any material form whatsoever (such as a translation, dramatisation, or abridgment), other than (i) as a whole and in unmodified form or (ii) by modifying it as may be technically necessary to Use it in a different mode or format; but a Collective Work is not a Derivative Work for the purpose of this Licence.
23
+ "Derogatory Treatment" means derogatory treatment as defined by section 80 of the Copyright Design and Patents Act 1988, except as allowed by that Act: and it has that meaning wherever that treatment may take place and whoever may carry it out.
24
+ "Licence Elements" means the following licence attributes indicated in the title of this Licence: Attribution, Non-commercial, Share-Alike.
25
+ "Original Author" means the Person who created the Work.
26
+ "Person" means a natural person or a body of persons corporate or incorporate.
27
+ "Use", as a verb, means doing any act which is restricted by copyright, whether in the original medium or any other; and includes modifying the Work as may be technically necessary to Use it in a different mode or format.
28
+ "Work" means the work protected by copyright which is offered under the terms of this Licence, and includes the Work as incorporated in any Collective Work.
29
+ Words in the singular include the plural and vice versa.
30
+ 2. The Rights Granted
31
+
32
+ 2.1 The Licensor grants to You a worldwide, royalty-free, non-exclusive, licence to Use the Work for the duration of its copyright.
33
+
34
+ So you may, for example:
35
+
36
+ copy the Work, or create Derivative Works or incorporate it into a Collective Work;
37
+ copy Derivative Works, or the Work as incorporated in any Collective Work; and
38
+ publish, perform or communicate the Work and/or Derivative Works and/or the Work as incorporated in any Collective Work to anyone;
39
+ in any medium whether now known or created in the future.
40
+
41
+ 2.2 However, this Licence does not allow you to:
42
+
43
+ use the work in any way primarily intended for commercial advantage or payment; but exchange for other copyrighted work without payment (whether by means of digital file-sharing or otherwise) is not to be taken to be so intended;
44
+ impose any terms or any technological measures on the Work, or a Derivative Work, that alter or restrict the terms of this Licence or any rights granted under it or have the effect or intent of restricting the ability of any person to exercise those rights;
45
+ sublicense the Work; or
46
+ subject the Work to Derogatory Treatment.
47
+ 2.3 You must, if you publish or distribute the Work or any Derivative Work to anyone else in any way, give reasonable credit to the Original Author as follows:
48
+
49
+ by giving the name of the Original Author if that has been supplied; or a pseudonym if that has been supplied instead; and/or the name of any other person if that has been supplied for attribution in place of the Original Author;
50
+ by giving the title of the Work if that has been supplied;
51
+ by giving the Uniform Resource Identifier of the Work if that has been supplied, but you need not do so if this does not refer to its copyright terms or to its licensing information;
52
+ in the case of a Derivative Work, by identifying how the Work has been used (for example, "French translation of the Work by X", or "Screenplay based on original work by X"); and
53
+ in the case of a Derivative Work or a Collective Work, placing that credit in the same place, and at least as prominently, as any comparable authorship credit.
54
+ But, if what you are publishing or distributing is a Derivative Work or a Collective Work, you must remove any of these credits if you are asked to do so by the Licensor and if it is practicable to do so.
55
+ These are important conditions of this licence and if you fail to do so you will be in material breach of its terms.
56
+
57
+ 2.4 The Original Author asserts the right to be identified as the original author of the Work; but only as and when required by clause 2.3 above.
58
+
59
+ 2.5 You must also, if you publish or distribute the Work or any Derivative Work to anyone else in any way:
60
+
61
+ include a copy of this Licence (or its Uniform Resource Identifier) with it; and
62
+ keep intact any copyright notices for the Work and notices that refer to this Licence.
63
+ These are important conditions of this licence and if you fail to do so you will be in material breach of its terms.
64
+
65
+ 2.6 Each time You publish the Work or any Derivative Work to anyone else in any way, the Licensor offers to the recipient a licence to the Work on the same terms and conditions as this Licence.
66
+
67
+ 2.7 Any Derivative Work you create must be under a licence which is either one with the same terms and conditions as this Licence, or a later version of this Licence with the same Licence Elements as this Licence, or another Creative Commons licence with the same Licence Elements as this Licence (for example, Attribution-Noncommercial-ShareAlike 2.5 Slovenia).
68
+
69
+ 2.8 And:
70
+
71
+ the right to collect royalties for any commercial use of the Work is reserved;
72
+ the right to collect payments via a licensing body or collecting society for any commercial use of the Work is reserved;
73
+ the right to collect payments under the Public Lending Right scheme (or any public scheme that provides payment for public borrowing or use) is reserved;
74
+ the right to release the Work under different terms, or to stop distributing the Work, is reserved; and
75
+ all other rights not expressly granted by the Licensor are reserved.
76
+ 2.9 This Licence does not affect any rights that You or anyone else may independently have under any applicable law (including fair dealing, fair use, or any other legally recognised limitation or exception to copyright infringement) to make any Use of this Work.
77
+
78
+ 3. Warranties and Disclaimer
79
+
80
+ 3.1 The Work is licensed by the Licensor "as is" and without any warranty of any kind, either express or implied, whether of title, of accuracy, of fitness for purpose, or otherwise.
81
+
82
+ 4. Limit of Liability
83
+
84
+ 4.1 Subject to any liability which may not be excluded or limited by law, the Licensor shall not be liable for, and expressly excludes, all liability for loss or damage however and whenever caused to anyone by any Use under this Licence, whether by You or by anyone else, and whether caused by any fault on the part of the Licensor or not. If liability may not be excluded by law, it is limited to actual and direct financial loss to the extent it is caused by proved negligence on the part of the Licensor.
85
+
86
+ 5. Termination
87
+
88
+ 5.1 Any breach by You of the terms of this licence (for example, by distributing the work licensed without attributing authorship as appropriate) entitles the Licensor to terminate this Licence with immediate effect and without notice to you. Persons who have received the Work, Derivative Works, or Collective Works from You under this Licence, however, will not have their licences terminated provided their use is in full compliance with this Licence or a licence granted under clause 2.6 of this Licence.
89
+
90
+ 5.2 If You are not in breach of the terms of this licence, the Licensor may not terminate your rights under it.
91
+
92
+ 5.3 Unless terminated under clause 5.1, this Licence is granted to you for the duration of copyright in the Work.
93
+
94
+ 6. General
95
+
96
+ 6.1 If any provision of this Licence is held to be invalid or unenforceable, that shall not affect the validity or enforceability of the remainder of the terms of this Licence.
97
+
98
+ 6.2 This Licence is the entire agreement between the parties with respect to the Work licensed here. It replaces any earlier understandings, agreements or representations with respect to the Work not specified here.
99
+
100
+ 6.3 If You are in breach of the terms of this Licence (for example, by distributing the work licensed without attributing authorship as appropriate) you will not be entitled to rely on the terms of this Licence or to complain of any breach by the Licensor.
101
+
102
+ 6.4 Neither You, nor anyone else, may enforce any provision of this Licence by specific implement or interdict.
103
+
104
+ 6.5 This Licence is governed by the law of Scotland and the parties accept the exclusive jurisdiction of the Courts of Scotland to decide any action or claim directed against the Licensor.
105
+
106
+ 6.6 If there is any dispute as to the meaning or effect of any provision of this Licence, it must so far as possible be read and given effect in a way that is compatible with the provisions of the Creative Commons generic licence, version 2.5, which has the same Licence Elements.
107
+
108
+ The Notice below is not part of this licence.
109
+
110
+ Creative Commons is not a party to this Licence, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this licence. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
111
+
112
+ Except for the limited purpose of indicating to the public that the Work is licensed under the Creative Commons Public Licence, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
113
+
114
+ Creative Commons may be contacted at http://creativecommons.org/.
115
+
data/README.md ADDED
File without changes
data/bin/showoff2pdf ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require File.dirname(__FILE__) + '/../show_off_pdf'
4
+ paths = []
5
+ images = []
6
+ ShowOffUtils.showoff_sections.each do |section|
7
+ paths << Dir.glob(File.expand_path("./#{section}/*.{md,mdown}"))
8
+ images << Dir.glob(File.expand_path("./#{section}/**/*.{png,jpg,jpeg}"))
9
+ end
10
+ paths.flatten!
11
+ images.flatten!
12
+ ImageFragment._image_assets = images
13
+ puts ShowoffSlideStream.new(paths).to_pdf.render
@@ -0,0 +1,36 @@
1
+ !SLIDE
2
+ .notes It even parses out slide notes when entered all on one line with the notes class. Try it out!
3
+ # Introducing showoff2pdf #
4
+
5
+ * Uses on Prawn to dynamically generate PDF
6
+ files form Markdown Slides
7
+ * Favours sensible defaults to make readable
8
+ and printable documents
9
+ * Just depends on Ruby and Prawn (--pre)
10
+ * Based on my Markdown Prawn library
11
+
12
+ !SLIDE
13
+ #Supported features#
14
+
15
+ ![images](./images.png)
16
+
17
+ ##Multi
18
+ ###Level
19
+ ####Headings
20
+
21
+ !SLIDE
22
+ #Supported features continued
23
+
24
+ $ cat slide.md | ./md2pdf > slide.pdf
25
+ $ formatting for command line segments
26
+
27
+
28
+ !SLIDE
29
+ .notes I'm happy to accept any patches for this; so please get in touch. There's still a lot that needs to be done!
30
+ # What doesn't work #
31
+
32
+ * Custom CSS styles and the like will not be
33
+ preserved, if you want that, then use Prince
34
+ * For obvious reasons, executing Ruby or JavaScript
35
+ won't work.
36
+ * A lot of inline HTML will be silently dropped.
Binary file
@@ -0,0 +1,11 @@
1
+ {
2
+
3
+ "name": "Introducing showoff2pdf",
4
+ "author": "Ryan Stenhouse",
5
+ "description": "showoff2pdf is a new utility that uses Prawn to convert your ShowOff presentations to PDF Documents." ,
6
+
7
+ "sections": [
8
+ {"section":"one"}
9
+ ]
10
+
11
+ }
data/example.pdf ADDED
Binary file
@@ -0,0 +1,32 @@
1
+ class CodeFragment < MarkdownFragment
2
+ attr_accessor :ordered
3
+
4
+ def render_on(pdf_object, options = {})
5
+ arguments = _default_render_options.merge(options)
6
+ width = ((pdf_object.bounds.width / 100) * 90)
7
+ data = []
8
+
9
+ @content.each_with_index do |item, i|
10
+ # Strip any un-needed white space
11
+ #
12
+ item = item.gsub(/\s\s+/,' ')
13
+ data << [ "<font name=\"Courier\">#{item}</font>" ]
14
+ end
15
+
16
+ pdf_object.table data, arguments.merge({:width => width}) do
17
+ cells.borders = []
18
+ end
19
+ pdf_object.move_down(5)
20
+ end
21
+
22
+ def ordered?
23
+ @ordered == true
24
+ end
25
+
26
+ private
27
+
28
+ def _default_render_options
29
+ options = {:cell_style => { :inline_format => true}}
30
+ end
31
+
32
+ end
@@ -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 "<b>#{@content.join(' ')}</b>", arguments
8
+ pdf_object.move_down(@level * 2)
9
+ end
10
+
11
+ private
12
+
13
+ def _default_render_options
14
+ options = { :size => (50 - (10*@level)), :align => :left, :leading => 2, :inline_format => true }
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,87 @@
1
+ require 'net/http'
2
+ require 'tmpdir'
3
+
4
+ class ImageFragment < MarkdownFragment
5
+ def self._image_assets=(value)
6
+ @@_image_assets = value
7
+ end
8
+
9
+ def self._image_assets
10
+ @@_image_assets
11
+ end
12
+
13
+ def render_on(pdf_object)
14
+ if is_remote_uri?
15
+ filename = @content.first.split('/').last
16
+ file_path = "#{Dir.tmpdir}/#{filename}"
17
+ content = Net::HTTP.get(URI.parse(@content.first))
18
+ File.open(file_path, 'w') do |f|
19
+ f.puts content
20
+ end
21
+ else
22
+ if !ImageFragment._image_assets.nil?
23
+ ImageFragment._image_assets.each do |image|
24
+ if image =~ /#{@content.first}$/
25
+ file_path = image
26
+ end
27
+ end
28
+ else
29
+ file_path = @content.first
30
+ end
31
+ end
32
+ if file_path =~ /.png$/
33
+ width, height = IO.read(file_path)[0x10..0x18].unpack('NN')
34
+ else
35
+ width = JPEG.new(file_path).width
36
+ height = JPEG.new(file_path).height
37
+ end
38
+ pdf_object.image file_path, :width => width, :height => height, :scale => 0.8
39
+ end
40
+
41
+ private
42
+
43
+ def is_remote_uri?
44
+ !/^(http:|https:)/.match(@content.first).nil?
45
+ end
46
+
47
+ end
48
+
49
+ class JPEG
50
+ attr_reader :width, :height, :bits
51
+
52
+ def initialize(file)
53
+ if file.kind_of? IO
54
+ examine(file)
55
+ else
56
+ File.open(file, 'rb') { |io| examine(io) }
57
+ end
58
+ end
59
+
60
+ private
61
+ def examine(io)
62
+ raise 'malformed JPEG' unless io.getc == 0xFF && io.getc == 0xD8 # SOI
63
+
64
+ class << io
65
+ def readint; (readchar << 8) + readchar; end
66
+ def readframe; read(readint - 2); end
67
+ def readsof; [readint, readchar, readint, readint, readchar]; end
68
+ def next
69
+ c = readchar while c != 0xFF
70
+ c = readchar while c == 0xFF
71
+ c
72
+ end
73
+ end
74
+
75
+ while marker = io.next
76
+ case marker
77
+ when 0xC0..0xC3, 0xC5..0xC7, 0xC9..0xCB, 0xCD..0xCF # SOF markers
78
+ length, @bits, @height, @width, components = io.readsof
79
+ raise 'malformed JPEG' unless length == 8 + components * 3
80
+ when 0xD9, 0xDA: break # EOI, SOS
81
+ when 0xFE: @comment = io.readframe # COM
82
+ when 0xE1: io.readframe # APP1, contains EXIF tag
83
+ else io.readframe # ignore frame
84
+ end
85
+ end
86
+ end
87
+ 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,41 @@
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
+ column(0).style( { :width => 20 })
23
+ end
24
+ pdf_object.move_down(5)
25
+ end
26
+
27
+ def ordered?
28
+ @ordered == true
29
+ end
30
+
31
+ private
32
+
33
+ def _default_render_options
34
+ options = {}
35
+ if Prawn::VERSION =~ /^0.1/ || Prawn::VERSION =~ /^1/
36
+ options = options.merge({:cell_style => { :inline_format => true}})
37
+ end
38
+ options
39
+ end
40
+
41
+ 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,5 @@
1
+ class PageBreakFragment < MarkdownFragment
2
+ def render_on(pdf_object)
3
+ pdf_object.start_new_page(:size => "A4", :layout => :landscape)
4
+ end
5
+ 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,10 @@
1
+ class PresenterNotesFragment < MarkdownFragment
2
+ def render_on(pdf)
3
+ pdf.bounding_box([0,50], :width => pdf.bounds.width, :height => 150) do
4
+ pdf.horizontal_rule
5
+ pdf.stroke
6
+ pdf.move_down(5)
7
+ pdf.text "<b>Slide notes:</b> #{@content.first}", :inline_format => true, :size => 8
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
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'
8
+ require File.dirname(__FILE__) + '/markdown_fragments/code_fragment.rb'
9
+ require File.dirname(__FILE__) + '/markdown_fragments/page_break_fragment.rb'
10
+ require File.dirname(__FILE__) + '/markdown_fragments/presenter_notes_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,243 @@
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
+ in_code_block = false
44
+ code = CodeFragment.new
45
+
46
+
47
+ @content.each_with_index do |line, index|
48
+ line = process_inline_formatting(line)
49
+
50
+ # Assume everything is part of a paragraph by default and
51
+ # add its content to the current in-scope paragraph object.
52
+ #
53
+ paragraph.content << line
54
+ if line == ""
55
+ unless paragraph.content.empty?
56
+ @document_structure << paragraph
57
+ paragraph = ParagraphFragment.new
58
+ end
59
+ end
60
+
61
+ # Deal with inline headings
62
+ #
63
+ unless /^(#+)(\s?)\S/.match(line).nil?
64
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
65
+ hashes = $1.dup
66
+ heading = HeadingFragment.new([line.gsub(hashes,'')])
67
+ heading.level = hashes.length
68
+ @document_structure << heading
69
+ end
70
+
71
+ # Deal with Level 1 Headings
72
+ #
73
+ if !/^(=)+$/.match(line).nil?
74
+ paragraph.content = paragraph.content.delete_if do |item|
75
+ item == line || item == @content[index - 1]
76
+ end
77
+ heading = HeadingFragment.new([@content[index - 1]])
78
+ heading.level = 1
79
+ @document_structure << heading
80
+ end
81
+
82
+ # Deal with Level 2 Headings or horizontal rules.
83
+ #
84
+ if !/^(-)+$/.match(line).nil?
85
+ if @content[index - 1].strip == ''
86
+ # Assume it's a horizontal rule
87
+ #
88
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
89
+ @document_structure << HorizontalRuleFragment.new
90
+ else
91
+ paragraph.content = paragraph.content.delete_if do |item|
92
+ item == line || item == @content[index - 1]
93
+ end
94
+ heading = HeadingFragment.new([@content[index - 1]])
95
+ heading.level = 2
96
+ @document_structure << heading
97
+ end
98
+ end
99
+
100
+ # Deal with all other kinds of horizontal rules
101
+ #
102
+ if !/^(\*+)(\s)?(\*+)(\s)?(\*+)/.match(line).nil? || !/^(-+)(\s)(-+)(\s)(-+)/.match(line).nil?
103
+ if @content[index - 1].strip == ''
104
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
105
+ @document_structure << HorizontalRuleFragment.new
106
+ end
107
+ end
108
+
109
+
110
+ # Try to deal with lists.
111
+ #
112
+ if in_list
113
+ # We're in a list just now.
114
+ #
115
+
116
+ # Remove the content from the paragraph where it will have
117
+ # automatically been appended
118
+ #
119
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
120
+
121
+ # Check to see if we've got a new list item.
122
+ #
123
+ if (!/^(\s*)?\*\s/.match(line).nil? || !/^(\s*)?\d+\.\s/.match(line).nil?)
124
+
125
+ # Find out if this new list item is for a different type of list
126
+ # and deal with that before adding the new list item.
127
+ #
128
+ if list.ordered? && !/^(\s*)?\*\s/.match(line).nil?
129
+ @document_structure << list
130
+ list = ListFragment.new
131
+ elsif !list.ordered? && !/^(\s*)?\d+\.\s/.match(line).nil?
132
+ @document_structure << list
133
+ list = ListFragment.new
134
+ list.ordered = true
135
+ end
136
+
137
+ # Remove the list style and add the new list item.
138
+ #
139
+ list.content << line.sub(/^(\s*)?\*\s/,'').sub(/^(\s*)?\d+\.\s/,'')
140
+
141
+ else
142
+ # If this line isn't a new list item, then it's a continuation for the current
143
+ # list item.
144
+ #
145
+ list.content[-1] += line
146
+ end
147
+
148
+ # If the current line is empty, then we're done with the list.
149
+ #
150
+ if line == ''
151
+ @document_structure << list
152
+ list = ListFragment.new
153
+ in_list = false
154
+ end
155
+ else
156
+ # Not currently in a list, but we've detected a list item
157
+ #
158
+ if (!/^(\s*)?\*\s/.match(line).nil? || !/^(\s*)?\d+\.\s/.match(line).nil?)
159
+ ordered = false
160
+ ordered = true if !/^\s+\d+\.\s/.match(line).nil?
161
+ list = ListFragment.new
162
+ list.ordered = ordered
163
+ list.content << line.sub(/^(\s*)?\*\s/,'').sub(/^(\s*)?\d+\.\s/,'')
164
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
165
+ in_list = true
166
+ end
167
+ end
168
+
169
+
170
+ # Try to deal with code blocks
171
+ #
172
+ if in_code_block
173
+ if (!/(\s{4,})(\$\s)?(\w{1,})/.match(line).nil? || !/(\t{1,})(\$\s)?(\w{1,})/.match(line).nil? )
174
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
175
+ code.content << line
176
+ end
177
+ if line == ''
178
+ @document_structure << code
179
+ code = CodeFragment.new
180
+ in_code_block = false
181
+ end
182
+ else
183
+ if (!/(\s{4,})(\$\s)?(\w{1,})/.match(line).nil? || !/(\t{1,})(\$\s)?(\w{1,})/.match(line).nil? )
184
+ code = CodeFragment.new
185
+ code.content << line
186
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
187
+ in_code_block = true
188
+ end
189
+ end
190
+
191
+ # Deal with a link reference by adding it ot the list of references
192
+ #
193
+ if !/^(\[\S+\]){1}:\s(\S+)\s?(.+)?/.match(line).nil?
194
+ reference, url, title = $1, $2, $3
195
+ paragraph.content = paragraph.content.delete_if { |i| i == line }
196
+ @links_list[:urls_seen] << url
197
+ @links_list[:object].content << [ reference, url, "#{title}" ]
198
+ end
199
+
200
+ # Deal with inline images
201
+ #
202
+ line.scan(/(?:^|\s)?(\!\[(?:.+?)\]\((.+?)\))/) do |val|
203
+ paragraph.content[-1] = paragraph.content[-1].gsub(val[0],'')
204
+ @document_structure << ImageFragment.new([val[1]])
205
+ end
206
+ end
207
+ if !list.content.empty? && ! @document_structure.include?(list)
208
+ @document_structure << list
209
+ list = ListFragment.new
210
+ end
211
+ if !code.content.empty? && !@document_structure.include?(code)
212
+ @document_structure << code
213
+ code = CodeFragment.new
214
+ end
215
+ @document_structure << paragraph unless paragraph.content == ''
216
+ @document_structure << @links_list[:object] if !@links_list[:urls_seen].empty?
217
+ end
218
+
219
+ private
220
+
221
+ def detab(string, tabwidth = 2)
222
+ string.split("\n").collect { |line|
223
+ line.gsub(/(.*?)\t/) do
224
+ $1 + ' ' * (tabwidth - $1.length % tabwidth)
225
+ end
226
+ }.join("\n")
227
+ end
228
+
229
+ # Only do Inline formatting for versions of Prawn which support it.
230
+ #
231
+ def process_inline_formatting(str)
232
+ breg = [ %r{ \b(\_\_) (\S|\S.*?\S) \1\b }x, %r{ (\*\*) (\S|\S.*?\S) \1 }x ]
233
+ ireg = [ %r{ (\*) (\S|\S.*?\S) \1 }x, %r{ \b(_) (\S|\S.*?\S) \1\b }x ]
234
+ if Prawn::VERSION =~ /^0.1/ || Prawn::VERSION =~ /^1/
235
+ 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>} )
236
+ else
237
+ str.gsub(breg[0], %{\\2} ).gsub(breg[1], %{\\2} ).gsub(ireg[0], %{\\2} ).gsub(ireg[1], %{\\2} )
238
+ end
239
+ end
240
+
241
+ end
242
+
243
+ 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")).strip.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,42 @@
1
+ class ShowOffSlide
2
+ attr_accessor :components, :content
3
+
4
+ def initialize(content = '', options = {})
5
+ @components = []
6
+ @options = { :size => 'A4', :layout => :landscape }.merge(options)
7
+ @content = content
8
+ end
9
+
10
+ def render_on(pdf)
11
+ parse if @components.empty?
12
+ pdf.start_new_page(@options)
13
+ pdf.font_size(20)
14
+ @components.each { |c| c.render_on(pdf) }
15
+ end
16
+
17
+ private
18
+
19
+ def parse
20
+
21
+ s =/(\s)?.notes\s(.*)(\n)?/.match(@content)
22
+ if !s.nil?
23
+ @slide_notes = $2
24
+ @content.gsub!(".notes #{@slide_notes}",'')
25
+ else
26
+ @slide_notes = nil
27
+ end
28
+
29
+ sp = MarkdownPrawn::StringParser.new(@content)
30
+ sp.parse!
31
+ #@components << ParagraphFragment.new([@content])
32
+ @components = sp.document_structure
33
+ if @slide_notes
34
+ @components << PresenterNotesFragment.new([@slide_notes])
35
+ end
36
+ end
37
+
38
+ def default_options
39
+ { :align => :center }
40
+ end
41
+
42
+ end
@@ -0,0 +1,27 @@
1
+ class ShowoffSlideStream
2
+ def initialize(paths_for_markdown)
3
+ content = ''
4
+ paths_for_markdown.each { |p| content << IO.read(p) }
5
+ @slides = []
6
+ content.split(/!SLIDE/).each do |slide|
7
+ @slides << ShowOffSlide.new(CGI.unescapeHTML(slide))
8
+ end
9
+ end
10
+
11
+ def to_pdf
12
+ pdf = Prawn::Document.new(:page_size => 'A4', :page_layout => :portrait)
13
+ pdf.text ShowOffUtils.showoff_title, :size => 45, :inline_format => true
14
+ pdf.text "by <b>#{ShowOffUtils.showoff_author}</b>", :size => 30, :inline_format => true
15
+ pdf.move_down(200)
16
+ pdf.text ShowOffUtils.showoff_description, :size => 20
17
+ pdf.bounding_box([0,100], :width => pdf.bounds.width, :height => 150) do
18
+ pdf.horizontal_rule
19
+ pdf.stroke
20
+ pdf.move_down(5)
21
+ pdf.text 'Generated by <b>showoff2pdf</b> by Ryan Stenhouse', :inline_format => true
22
+ pdf.text '<link href="http://github.com/HHRy"><u>http://github.com/HHRy</u></link> | <link href="http://ryanstenhouse.eu"><u>http://ryanstenhouse.eu</u></link>', :inline_format => true
23
+ end
24
+ @slides.each { |slide| slide.render_on(pdf) }
25
+ pdf
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ class ShowOffUtils
2
+ def self.showoff_author(dir = '.')
3
+ index = File.join(dir, ShowOffUtils::SHOWOFF_JSON_FILE )
4
+ order = nil
5
+ if File.exists?(index)
6
+ data = JSON.parse(File.read(index))
7
+ data.is_a?(Hash) && data['author'] || "Anonymous"
8
+ end
9
+ end
10
+
11
+ def self.showoff_description(dir = '.')
12
+ index = File.join(dir, ShowOffUtils::SHOWOFF_JSON_FILE )
13
+ order = nil
14
+ if File.exists?(index)
15
+ data = JSON.parse(File.read(index))
16
+ data.is_a?(Hash) && data['description'] || ""
17
+ end
18
+ end
19
+ end
Binary file
@@ -0,0 +1,23 @@
1
+ description = "ShowOff slides can now be translated into semi-useful PDF documents for printing out and handing out at your talks, using Markdown Prawn and the Prawn libary."
2
+ Gem::Specification.new do |spec|
3
+ spec.name = "show_off_pdf"
4
+ spec.version = '0.0.1.pre'
5
+ spec.platform = Gem::Platform::RUBY
6
+ spec.files = Dir.glob("{bin,lib,.}/**/**/*") +
7
+ ["README.md","COPYING.md",'LICENSE', "show_off_pdf.gemspec"]
8
+ spec.require_path = "lib"
9
+ spec.required_ruby_version = '>= 1.8.7'
10
+ spec.required_rubygems_version = ">= 1.3.6"
11
+ spec.test_files = Dir[ "test/*_test.rb" ]
12
+ spec.has_rdoc = false
13
+ spec.author = "Ryan Stenhouse"
14
+ spec.email = "ryan@ryanstenhouse.eu"
15
+ spec.rubyforge_project = "show_off_pdf"
16
+ spec.add_dependency('prawn', '~>0.10')
17
+ spec.add_dependency('showoff', '~>0.3.2')
18
+
19
+ spec.homepage = "http://ryanstenhouse.eu"
20
+ spec.summary = description
21
+ spec.description = description
22
+ spec.executables << 'showoff2pdf'
23
+ end
data/show_off_pdf.rb ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'prawn'
4
+ require File.dirname(__FILE__) + '/lib/markdown_parser.rb'
5
+ require File.dirname(__FILE__) + '/lib/show_off_utils.rb'
6
+ require File.dirname(__FILE__) + '/lib/show_off_slide_stream.rb'
7
+ require File.dirname(__FILE__) + '/lib/show_off_slide.rb'
8
+ require 'cgi'
9
+ require 'showoff'
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: show_off_pdf
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-16 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
+ - !ruby/object:Gem::Dependency
38
+ name: showoff
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 23
46
+ segments:
47
+ - 0
48
+ - 3
49
+ - 2
50
+ version: 0.3.2
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ description: ShowOff slides can now be translated into semi-useful PDF documents for printing out and handing out at your talks, using Markdown Prawn and the Prawn libary.
54
+ email: ryan@ryanstenhouse.eu
55
+ executables:
56
+ - showoff2pdf
57
+ extensions: []
58
+
59
+ extra_rdoc_files: []
60
+
61
+ files:
62
+ - bin/showoff2pdf
63
+ - lib/markdown_fragments/code_fragment.rb
64
+ - lib/markdown_fragments/heading_fragment.rb
65
+ - lib/markdown_fragments/horizontal_rule_fragment.rb
66
+ - lib/markdown_fragments/image_fragment.rb
67
+ - lib/markdown_fragments/links_reference_fragment.rb
68
+ - lib/markdown_fragments/list_fragment.rb
69
+ - lib/markdown_fragments/markdown_fragment.rb
70
+ - lib/markdown_fragments/page_break_fragment.rb
71
+ - lib/markdown_fragments/paragraph_fragment.rb
72
+ - lib/markdown_fragments/presenter_notes_fragment.rb
73
+ - lib/markdown_fragments.rb
74
+ - lib/markdown_parser/file_parser.rb
75
+ - lib/markdown_parser/parser.rb
76
+ - lib/markdown_parser/string_parser.rb
77
+ - lib/markdown_parser.rb
78
+ - lib/markdown_prawn_exceptions.rb
79
+ - lib/show_off_slide.rb
80
+ - lib/show_off_slide_stream.rb
81
+ - lib/show_off_utils.rb
82
+ - ./bin/showoff2pdf
83
+ - ./COPYING.md
84
+ - ./example/one/01_slide.md
85
+ - ./example/one/images.png
86
+ - ./example/showoff.json
87
+ - ./example.pdf
88
+ - ./HACKING.md
89
+ - ./lib/markdown_fragments/code_fragment.rb
90
+ - ./lib/markdown_fragments/heading_fragment.rb
91
+ - ./lib/markdown_fragments/horizontal_rule_fragment.rb
92
+ - ./lib/markdown_fragments/image_fragment.rb
93
+ - ./lib/markdown_fragments/links_reference_fragment.rb
94
+ - ./lib/markdown_fragments/list_fragment.rb
95
+ - ./lib/markdown_fragments/markdown_fragment.rb
96
+ - ./lib/markdown_fragments/page_break_fragment.rb
97
+ - ./lib/markdown_fragments/paragraph_fragment.rb
98
+ - ./lib/markdown_fragments/presenter_notes_fragment.rb
99
+ - ./lib/markdown_fragments.rb
100
+ - ./lib/markdown_parser/file_parser.rb
101
+ - ./lib/markdown_parser/parser.rb
102
+ - ./lib/markdown_parser/string_parser.rb
103
+ - ./lib/markdown_parser.rb
104
+ - ./lib/markdown_prawn_exceptions.rb
105
+ - ./lib/show_off_slide.rb
106
+ - ./lib/show_off_slide_stream.rb
107
+ - ./lib/show_off_utils.rb
108
+ - ./LICENSE
109
+ - ./README.md
110
+ - ./show_off_pdf-0.0.1.pre.gem
111
+ - ./show_off_pdf.gemspec
112
+ - ./show_off_pdf.rb
113
+ - README.md
114
+ - COPYING.md
115
+ - LICENSE
116
+ - show_off_pdf.gemspec
117
+ has_rdoc: true
118
+ homepage: http://ryanstenhouse.eu
119
+ licenses: []
120
+
121
+ post_install_message:
122
+ rdoc_options: []
123
+
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ hash: 57
132
+ segments:
133
+ - 1
134
+ - 8
135
+ - 7
136
+ version: 1.8.7
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ hash: 23
143
+ segments:
144
+ - 1
145
+ - 3
146
+ - 6
147
+ version: 1.3.6
148
+ requirements: []
149
+
150
+ rubyforge_project: show_off_pdf
151
+ rubygems_version: 1.3.7
152
+ signing_key:
153
+ specification_version: 3
154
+ summary: ShowOff slides can now be translated into semi-useful PDF documents for printing out and handing out at your talks, using Markdown Prawn and the Prawn libary.
155
+ test_files: []
156
+