prawn-format 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +37 -0
- data/Rakefile +31 -0
- data/examples/basic-formatting.rb +37 -0
- data/examples/christmas-carol.txt +717 -0
- data/examples/document.rb +61 -0
- data/examples/flowing.rb +24 -0
- data/examples/style-classes.rb +12 -0
- data/examples/syntax-highlighting.rb +31 -0
- data/examples/tags.rb +24 -0
- data/lib/prawn/format.rb +211 -0
- data/lib/prawn/format/effects/link.rb +30 -0
- data/lib/prawn/format/effects/underline.rb +32 -0
- data/lib/prawn/format/instructions/base.rb +62 -0
- data/lib/prawn/format/instructions/tag_close.rb +52 -0
- data/lib/prawn/format/instructions/tag_open.rb +95 -0
- data/lib/prawn/format/instructions/text.rb +89 -0
- data/lib/prawn/format/layout_builder.rb +113 -0
- data/lib/prawn/format/lexer.rb +222 -0
- data/lib/prawn/format/line.rb +99 -0
- data/lib/prawn/format/parser.rb +181 -0
- data/lib/prawn/format/state.rb +189 -0
- data/lib/prawn/format/text_object.rb +107 -0
- data/lib/prawn/format/version.rb +11 -0
- data/manual/html.rb +187 -0
- data/manual/include/basics.rb +6 -0
- data/manual/include/breaks.rb +13 -0
- data/manual/include/custom-tags.rb +10 -0
- data/manual/include/custom-tags2.rb +2 -0
- data/manual/include/indent.rb +4 -0
- data/manual/include/options.rb +15 -0
- data/manual/include/style-classes.rb +5 -0
- data/manual/manual.txt +101 -0
- data/manual/pdf.rb +204 -0
- data/prawn-format.gemspec +45 -0
- data/spec/layout_builder_spec.rb +27 -0
- data/spec/lexer_spec.rb +91 -0
- data/spec/parser_spec.rb +103 -0
- data/spec/spec_helper.rb +24 -0
- metadata +157 -0
data/manual/pdf.rb
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
|
2
|
+
require 'prawn'
|
3
|
+
require 'prawn/format'
|
4
|
+
require 'prawn/format/version'
|
5
|
+
require 'coderay'
|
6
|
+
|
7
|
+
def process_style(document, style, content, line_number)
|
8
|
+
content = process_substitutions(content)
|
9
|
+
|
10
|
+
case style
|
11
|
+
when "h1" then h1(document, content)
|
12
|
+
when "h2" then h2(document, content)
|
13
|
+
when "p" then paragraph(document, content)
|
14
|
+
when "fp" then paragraph(document, content, false)
|
15
|
+
when "ul" then start_list(document)
|
16
|
+
when "li" then list_item(document, content)
|
17
|
+
when "/ul" then end_list(document)
|
18
|
+
when "page" then new_page(document)
|
19
|
+
when "highlight" then highlight(document, content)
|
20
|
+
when "hr" then horiz_rule(document)
|
21
|
+
when "center" then center(document, content)
|
22
|
+
else warn "unknown style #{style.inspect}"
|
23
|
+
end
|
24
|
+
|
25
|
+
rescue Exception => err
|
26
|
+
puts "[error occurred while processing line ##{line_number}]"
|
27
|
+
raise
|
28
|
+
end
|
29
|
+
|
30
|
+
def process_substitutions(content)
|
31
|
+
content.
|
32
|
+
gsub(/%FORMAT:VERSION%/, Prawn::Format::Version::STRING).
|
33
|
+
gsub(/%NOW%/, Time.now.utc.strftime("%e %B %Y at %H:%M UTC")).
|
34
|
+
gsub(/%PDF\{(.*?)\}HTML\{(.*?)\}END%/, '\\1')
|
35
|
+
end
|
36
|
+
|
37
|
+
def center(document, content)
|
38
|
+
padding(document, :clear => true)
|
39
|
+
document.text(content, :plain => false, :align => :center)
|
40
|
+
padding(document)
|
41
|
+
end
|
42
|
+
|
43
|
+
def horiz_rule(document)
|
44
|
+
padding(document, :clear => true)
|
45
|
+
document.stroke_color "000000"
|
46
|
+
document.stroke_horizontal_rule
|
47
|
+
padding(document)
|
48
|
+
end
|
49
|
+
|
50
|
+
def h1(document, content)
|
51
|
+
clear_padding!
|
52
|
+
document.text "<h1>#{content}</h1>"
|
53
|
+
document.stroke_color "000080"
|
54
|
+
document.stroke_horizontal_rule
|
55
|
+
padding(document, :size => document.font_size * 2)
|
56
|
+
end
|
57
|
+
|
58
|
+
def h2(document, content)
|
59
|
+
clear_padding!
|
60
|
+
document.text "<h2>#{content}</h2>"
|
61
|
+
document.stroke_color "000080"
|
62
|
+
document.stroke_horizontal_rule
|
63
|
+
padding(document, :size => document.font_size * 2)
|
64
|
+
end
|
65
|
+
|
66
|
+
def paragraph(document, content, indent=true)
|
67
|
+
return unless content.strip.length > 0
|
68
|
+
clear_padding!
|
69
|
+
document.text "#{content}", :align => :justify
|
70
|
+
padding(document, :size => document.font_size / 2)
|
71
|
+
end
|
72
|
+
|
73
|
+
def start_list(document)
|
74
|
+
padding(document)
|
75
|
+
end
|
76
|
+
|
77
|
+
def list_item(document, content)
|
78
|
+
clear_padding!
|
79
|
+
|
80
|
+
indent_b = document.font_size * 3
|
81
|
+
indent = document.font_size * 4
|
82
|
+
|
83
|
+
document.start_new_page if document.y < document.font_size
|
84
|
+
y = document.y - document.bounds.absolute_bottom
|
85
|
+
document.text "•", :at => [indent_b, y - document.font.ascender]
|
86
|
+
document.layout(content, :align => :justify) do |helper|
|
87
|
+
while !helper.done?
|
88
|
+
y = helper.fill(indent, y, document.bounds.width-indent, :height => document.y)
|
89
|
+
if helper.done?
|
90
|
+
document.y = y + document.bounds.absolute_bottom
|
91
|
+
else
|
92
|
+
document.start_new_page
|
93
|
+
y = document.y - document.bounds.absolute_bottom
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
padding(document, :size => document.font_size / 4)
|
99
|
+
end
|
100
|
+
|
101
|
+
def end_list(document)
|
102
|
+
padding(document)
|
103
|
+
end
|
104
|
+
|
105
|
+
def new_page(document)
|
106
|
+
document.start_new_page
|
107
|
+
clear_padding!
|
108
|
+
end
|
109
|
+
|
110
|
+
def highlight(document, content)
|
111
|
+
file, syntax = content.split(/,/)
|
112
|
+
analyzed = CodeRay.scan(File.read(File.join(File.dirname(__FILE__), file)), syntax.to_sym)
|
113
|
+
html = "<pre>" + analyzed.html + "</pre>"
|
114
|
+
|
115
|
+
padding(document, :size => document.font_size * 2, :clear => true)
|
116
|
+
y = document.y - document.bounds.absolute_bottom
|
117
|
+
start_y = y + document.font_size
|
118
|
+
|
119
|
+
indent = document.font_size * 2
|
120
|
+
|
121
|
+
document.layout(html) do |helper|
|
122
|
+
while !helper.done?
|
123
|
+
y = helper.fill(indent, y, document.bounds.width-indent*2, :height => document.y)
|
124
|
+
if helper.done?
|
125
|
+
document.y = y + document.bounds.absolute_bottom
|
126
|
+
else
|
127
|
+
document.start_new_page
|
128
|
+
y = document.y - document.bounds.absolute_bottom
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
document.stroke_color "a0a0a0"
|
134
|
+
document.rectangle [document.font_size, start_y], bounds.width - document.font_size*2, (start_y - y)
|
135
|
+
document.stroke
|
136
|
+
|
137
|
+
padding(document, :size => document.font_size)
|
138
|
+
end
|
139
|
+
|
140
|
+
def clear_padding!
|
141
|
+
@last_padding = nil
|
142
|
+
end
|
143
|
+
|
144
|
+
# This padding stuff is mostly here just so that adjacent vertical spaces
|
145
|
+
# will collapse.
|
146
|
+
def padding(document, options={})
|
147
|
+
size = options[:size] || document.font_size
|
148
|
+
|
149
|
+
if @last_padding
|
150
|
+
full_size = [@last_padding, size].max
|
151
|
+
size = full_size - @last_padding
|
152
|
+
@last_padding = full_size
|
153
|
+
else
|
154
|
+
@last_padding = size
|
155
|
+
end
|
156
|
+
|
157
|
+
document.y -= size
|
158
|
+
clear_padding! if options[:clear]
|
159
|
+
end
|
160
|
+
|
161
|
+
SERIF_FONT = "/Library/Fonts/Baskerville.dfont"
|
162
|
+
|
163
|
+
Prawn::Document.generate("prawn-format.pdf", :compress => true) do
|
164
|
+
if File.exists?(SERIF_FONT)
|
165
|
+
font_families["Baskerville"] = {
|
166
|
+
:normal => { :file => SERIF_FONT, :font => 1 },
|
167
|
+
:italic => { :file => SERIF_FONT, :font => 2 },
|
168
|
+
:bold => { :file => SERIF_FONT, :font => 4 }, # semi-bold, not bold
|
169
|
+
:bold_italic => { :file => SERIF_FONT, :font => 3 }
|
170
|
+
}
|
171
|
+
font "Baskerville", :size => 14
|
172
|
+
else
|
173
|
+
warn "Baskerville font is preferred for the manual, but could not be found. Using Times-Roman."
|
174
|
+
font "Times-Roman", :size => 14
|
175
|
+
end
|
176
|
+
|
177
|
+
tags :h1 => { :font_size => "2em", :font_weight => :bold, :color => "navy" },
|
178
|
+
:h2 => { :font_size => "1.5em", :font_weight => :bold, :color => "navy" },
|
179
|
+
:about => { :font_size => "80%", :color => "808080", :font_style => :italic }
|
180
|
+
|
181
|
+
styles :no => { :color => "gray" },
|
182
|
+
:c => { :color => "#666" },
|
183
|
+
:s => { :color => "#d20" },
|
184
|
+
:dl => { :color => "black" },
|
185
|
+
:co => { :color => "#036", :font_weight => :bold },
|
186
|
+
:pc => { :color => "#038", :font_weight => :bold },
|
187
|
+
:sy => { :color => "#A60" },
|
188
|
+
:r => { :color => "#080" },
|
189
|
+
:i => { :color => "#00D", :font_weight => :bold },
|
190
|
+
:idl => { :color => "#888", :font_weight => :bold },
|
191
|
+
:dl => { :color => "#840", :font_weight => :bold }
|
192
|
+
|
193
|
+
File.open("#{File.dirname(__FILE__)}/manual.txt") do |source|
|
194
|
+
number = 0
|
195
|
+
source.each_line do |line|
|
196
|
+
number += 1
|
197
|
+
line.chomp!
|
198
|
+
next if line.length == 0
|
199
|
+
|
200
|
+
style, content = line.match(/^(\S+)\.\s*(.*)/)[1,2]
|
201
|
+
process_style(self, style, content, number)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = %q{prawn-format}
|
3
|
+
s.version = "0.1.0"
|
4
|
+
|
5
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
6
|
+
s.authors = ["Jamis Buck"]
|
7
|
+
s.date = %q{2009-01-26}
|
8
|
+
s.description = %q{an extension of Prawn that allows inline formatting}
|
9
|
+
s.email = %q{jamis@jamisbuck.org}
|
10
|
+
s.extra_rdoc_files = ["lib/prawn/format/effects/link.rb", "lib/prawn/format/effects/underline.rb", "lib/prawn/format/instructions/base.rb", "lib/prawn/format/instructions/tag_close.rb", "lib/prawn/format/instructions/tag_open.rb", "lib/prawn/format/instructions/text.rb", "lib/prawn/format/layout_builder.rb", "lib/prawn/format/lexer.rb", "lib/prawn/format/line.rb", "lib/prawn/format/parser.rb", "lib/prawn/format/state.rb", "lib/prawn/format/text_object.rb", "lib/prawn/format/version.rb", "lib/prawn/format.rb"]
|
11
|
+
s.files = ["examples/basic-formatting.rb", "examples/christmas-carol.txt", "examples/document.rb", "examples/flowing.rb", "examples/style-classes.rb", "examples/syntax-highlighting.rb", "examples/tags.rb", "lib/prawn/format/effects/link.rb", "lib/prawn/format/effects/underline.rb", "lib/prawn/format/instructions/base.rb", "lib/prawn/format/instructions/tag_close.rb", "lib/prawn/format/instructions/tag_open.rb", "lib/prawn/format/instructions/text.rb", "lib/prawn/format/layout_builder.rb", "lib/prawn/format/lexer.rb", "lib/prawn/format/line.rb", "lib/prawn/format/parser.rb", "lib/prawn/format/state.rb", "lib/prawn/format/text_object.rb", "lib/prawn/format/version.rb", "lib/prawn/format.rb", "manual/html.rb", "manual/include/basics.rb", "manual/include/breaks.rb", "manual/include/custom-tags.rb", "manual/include/custom-tags2.rb", "manual/include/indent.rb", "manual/include/options.rb", "manual/include/style-classes.rb", "manual/manual.txt", "manual/pdf.rb", "Rakefile", "spec/layout_builder_spec.rb", "spec/lexer_spec.rb", "spec/parser_spec.rb", "spec/spec_helper.rb", "Manifest", "prawn-format.gemspec"]
|
12
|
+
s.has_rdoc = true
|
13
|
+
s.homepage = %q{http://rubyforge.org/projects/prawn}
|
14
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Prawn-format"]
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
s.rubyforge_project = %q{prawn}
|
17
|
+
s.rubygems_version = %q{1.2.0}
|
18
|
+
s.summary = %q{an extension of Prawn that allows inline formatting}
|
19
|
+
s.test_files = ["spec/layout_builder_spec.rb", "spec/lexer_spec.rb", "spec/parser_spec.rb"]
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 2
|
24
|
+
|
25
|
+
if current_version >= 3 then
|
26
|
+
s.add_runtime_dependency(%q<prawn>, [">= 0.4"])
|
27
|
+
s.add_runtime_dependency(%q<prawn>, ["< 0.5"])
|
28
|
+
s.add_development_dependency(%q<echoe>, [">= 0"])
|
29
|
+
s.add_development_dependency(%q<rake>, [">= 0"])
|
30
|
+
s.add_development_dependency(%q<coderay>, [">= 0"])
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<prawn>, [">= 0.4"])
|
33
|
+
s.add_dependency(%q<prawn>, ["< 0.5"])
|
34
|
+
s.add_dependency(%q<echoe>, [">= 0"])
|
35
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
36
|
+
s.add_dependency(%q<coderay>, [">= 0"])
|
37
|
+
end
|
38
|
+
else
|
39
|
+
s.add_dependency(%q<prawn>, [">= 0.4"])
|
40
|
+
s.add_dependency(%q<prawn>, ["< 0.5"])
|
41
|
+
s.add_dependency(%q<echoe>, [">= 0"])
|
42
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
43
|
+
s.add_dependency(%q<coderay>, [">= 0"])
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
|
4
|
+
|
5
|
+
describe "when building a text layout" do
|
6
|
+
|
7
|
+
before(:each) { create_pdf }
|
8
|
+
|
9
|
+
it "should understand common tags by default" do
|
10
|
+
layout = new_layout("<b>hi</b> <i>there</i>")
|
11
|
+
assert_nothing_raised { lines(layout) }
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def new_layout(text, opts={})
|
17
|
+
Prawn::Format::LayoutBuilder.new(@pdf, text, opts)
|
18
|
+
end
|
19
|
+
|
20
|
+
def lines(layout)
|
21
|
+
lines = []
|
22
|
+
while (line = layout.next)
|
23
|
+
lines << line
|
24
|
+
end
|
25
|
+
return lines
|
26
|
+
end
|
27
|
+
end
|
data/spec/lexer_spec.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
|
4
|
+
|
5
|
+
def lexer_for(text)
|
6
|
+
Prawn::Format::Lexer.new(text)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "when scanning text" do
|
10
|
+
|
11
|
+
it "should scan a single text word as a chunk" do
|
12
|
+
lexer = lexer_for("christmas")
|
13
|
+
assert_equal({ :type => :text, :text => ["christmas"] }, lexer.next)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should delimit multiple words by spaces" do
|
17
|
+
lexer = lexer_for("a christmas carol by charles dickens")
|
18
|
+
assert_equal({ :type => :text, :text => ["a", " ", "christmas", " ", "carol", " ", "by", " ", "charles", " ", "dickens"] }, lexer.next)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should delimit multiple words by hyphens" do
|
22
|
+
lexer = lexer_for("christmas-carol-thingy")
|
23
|
+
assert_equal({ :type => :text, :text => ["christmas", "-", "carol", "-", "thingy"] }, lexer.next)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should delimit multiple words by em-dashes" do
|
27
|
+
lexer = lexer_for("christmas—carol—thingy")
|
28
|
+
assert_equal({ :type => :text, :text => ["christmas", "—", "carol", "—", "thingy"] }, lexer.next)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should report nil as end-of-stream after scanning" do
|
32
|
+
lexer = lexer_for("a christmas carol by charles dickens")
|
33
|
+
assert_not_nil lexer.next
|
34
|
+
assert_nil lexer.next
|
35
|
+
assert_nil lexer.next
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "when scanning XML entities" do
|
41
|
+
|
42
|
+
Prawn::Format::Lexer::ENTITY_MAP.each do |key, value|
|
43
|
+
it "should map #{key} to #{value}" do
|
44
|
+
lexer = lexer_for("&#{key};")
|
45
|
+
assert_equal({ :type => :text, :text => [value] }, lexer.next)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should convert decimal entities to utf-8" do
|
50
|
+
lexer = lexer_for('—')
|
51
|
+
assert_equal({ :type => :text, :text => ["\xe2\x80\x94"] }, lexer.next)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should convert hexadecimal entities to utf-8" do
|
55
|
+
lexer = lexer_for('—')
|
56
|
+
assert_equal({ :type => :text, :text => ["\xe2\x80\x94"] }, lexer.next)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should raise InvalidFormat on unrecognized entities" do
|
60
|
+
lexer = lexer_for('&bogus;')
|
61
|
+
assert_raises(Prawn::Format::Lexer::InvalidFormat) do
|
62
|
+
lexer.next
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "when scanning tags" do
|
69
|
+
|
70
|
+
it "should return tag with empty options for a tag with no options" do
|
71
|
+
lexer = lexer_for("<test>")
|
72
|
+
assert_equal({ :type => :open, :tag => :test, :options => {} }, lexer.next)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should scan tags with different delimiters consistently" do
|
76
|
+
lexer = lexer_for("<test first=january second=\"february\" third='march'>")
|
77
|
+
assert_equal({ :type => :open, :tag => :test, :options => { :first => "january", :second => "february", :third => "march" } }, lexer.next)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should scan closing tag" do
|
81
|
+
lexer = lexer_for("</test>")
|
82
|
+
assert_equal({ :type => :close, :tag => :test }, lexer.next)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should scan self-closing tag as two tokens" do
|
86
|
+
lexer = lexer_for("<test/>")
|
87
|
+
assert_equal({ :type => :open, :tag => :test, :options => {} }, lexer.next)
|
88
|
+
assert_equal({ :type => :close, :tag => :test }, lexer.next)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
data/spec/parser_spec.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
|
4
|
+
|
5
|
+
describe "when parsing formatted text" do
|
6
|
+
|
7
|
+
before(:each) { create_pdf }
|
8
|
+
|
9
|
+
it "should raise TagError when it does not recognize a tag" do
|
10
|
+
parser = parser_for("<hello>")
|
11
|
+
assert_raises(Prawn::Format::Parser::TagError) { parser.next }
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should raise TagError when it encounters an unmatched closing tag" do
|
15
|
+
parser = parser_for("</b>")
|
16
|
+
assert_raises(Prawn::Format::Parser::TagError) { parser.next }
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should raise TagError when it a tag is closed with the wrong type" do
|
20
|
+
parser = parser_for("<b></i>")
|
21
|
+
assert_raises(Prawn::Format::Parser::TagError) { parse(parser) }
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should apply styles defined for tag" do
|
25
|
+
parser = parser_for("a<b>c</b>d")
|
26
|
+
weights = [:normal, :bold, :bold, :bold, :normal]
|
27
|
+
assert_equal weights, parse(parser).map { |i| i.state.font_weight }
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should honor custom tag styles" do
|
31
|
+
parser = parser_for("a<k>c</k>d", :tags => { :k => { :text_decoration => :underline } })
|
32
|
+
decorations = [:none, :underline, :underline, :underline, :none]
|
33
|
+
assert_equal decorations, parse(parser).map { |i| i.state.text_decoration }
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should honor custom style classes" do
|
37
|
+
parser = parser_for("a<j class='test'>c</j>d", :tags => { :j => {} }, :styles => { :test => { :text_decoration => :underline } })
|
38
|
+
decorations = [:none, :underline, :underline, :underline, :none]
|
39
|
+
assert_equal decorations, parse(parser).map { |i| i.state.text_decoration }
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should parse delimited text as separate instructions" do
|
43
|
+
parser = parser_for("a b-cd")
|
44
|
+
bits = ["a", " ", "b", "-", "cd"]
|
45
|
+
assert_equal bits, parse(parser).map { |i| i.text }
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should return nil after last token is parsed" do
|
49
|
+
parser = parser_for("a")
|
50
|
+
assert_not_nil parser.next
|
51
|
+
assert_nil parser.next
|
52
|
+
assert_nil parser.next
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should report eos? at end of stream" do
|
56
|
+
parser = parser_for("a")
|
57
|
+
assert !parser.eos?
|
58
|
+
parser.next
|
59
|
+
assert parser.eos?
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should return next instruction without consuming it when peek is called" do
|
63
|
+
parser = parser_for("a")
|
64
|
+
assert_equal "a", parser.peek.text
|
65
|
+
assert !parser.eos?
|
66
|
+
assert_equal "a", parser.next.text
|
67
|
+
assert parser.eos?
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should save instruction for next call when push is called" do
|
71
|
+
parser = parser_for("a")
|
72
|
+
k = parser.next
|
73
|
+
assert parser.eos?
|
74
|
+
parser.push(k)
|
75
|
+
assert !parser.eos?
|
76
|
+
assert_equal k, parser.next
|
77
|
+
assert parser.eos?
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should map meta styles to styles on the tag" do
|
81
|
+
parser = parser_for("<k name='bob'>a</k>", :tags => { :k => { :meta => { :name => :__name__ } } })
|
82
|
+
i = parser.next
|
83
|
+
assert_equal "bob", i.tag[:style][:__name__]
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def parser_for(text, opts={})
|
89
|
+
tags = @pdf.tags.merge(opts[:tags] || {})
|
90
|
+
styles = @pdf.styles.merge(opts[:styles] || {})
|
91
|
+
@parser = Prawn::Format::Parser.new(@pdf, text, opts.merge(:styles => styles, :tags => tags))
|
92
|
+
end
|
93
|
+
|
94
|
+
def parse(parser)
|
95
|
+
instructions = []
|
96
|
+
|
97
|
+
while instr = parser.next
|
98
|
+
instructions << instr
|
99
|
+
end
|
100
|
+
|
101
|
+
return instructions
|
102
|
+
end
|
103
|
+
end
|