zlide 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +47 -0
- data/Rakefile +8 -0
- data/bin/zlide +9 -0
- data/lib/zlide.rb +9 -0
- data/lib/zlide/cli/base.rb +40 -0
- data/lib/zlide/deck.rb +56 -0
- data/lib/zlide/layouts/deckjs.haml +44 -0
- data/lib/zlide/redcarpet/renderers.rb +5 -0
- data/lib/zlide/redcarpet/renderers/html.rb +35 -0
- data/lib/zlide/redcarpet/renderers/mock.rb +31 -0
- data/lib/zlide/redcarpet/renderers/pdf.rb +214 -0
- data/lib/zlide/server/base.rb +45 -0
- data/lib/zlide/templates/README.md.tt +5 -0
- data/lib/zlide/templates/config/deck.yml.tt +1 -0
- data/lib/zlide/templates/stylesheets/deck-theme.css +214 -0
- data/lib/zlide/templates/stylesheets/highlight-theme.css +127 -0
- data/lib/zlide/templates/stylesheets/slides.css +10 -0
- data/lib/zlide/version.rb +3 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/zlide/deck_spec.rb +32 -0
- data/spec/zlide/server/base_spec.rb +23 -0
- data/vendor/javascripts/deck.js +1 -0
- data/vendor/javascripts/deck/deck.core.js +498 -0
- data/vendor/javascripts/deck/deck.edit.js +33 -0
- data/vendor/javascripts/deck/deck.goto.js +170 -0
- data/vendor/javascripts/deck/deck.hash.js +142 -0
- data/vendor/javascripts/deck/deck.menu.js +187 -0
- data/vendor/javascripts/deck/deck.navigation.js +92 -0
- data/vendor/javascripts/deck/deck.scale.js +170 -0
- data/vendor/javascripts/deck/deck.status.js +95 -0
- data/vendor/javascripts/highlight.js +1 -0
- data/vendor/javascripts/jquery-1.7.2.min.js +4 -0
- data/vendor/javascripts/modernizr.custom.js +4 -0
- data/vendor/stylesheets/deck.css +4 -0
- data/vendor/stylesheets/deck/deck.core.css +407 -0
- data/vendor/stylesheets/deck/deck.goto.css +41 -0
- data/vendor/stylesheets/deck/deck.hash.css +13 -0
- data/vendor/stylesheets/deck/deck.menu.css +47 -0
- data/vendor/stylesheets/deck/deck.navigation.css +43 -0
- data/vendor/stylesheets/deck/deck.scale.css +28 -0
- data/vendor/stylesheets/deck/deck.status.css +18 -0
- data/vendor/stylesheets/deck/transition/fade.css +43 -0
- data/vendor/stylesheets/deck/transition/fade.scss +69 -0
- data/vendor/stylesheets/deck/transition/horizontal-slide.css +76 -0
- data/vendor/stylesheets/deck/transition/horizontal-slide.scss +90 -0
- data/vendor/stylesheets/deck/transition/vertical-slide.css +94 -0
- data/vendor/stylesheets/deck/transition/vertical-slide.scss +112 -0
- data/vendor/stylesheets/test.css +3 -0
- data/zlide.gemspec +27 -0
- metadata +229 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 David Roetzel
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# zlide
|
2
|
+
|
3
|
+
zlide is a tool to build web-based presentations from markdown files.
|
4
|
+
|
5
|
+
It currently uses deck.js for handling of slides in the browser.
|
6
|
+
|
7
|
+
## Alternatives
|
8
|
+
|
9
|
+
zlide is currently a work in progress and you are probably better served by one of the many alternatives.
|
10
|
+
|
11
|
+
If all you want is to generate and dynamically serve up deck.js presentations from markdown files, have a look at deck.rb (https://github.com/alexch/deck.rb).
|
12
|
+
|
13
|
+
If you want a maximum of flexibility check out the excellent slideshow gem: http://slideshow.rubyforge.org/
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Install via rubygems:
|
18
|
+
|
19
|
+
$ gem install zlide
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Create a new slide deck:
|
24
|
+
|
25
|
+
$ zlide new <deck_name>
|
26
|
+
|
27
|
+
This will scaffold a directory called 'deck_name' with a few files and directories to get you started.
|
28
|
+
|
29
|
+
Change to the newly created directory, put one or more markdown files in the slides/ folder and you are good to go.
|
30
|
+
|
31
|
+
Serve up your presentation:
|
32
|
+
|
33
|
+
$ zlide serve
|
34
|
+
|
35
|
+
This will start a web server on port 4567. Just navigate to http://localhost:4567 in your web browser.
|
36
|
+
|
37
|
+
Compile a pdf handout:
|
38
|
+
|
39
|
+
$ zlide pdf
|
40
|
+
|
41
|
+
## Contributing
|
42
|
+
|
43
|
+
1. Fork it
|
44
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
45
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
46
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
47
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/zlide
ADDED
data/lib/zlide.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Zlide
|
4
|
+
module CLI
|
5
|
+
class Base < Thor
|
6
|
+
include Thor::Actions
|
7
|
+
|
8
|
+
def self.source_root
|
9
|
+
File.expand_path("../../templates", __FILE__)
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "new", "create a new slide deck"
|
13
|
+
def new(name)
|
14
|
+
empty_directory(name)
|
15
|
+
self.destination_root = name
|
16
|
+
["slides", "public", "stylesheets", "config"].each do |subdirectory|
|
17
|
+
empty_directory(subdirectory)
|
18
|
+
end
|
19
|
+
@name = name
|
20
|
+
template "config/deck.yml.tt"
|
21
|
+
template "README.md.tt"
|
22
|
+
copy_file "stylesheets/deck-theme.css"
|
23
|
+
copy_file "stylesheets/highlight-theme.css"
|
24
|
+
copy_file "stylesheets/slides.css"
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "serve", "serve your slides via http"
|
28
|
+
def serve
|
29
|
+
Zlide::Server::Base.run!
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "pdf", "create a pdf version of your slides"
|
33
|
+
def pdf
|
34
|
+
deck = Zlide::Deck.new
|
35
|
+
deck.to_pdf
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/zlide/deck.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'haml'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Zlide
|
5
|
+
|
6
|
+
class Deck
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
files = Dir.glob('slides/*.md')
|
10
|
+
@slides = Array.new
|
11
|
+
files.sort.each do |f|
|
12
|
+
slides = File.read(f).split(/(^!SLIDE.*$)/)[1..-1].each_slice(2).map(&:join)
|
13
|
+
slides.each do |slide|
|
14
|
+
@slides << {:file => f, :content => slide}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def slide(index)
|
20
|
+
@slides[index][:content]
|
21
|
+
end
|
22
|
+
|
23
|
+
def update_slide(index, content)
|
24
|
+
@slides[index][:content] = content
|
25
|
+
file = @slides[index][:file]
|
26
|
+
file_slides = @slides.select{|s| s[:file] == file}.map{|s| s[:content]}
|
27
|
+
File.open("#{file}.tmp", "w") {|f| f.write(file_slides.join)}
|
28
|
+
File.rename("#{file}.tmp", file)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_html
|
32
|
+
slides = markdown_parser(Zlide::Redcarpet::Renderers::HTML).render(full_markdown)
|
33
|
+
layout = File.read(File.join(File.dirname(__FILE__), "layouts", "deckjs.haml"))
|
34
|
+
layout_renderer = Haml::Engine.new(layout)
|
35
|
+
layout_renderer.render { slides }
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_pdf
|
39
|
+
pdf = Zlide::Redcarpet::Renderers::PDF.new
|
40
|
+
markdown_parser(pdf).render(full_markdown)
|
41
|
+
pdf.write_file
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def full_markdown
|
47
|
+
@slides.map{|s| s[:content]}.join
|
48
|
+
end
|
49
|
+
|
50
|
+
def markdown_parser(renderer)
|
51
|
+
::Redcarpet::Markdown.new(renderer, :fenced_code_blocks => true, :tables => true)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
!!!
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%meta{:charset => "utf-8"}/
|
5
|
+
%meta{:content => "IE=edge,chrome=1", "http-equiv" => "X-UA-Compatible"}/
|
6
|
+
%meta{:content => "width=1024, user-scalable=no", :name => "viewport"}/
|
7
|
+
%title= CONFIG['title']
|
8
|
+
/ Required stylesheet
|
9
|
+
%link{:href => "/stylesheets/deck.css", :rel => "stylesheet"}/
|
10
|
+
%link{:href => "/stylesheets/slides.css", :rel => "stylesheet"}/
|
11
|
+
/ Required Modernizr file
|
12
|
+
%script{:src => "/javascripts/modernizr.custom.js"}
|
13
|
+
%body.deck-container
|
14
|
+
/ Begin slides. Just make elements with a class of slide.
|
15
|
+
= yield
|
16
|
+
/ End slides.
|
17
|
+
/ Begin extension snippets. Add or remove as needed.
|
18
|
+
/ deck.navigation snippet
|
19
|
+
%a.deck-prev-link{:href => "#", :title => "Previous"} ←
|
20
|
+
%a.deck-next-link{:href => "#", :title => "Next"} →
|
21
|
+
/ deck.status snippet
|
22
|
+
%p.deck-status
|
23
|
+
%span.deck-status-current
|
24
|
+
\/
|
25
|
+
%span.deck-status-total
|
26
|
+
/ deck.goto snippet
|
27
|
+
%form.goto-form{:action => ".", :method => "get"}
|
28
|
+
%label{:for => "goto-slide"} Go to slide:
|
29
|
+
%input#goto-slide{:list => "goto-datalist", :name => "slidenum", :type => "text"}/
|
30
|
+
%datalist#goto-datalist
|
31
|
+
%input{:type => "submit", :value => "Go"}/
|
32
|
+
/ deck.hash snippet
|
33
|
+
%a.deck-permalink{:href => ".", :title => "Permalink to this slide"} #
|
34
|
+
/ End extension snippets.
|
35
|
+
/ Required JS files.
|
36
|
+
%script{:src => "/javascripts/jquery-1.7.2.min.js"}
|
37
|
+
%script{:src => "/javascripts/deck.js"}
|
38
|
+
%script{:src => "/javascripts/highlight.js"}
|
39
|
+
/ Initialize the deck. You can put this in an external file if desired.
|
40
|
+
:javascript
|
41
|
+
$(function() {
|
42
|
+
$.deck('.slide');
|
43
|
+
$('pre code').each(function(i, e) {hljs.highlightBlock(e)});
|
44
|
+
});
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Zlide
|
2
|
+
module Redcarpet
|
3
|
+
module Renderers
|
4
|
+
|
5
|
+
class HTML < ::Redcarpet::Render::HTML
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@slide_open = false
|
9
|
+
super(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def paragraph(text)
|
13
|
+
result = ""
|
14
|
+
if text =~ /^!SLIDE/
|
15
|
+
result += "</section>\n" if @slide_open
|
16
|
+
result += "<section class=\"slide\">\n"
|
17
|
+
@slide_open = true
|
18
|
+
else
|
19
|
+
result += "<p>#{text}</p>\n"
|
20
|
+
end
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
def doc_footer
|
25
|
+
if @slide_open
|
26
|
+
@slide_open = false
|
27
|
+
"</section>"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Zlide
|
2
|
+
module Redcarpet
|
3
|
+
module Renderers
|
4
|
+
|
5
|
+
class Mock < ::Redcarpet::Render::HTML #Base
|
6
|
+
|
7
|
+
def block_html(text)
|
8
|
+
puts "block html: #{text}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def paragraph(text)
|
12
|
+
if text =~ /!SLIDE/
|
13
|
+
puts "p: #{text}"
|
14
|
+
else
|
15
|
+
puts super(text)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def header(text, level)
|
20
|
+
puts "header#{level}: #{text}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def entity(text)
|
24
|
+
puts "ENTITY: #{text}"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'prawn'
|
3
|
+
require 'prawn/measurement_extensions'
|
4
|
+
|
5
|
+
module Zlide
|
6
|
+
module Redcarpet
|
7
|
+
module Renderers
|
8
|
+
|
9
|
+
class PDF < ::Redcarpet::Render::Base
|
10
|
+
|
11
|
+
class ListData < ::Array ; end
|
12
|
+
class TableData < ::Array ; end
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@pdf = Prawn::Document.new(
|
16
|
+
:page_size => "A4",
|
17
|
+
:page_layout => :landscape,
|
18
|
+
:info => {:Title => CONFIG['title']}
|
19
|
+
)
|
20
|
+
@pdf.define_grid(:rows => 2, :columns => 2, :gutter => 1.cm)
|
21
|
+
@current_row = 0
|
22
|
+
@current_column = 0
|
23
|
+
@command_queue = []
|
24
|
+
@current_box = @pdf.grid(0,0)
|
25
|
+
@slide_open = false
|
26
|
+
@list_items = []
|
27
|
+
@table_cells = []
|
28
|
+
@table_rows = []
|
29
|
+
@table_cache = []
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
def header(text, level)
|
34
|
+
text_size = 17 - level
|
35
|
+
@command_queue << Proc.new do
|
36
|
+
@pdf.text text, :size => text_size, :style => :bold, :inline_format => true
|
37
|
+
if level <= 2
|
38
|
+
@pdf.line_width 0.5
|
39
|
+
@pdf.horizontal_rule
|
40
|
+
@pdf.stroke
|
41
|
+
end
|
42
|
+
@pdf.move_down text_size
|
43
|
+
end
|
44
|
+
""
|
45
|
+
end
|
46
|
+
|
47
|
+
def list(content, type)
|
48
|
+
data = ListData.new
|
49
|
+
counter = 1
|
50
|
+
number_of_items = content.count('.')
|
51
|
+
@list_items.pop(number_of_items).each do |item|
|
52
|
+
if item.is_a? String
|
53
|
+
case type
|
54
|
+
when :unordered
|
55
|
+
data << ["•", item]
|
56
|
+
when :ordered
|
57
|
+
data << ["#{counter}.", item]
|
58
|
+
counter += 1
|
59
|
+
end
|
60
|
+
else
|
61
|
+
data << [nil, item]
|
62
|
+
@command_queue.delete(item)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
index = @table_cache.size
|
66
|
+
@command_queue << data
|
67
|
+
@table_cache << data
|
68
|
+
"__table_#{index}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def list_item(text, type)
|
72
|
+
table = nil
|
73
|
+
matches = text.scan(/\n__table_\d+/)
|
74
|
+
if matches.any?
|
75
|
+
table_marker = matches.first
|
76
|
+
table = @table_cache[table_marker.sub("\n__table_", '').to_i]
|
77
|
+
text.sub!(table_marker, '')
|
78
|
+
end
|
79
|
+
@list_items << text
|
80
|
+
@list_items << table if table
|
81
|
+
table ? ".." : "."
|
82
|
+
end
|
83
|
+
|
84
|
+
def table(header, body)
|
85
|
+
data = TableData.new
|
86
|
+
body_rows = @table_rows.pop(body.count('.'))
|
87
|
+
header_rows = @table_rows.pop(body.count('.'))
|
88
|
+
data.concat header_rows.map{|row| row.map{|column| tag(:b, column)}}
|
89
|
+
data.concat body_rows
|
90
|
+
@command_queue << data
|
91
|
+
""
|
92
|
+
end
|
93
|
+
|
94
|
+
def table_row(content)
|
95
|
+
@table_rows << @table_cells.pop(content.count('.'))
|
96
|
+
"."
|
97
|
+
end
|
98
|
+
|
99
|
+
def table_cell(content, alignment)
|
100
|
+
@table_cells << content
|
101
|
+
"."
|
102
|
+
end
|
103
|
+
|
104
|
+
def emphasis(text)
|
105
|
+
tag :i, text
|
106
|
+
end
|
107
|
+
|
108
|
+
def double_emphasis(text)
|
109
|
+
tag :b, text
|
110
|
+
end
|
111
|
+
|
112
|
+
def strikethrough(text)
|
113
|
+
tag :strikethrough, text
|
114
|
+
end
|
115
|
+
|
116
|
+
def link(href, title, content)
|
117
|
+
"<link href='#{href}'>#{content}</link>"
|
118
|
+
end
|
119
|
+
|
120
|
+
def image(href, title, alt_text)
|
121
|
+
@command_queue << Proc.new do
|
122
|
+
@pdf.image File.join('public', href), :fit => [@pdf.bounds.width, @pdf.cursor - 5]
|
123
|
+
end
|
124
|
+
""
|
125
|
+
end
|
126
|
+
|
127
|
+
def paragraph(text)
|
128
|
+
if text =~ /^!SLIDE/
|
129
|
+
if @slide_open
|
130
|
+
next_box
|
131
|
+
else
|
132
|
+
@slide_open = true
|
133
|
+
end
|
134
|
+
else
|
135
|
+
@command_queue << Proc.new { @pdf.text(text, :inline_format => true) }
|
136
|
+
end
|
137
|
+
""
|
138
|
+
end
|
139
|
+
|
140
|
+
def block_code(code, language)
|
141
|
+
code.gsub!(/^(\s+)/m) { |m| "\xC2\xA0" * m.size }
|
142
|
+
code_block = Proc.new do
|
143
|
+
font = @pdf.font.name
|
144
|
+
@pdf.bounding_box([5, @pdf.cursor], :width => @pdf.bounds.width - 10) do
|
145
|
+
@pdf.move_down 5
|
146
|
+
@pdf.font "Courier"
|
147
|
+
@pdf.indent(5) { @pdf.text code, :size => 9 }
|
148
|
+
@pdf.line_width 0.1
|
149
|
+
@pdf.join_style :round
|
150
|
+
@pdf.stroke_bounds
|
151
|
+
end
|
152
|
+
@pdf.font font
|
153
|
+
end
|
154
|
+
@command_queue << code_block
|
155
|
+
""
|
156
|
+
end
|
157
|
+
|
158
|
+
def write_file
|
159
|
+
@pdf.render_file("#{CONFIG['title']}.pdf")
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def next_box
|
165
|
+
@current_box.bounding_box do
|
166
|
+
@pdf.line_width 0.5
|
167
|
+
@pdf.stroke_bounds
|
168
|
+
@pdf.bounding_box([5,@pdf.bounds.top - 5], :width => @pdf.bounds.width - 10, :height => @pdf.bounds.height - 10) do
|
169
|
+
@command_queue.each do |c|
|
170
|
+
if c.is_a? Proc
|
171
|
+
c.call
|
172
|
+
elsif c.is_a? ListData
|
173
|
+
cell_style = {:borders => [], :padding => 2}
|
174
|
+
data = c.map do |row|
|
175
|
+
row.map do |cell|
|
176
|
+
if cell.is_a? Array
|
177
|
+
@pdf.make_table cell, :cell_style => cell_style.merge(:inline_format => true), :column_widths => [25, @pdf.bounds.width - 55]
|
178
|
+
else
|
179
|
+
cell
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
@pdf.table data, :cell_style => cell_style, :column_widths => [25, @pdf.bounds.width - 30] do |t|
|
184
|
+
t.cells.grep(Prawn::Table::Cell::Text).each {|text| text.style({:inline_format => true})}
|
185
|
+
end
|
186
|
+
elsif c.is_a? TableData
|
187
|
+
@pdf.table c, :cell_style => {:inline_format => true, :border_width => 0.5}
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
@command_queue.clear
|
193
|
+
if @current_row == 1 and @current_column == 1
|
194
|
+
@pdf.start_new_page
|
195
|
+
@current_row = 0
|
196
|
+
@current_column = 0
|
197
|
+
else
|
198
|
+
@current_column += 1
|
199
|
+
if @current_column > 1
|
200
|
+
@current_row += 1
|
201
|
+
@current_column = 0
|
202
|
+
end
|
203
|
+
end
|
204
|
+
@current_box = @pdf.grid(@current_row, @current_column)
|
205
|
+
end
|
206
|
+
|
207
|
+
def tag(type, content)
|
208
|
+
"<#{type}>#{content}</#{type}>"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|