prawn 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +340 -0
- data/LICENSE +56 -0
- data/README +30 -0
- data/Rakefile +83 -0
- data/data/fonts/Activa.ttf +0 -0
- data/data/fonts/Chalkboard.ttf +0 -0
- data/data/fonts/Courier-Bold.afm +342 -0
- data/data/fonts/Courier-BoldOblique.afm +342 -0
- data/data/fonts/Courier-Oblique.afm +342 -0
- data/data/fonts/Courier.afm +342 -0
- data/data/fonts/DejaVuSans.ttf +0 -0
- data/data/fonts/Dustismo_Roman.ttf +0 -0
- data/data/fonts/Helvetica-Bold.afm +2827 -0
- data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
- data/data/fonts/Helvetica-Oblique.afm +3051 -0
- data/data/fonts/Helvetica.afm +3051 -0
- data/data/fonts/MustRead.html +19 -0
- data/data/fonts/Symbol.afm +213 -0
- data/data/fonts/Times-Bold.afm +2588 -0
- data/data/fonts/Times-BoldItalic.afm +2384 -0
- data/data/fonts/Times-Italic.afm +2667 -0
- data/data/fonts/Times-Roman.afm +2419 -0
- data/data/fonts/ZapfDingbats.afm +225 -0
- data/data/fonts/comicsans.ttf +0 -0
- data/data/fonts/gkai00mp.ttf +0 -0
- data/data/images/dice.png +0 -0
- data/data/images/pigs.jpg +0 -0
- data/data/images/ruport.png +0 -0
- data/data/images/ruport_data.dat +0 -0
- data/data/images/ruport_transparent.png +0 -0
- data/data/images/stef.jpg +0 -0
- data/data/shift_jis_text.txt +1 -0
- data/examples/addressbook.csv +6 -0
- data/examples/alignment.rb +16 -0
- data/examples/bounding_boxes.pdf +62 -0
- data/examples/bounding_boxes.rb +30 -0
- data/examples/canvas.pdf +81 -0
- data/examples/canvas.rb +12 -0
- data/examples/cell.rb +27 -0
- data/examples/currency.csv +1834 -0
- data/examples/curves.rb +10 -0
- data/examples/fancy_table.rb +48 -0
- data/examples/font_size.rb +19 -0
- data/examples/hexagon.rb +14 -0
- data/examples/image.pdf +0 -0
- data/examples/image.rb +23 -0
- data/examples/image2.rb +13 -0
- data/examples/inline_styles.pdf +117 -0
- data/examples/kerning.rb +27 -0
- data/examples/line.rb +31 -0
- data/examples/multi_page_layout.rb +14 -0
- data/examples/on_page_start.rb +17 -0
- data/examples/page_geometry.rb +28 -0
- data/examples/polygons.rb +16 -0
- data/examples/ruport_formatter.rb +47 -0
- data/examples/ruport_helpers.rb +17 -0
- data/examples/russian_boxes.rb +34 -0
- data/examples/simple_text.rb +15 -0
- data/examples/simple_text_ttf.rb +16 -0
- data/examples/sjis.rb +19 -0
- data/examples/table.rb +45 -0
- data/examples/table_bench.rb +92 -0
- data/examples/text_flow.rb +65 -0
- data/examples/utf8.rb +12 -0
- data/lib/prawn.rb +33 -0
- data/lib/prawn/compatibility.rb +33 -0
- data/lib/prawn/document.rb +334 -0
- data/lib/prawn/document/bounding_box.rb +253 -0
- data/lib/prawn/document/page_geometry.rb +78 -0
- data/lib/prawn/document/table.rb +253 -0
- data/lib/prawn/document/text.rb +346 -0
- data/lib/prawn/errors.rb +33 -0
- data/lib/prawn/font.rb +5 -0
- data/lib/prawn/font/cmap.rb +59 -0
- data/lib/prawn/font/metrics.rb +414 -0
- data/lib/prawn/font/wrapping.rb +45 -0
- data/lib/prawn/graphics.rb +285 -0
- data/lib/prawn/graphics/cell.rb +226 -0
- data/lib/prawn/images.rb +241 -0
- data/lib/prawn/images/jpg.rb +43 -0
- data/lib/prawn/images/png.rb +178 -0
- data/lib/prawn/pdf_object.rb +64 -0
- data/lib/prawn/reference.rb +47 -0
- data/spec/bounding_box_spec.rb +120 -0
- data/spec/box_calculation_spec.rb +17 -0
- data/spec/document_spec.rb +152 -0
- data/spec/graphics_spec.rb +250 -0
- data/spec/images_spec.rb +42 -0
- data/spec/jpg_spec.rb +25 -0
- data/spec/metrics_spec.rb +60 -0
- data/spec/pdf_object_spec.rb +102 -0
- data/spec/png_spec.rb +35 -0
- data/spec/reference_spec.rb +29 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/table_spec.rb +145 -0
- data/spec/text_spec.rb +190 -0
- data/vendor/font_ttf/ttf.rb +20 -0
- data/vendor/font_ttf/ttf/datatypes.rb +189 -0
- data/vendor/font_ttf/ttf/encodings.rb +140 -0
- data/vendor/font_ttf/ttf/exceptions.rb +28 -0
- data/vendor/font_ttf/ttf/file.rb +290 -0
- data/vendor/font_ttf/ttf/fontchunk.rb +77 -0
- data/vendor/font_ttf/ttf/table/cmap.rb +408 -0
- data/vendor/font_ttf/ttf/table/cvt.rb +49 -0
- data/vendor/font_ttf/ttf/table/fpgm.rb +48 -0
- data/vendor/font_ttf/ttf/table/gasp.rb +88 -0
- data/vendor/font_ttf/ttf/table/glyf.rb +452 -0
- data/vendor/font_ttf/ttf/table/head.rb +86 -0
- data/vendor/font_ttf/ttf/table/hhea.rb +96 -0
- data/vendor/font_ttf/ttf/table/hmtx.rb +98 -0
- data/vendor/font_ttf/ttf/table/kern.rb +186 -0
- data/vendor/font_ttf/ttf/table/loca.rb +75 -0
- data/vendor/font_ttf/ttf/table/maxp.rb +81 -0
- data/vendor/font_ttf/ttf/table/name.rb +222 -0
- data/vendor/font_ttf/ttf/table/os2.rb +172 -0
- data/vendor/font_ttf/ttf/table/post.rb +120 -0
- data/vendor/font_ttf/ttf/table/prep.rb +27 -0
- data/vendor/font_ttf/ttf/table/vhea.rb +45 -0
- data/vendor/font_ttf/ttf/table/vmtx.rb +36 -0
- metadata +180 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
require "prawn"
|
5
|
+
|
6
|
+
# Demonstrates some features stolen from Ruport::Formatter::PDF
|
7
|
+
Prawn::Document.generate("ruport.pdf") do
|
8
|
+
move_down 50
|
9
|
+
# TODO: Figure out where to set the y cursor to.
|
10
|
+
stroke_horizontal_rule
|
11
|
+
text "Hi there"
|
12
|
+
pad(50) { text "I'm Padded" }
|
13
|
+
text "I'm far away"
|
14
|
+
stroke_horizontal_line 50, 100
|
15
|
+
stroke_vertical_line_at 300, 50, 250
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
require 'prawn'
|
3
|
+
|
4
|
+
class Array
|
5
|
+
def combine(arr)
|
6
|
+
output = []
|
7
|
+
self.each do |i1|
|
8
|
+
arr.each do |i2|
|
9
|
+
output += [[i1,i2]]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
output
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def recurse_bounding_box(pdf, max_depth=5, depth=1)
|
17
|
+
box = pdf.bounds
|
18
|
+
width = (box.width-15)/2
|
19
|
+
height = (box.height-15)/2
|
20
|
+
left_top_corners = [5, box.right-width-5].combine [box.top-5, height+5]
|
21
|
+
left_top_corners.each do |lt|
|
22
|
+
pdf.bounding_box(lt, :width=>width, :height=>height) do
|
23
|
+
pdf.stroke_rectangle [0,height], width, height
|
24
|
+
recurse_bounding_box(pdf, max_depth, depth+1) if depth<max_depth
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
Prawn::Document.generate("russian_boxes.pdf") do |pdf|
|
31
|
+
recurse_bounding_box(pdf)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
require "prawn"
|
5
|
+
|
6
|
+
Prawn::Document.generate "hello.pdf" do
|
7
|
+
fill_color "0000ff"
|
8
|
+
text "Hello World", :at => [200,720], :size => 32
|
9
|
+
font "Times-Roman"
|
10
|
+
fill_color "ff0000"
|
11
|
+
text "Overcoming singular font limitation", :at => [5,5]
|
12
|
+
start_new_page
|
13
|
+
font "Courier"
|
14
|
+
text "Goodbye World", :at => [288,50]
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
require "prawn"
|
5
|
+
|
6
|
+
Prawn::Document.generate "hello-ttf.pdf" do
|
7
|
+
fill_color "0000ff"
|
8
|
+
font "#{Prawn::BASEDIR}/data/fonts/comicsans.ttf"
|
9
|
+
text "Hello World", :at => [200,720], :size => 32
|
10
|
+
|
11
|
+
font "#{Prawn::BASEDIR}/data/fonts/Chalkboard.ttf"
|
12
|
+
|
13
|
+
pad(20) do
|
14
|
+
text "This is chalkboard wrapping " * 20
|
15
|
+
end
|
16
|
+
end
|
data/examples/sjis.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# Tests passing non utf-8 data into Prawns text function. Should
|
4
|
+
# be transparently converted to utf-8 and rendered as usual.
|
5
|
+
#
|
6
|
+
# NOTE: only works on ruby1.9 compatible VMs, and requires the current
|
7
|
+
# font to include japanese glyphs. On 1.8.x comaptible VMs, an exception
|
8
|
+
# will be raised.
|
9
|
+
|
10
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
11
|
+
require "prawn"
|
12
|
+
|
13
|
+
datafile = File.join(File.dirname(__FILE__), "..", "data", "shift_jis_text.txt")
|
14
|
+
sjis_str = File.open(datafile, "r:shift_jis") { |f| f.gets }
|
15
|
+
|
16
|
+
Prawn::Document.generate("sjis.pdf") do
|
17
|
+
font "#{Prawn::BASEDIR}/data/fonts/gkai00mp.ttf"
|
18
|
+
text sjis_str
|
19
|
+
end
|
data/examples/table.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
require "prawn"
|
5
|
+
|
6
|
+
Prawn::Document.generate("table.pdf") do
|
7
|
+
font "#{Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf"
|
8
|
+
table [["ὕαλον ϕαγεῖν", "baaar", "1" ],
|
9
|
+
["This is","a sample", "2" ],
|
10
|
+
["Table", "dont\ncha\nknow?", "3" ],
|
11
|
+
[ "It", "Rules", "4" ],
|
12
|
+
[ "It", "Rules", "4" ],
|
13
|
+
[ "It", "Rules", "4" ],
|
14
|
+
[ "It", "Rules", "4" ],
|
15
|
+
[ "It", "Rules", "4" ],
|
16
|
+
[ "It", "Rules", "4" ],
|
17
|
+
[ "It", "Rules", "4" ],
|
18
|
+
[ "It", "Rules", "4" ],
|
19
|
+
[ "It", "Rules", "4" ],
|
20
|
+
[ "It", "Rules\nwith an iron fist", "x" ],
|
21
|
+
[ "It", "Rules", "4" ],
|
22
|
+
[ "It", "Rules", "4" ],
|
23
|
+
[ "It", "Rules", "4" ],
|
24
|
+
[ "It", "Rules", "4" ],
|
25
|
+
[ "It", "Rules", "4" ],
|
26
|
+
[ "It", "Rules", "4" ],
|
27
|
+
[ "It", "Rules", "4" ],
|
28
|
+
[ "It", "Rules", "4" ],
|
29
|
+
[ "It", "Rules", "4" ]],
|
30
|
+
|
31
|
+
:font_size => 24,
|
32
|
+
:horizontal_padding => 10,
|
33
|
+
:vertical_padding => 3,
|
34
|
+
:border => 2,
|
35
|
+
:position => :center,
|
36
|
+
:headers => ["Column A","Column B","#"]
|
37
|
+
pad(20) do
|
38
|
+
text "This should appear in the original font size"
|
39
|
+
end
|
40
|
+
|
41
|
+
table [[ "Wide", "columns", "streeetch"],
|
42
|
+
["are","mighty fine", "streeeeeeeech"]],
|
43
|
+
:widths => { 0 => 200, 1 => 250 }, :position => 5
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "pdf/writer"
|
5
|
+
require "pdf/simpletable"
|
6
|
+
gem "pdf-wrapper", ">=0.1.2"
|
7
|
+
require "pdf/wrapper"
|
8
|
+
require "fastercsv"
|
9
|
+
require "benchmark"
|
10
|
+
|
11
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
12
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'examples')
|
13
|
+
require "prawn"
|
14
|
+
require "ruport_formatter"
|
15
|
+
|
16
|
+
csv_data = FasterCSV.read("#{Prawn::BASEDIR}/examples/currency.csv") *
|
17
|
+
(ARGV.first || 1).to_i
|
18
|
+
|
19
|
+
####################################
|
20
|
+
# PDF::Writer Table Rendering Prep #
|
21
|
+
####################################
|
22
|
+
pdf = PDF::Writer.new
|
23
|
+
pdf.select_font("Helvetica")
|
24
|
+
|
25
|
+
table = PDF::SimpleTable.new do |tab|
|
26
|
+
tab.column_order.push(*%w(date rate))
|
27
|
+
|
28
|
+
tab.columns["date"] = PDF::SimpleTable::Column.new("date") { |col|
|
29
|
+
col.heading = "Date"
|
30
|
+
}
|
31
|
+
tab.columns["rate"] = PDF::SimpleTable::Column.new("rate") { |col|
|
32
|
+
col.heading = "Rate"
|
33
|
+
}
|
34
|
+
|
35
|
+
tab.orientation = :center
|
36
|
+
|
37
|
+
data = csv_data.map do |e|
|
38
|
+
{ "date" => e[0], "rate" => e[1] }
|
39
|
+
end
|
40
|
+
|
41
|
+
tab.data.replace data
|
42
|
+
end
|
43
|
+
|
44
|
+
##############################
|
45
|
+
# Prawn Table Rendering Prep #
|
46
|
+
##############################
|
47
|
+
doc = Prawn::Document.new
|
48
|
+
|
49
|
+
#####################
|
50
|
+
# Ruport/Prawn Prep #
|
51
|
+
#####################
|
52
|
+
|
53
|
+
ruport_table = Table(%w[Date Rate], :data => csv_data)
|
54
|
+
ruport_doc = Prawn::Document.new
|
55
|
+
|
56
|
+
##############################
|
57
|
+
# PDF::Wrapper Table Rendering Prep
|
58
|
+
##############################
|
59
|
+
wrapper_doc = PDF::Wrapper.new
|
60
|
+
wrapper_table = PDF::Wrapper::Table.new do |t|
|
61
|
+
t.data = csv_data
|
62
|
+
t.table_options :font_size => 6
|
63
|
+
end
|
64
|
+
|
65
|
+
#######################
|
66
|
+
# Benchmarking code #
|
67
|
+
#######################
|
68
|
+
|
69
|
+
puts "Processing #{csv_data.length} records"
|
70
|
+
|
71
|
+
Benchmark.bmbm do |x|
|
72
|
+
x.report("PDF Wrapper") do
|
73
|
+
wrapper_doc.table( wrapper_table, :width => 100 ) unless wrapper_doc.finished?
|
74
|
+
wrapper_doc.render_to_file('currency_pdf_wrapper.pdf')
|
75
|
+
end
|
76
|
+
x.report("Prawn") do
|
77
|
+
doc.table(csv_data, :font_size => 10,
|
78
|
+
:vertical_padding => 2,
|
79
|
+
:horizontal_padding => 5,
|
80
|
+
:position => :center,
|
81
|
+
:row_colors => :pdf_writer,
|
82
|
+
:headers => ["Date","Rate"])
|
83
|
+
doc.render_file('currency_prawn.pdf')
|
84
|
+
end
|
85
|
+
x.report("Ruport/Prawn") do
|
86
|
+
ruport_table.save_as('currency_ruport.pdf', :document => ruport_doc)
|
87
|
+
end
|
88
|
+
x.report("PDF Writer") do
|
89
|
+
table.render_on(pdf)
|
90
|
+
pdf.save_as('currency_pdf_writer.pdf')
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
require "prawn"
|
5
|
+
|
6
|
+
content = <<-EOS
|
7
|
+
How does
|
8
|
+
Prawn deal with
|
9
|
+
white
|
10
|
+
space
|
11
|
+
|
12
|
+
and
|
13
|
+
|
14
|
+
line
|
15
|
+
breaks?
|
16
|
+
EOS
|
17
|
+
|
18
|
+
poem = <<-EOS
|
19
|
+
GOOD-BYE
|
20
|
+
|
21
|
+
Good-bye, proud world! I'm going home: Thou art not my friend, and I'm not thine. Long through thy weary crowds I roam; A river-ark on the ocean brine, Long I've been tossed like the driven foam: But now, proud world! I'm going home.
|
22
|
+
|
23
|
+
Good-bye to Flattery's fawning face; To Grandeur with his wise grimace; To upstart Wealth's averted eye; To supple Office, low and high; To crowded halls, to court and street; To frozen hearts and hasting feet; To those who go, and those who come; Good-bye, proud world! I'm going home.
|
24
|
+
|
25
|
+
I am going to my own hearth-stone, Bosomed in yon green hills alone,-- secret nook in a pleasant land, Whose groves the frolic fairies planned; Where arches green, the livelong day, Echo the blackbird's roundelay, And vulgar feet have never trod A spot that is sacred to thought and God.
|
26
|
+
|
27
|
+
O, when I am safe in my sylvan home, I tread on the pride of Greece and Rome; And when I am stretched beneath the pines, Where the evening star so holy shines, I laugh at the lore and the pride of man, At the sophist schools and the learned clan; For what are they all, in their high conceit, When man in the bush with God may meet?
|
28
|
+
EOS
|
29
|
+
|
30
|
+
overflow = "This text should flow gracefully onto the next page, like a stream"+
|
31
|
+
" flows elegantly from a mountain lake down into the village below."
|
32
|
+
|
33
|
+
Prawn::Document.generate("flow.pdf") do |pdf|
|
34
|
+
|
35
|
+
pdf.font "Times-Roman"
|
36
|
+
pdf.stroke_line [pdf.bounds.left, pdf.bounds.top],
|
37
|
+
[pdf.bounds.right, pdf.bounds.top]
|
38
|
+
|
39
|
+
pdf.text content, :size => 10
|
40
|
+
|
41
|
+
pdf.bounding_box([100,600], :width => 200, :height => 525) do
|
42
|
+
pdf.stroke_line [pdf.bounds.left, pdf.bounds.top],
|
43
|
+
[pdf.bounds.right, pdf.bounds.top]
|
44
|
+
pdf.text poem, :size => 12
|
45
|
+
end
|
46
|
+
|
47
|
+
pdf.bounding_box([325,600], :width => 200, :height => 525) do
|
48
|
+
pdf.stroke_line [pdf.bounds.left, pdf.bounds.top],
|
49
|
+
[pdf.bounds.right, pdf.bounds.top]
|
50
|
+
pdf.text poem.reverse, :size => 12
|
51
|
+
end
|
52
|
+
|
53
|
+
pdf.text overflow * 10, :size => 14
|
54
|
+
|
55
|
+
pdf.text "Hooray! We've conquered the evil PDF gods", :size => 36
|
56
|
+
|
57
|
+
pdf.bounding_box([100,425], :width => 300) do
|
58
|
+
pdf.stroke_line [pdf.bounds.left, pdf.bounds.top],
|
59
|
+
[pdf.bounds.right, pdf.bounds.top]
|
60
|
+
pdf.text poem, :size => 12, :spacing => 5
|
61
|
+
end
|
62
|
+
|
63
|
+
pdf.text "And this text automatically goes below the poem", :size => 18
|
64
|
+
|
65
|
+
end
|
data/examples/utf8.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
require "prawn"
|
5
|
+
|
6
|
+
Prawn::Document.generate("utf8.pdf") do
|
7
|
+
font "#{Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf"
|
8
|
+
text "ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει." * 20
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
|
data/lib/prawn.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# prawn.rb : A library for PDF generation in Ruby
|
4
|
+
#
|
5
|
+
# Copyright April 2008, Gregory Brown. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
|
9
|
+
require "prawn/compatibility"
|
10
|
+
require "prawn/errors"
|
11
|
+
require "prawn/pdf_object"
|
12
|
+
require "prawn/graphics"
|
13
|
+
require "prawn/images"
|
14
|
+
require "prawn/images/jpg"
|
15
|
+
require "prawn/images/png"
|
16
|
+
require "prawn/document"
|
17
|
+
require "prawn/reference"
|
18
|
+
require "prawn/font"
|
19
|
+
|
20
|
+
%w[font_ttf].each do |dep|
|
21
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + "/../vendor/#{dep}")
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'ttf'
|
25
|
+
|
26
|
+
module Prawn
|
27
|
+
file = __FILE__
|
28
|
+
file = File.readlink(file) if File.symlink?(file)
|
29
|
+
dir = File.dirname(file)
|
30
|
+
|
31
|
+
# The base source directory for Prawn as installed on the system
|
32
|
+
BASEDIR = File.expand_path(File.join(dir, '..'))
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
if RUBY_VERSION < "1.9"
|
4
|
+
require "strscan"
|
5
|
+
|
6
|
+
class String
|
7
|
+
alias_method :lines, :to_a
|
8
|
+
|
9
|
+
def each_char
|
10
|
+
scanner, char = StringScanner.new(self), /./mu
|
11
|
+
loop { yield(scanner.scan(char) || break) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def ruby_18
|
16
|
+
yield
|
17
|
+
end
|
18
|
+
|
19
|
+
def ruby_19
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
else
|
24
|
+
|
25
|
+
def ruby_18
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
def ruby_19
|
30
|
+
yield
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,334 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# document.rb : Implements PDF document generation for Prawn
|
4
|
+
#
|
5
|
+
# Copyright April 2008, Gregory Brown. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
|
9
|
+
require "stringio"
|
10
|
+
require "prawn/document/page_geometry"
|
11
|
+
require "prawn/document/bounding_box"
|
12
|
+
require "prawn/document/text"
|
13
|
+
require "prawn/document/table"
|
14
|
+
|
15
|
+
module Prawn
|
16
|
+
class Document
|
17
|
+
|
18
|
+
include Prawn::Graphics
|
19
|
+
include Prawn::Images
|
20
|
+
include Text
|
21
|
+
include PageGeometry
|
22
|
+
|
23
|
+
attr_accessor :y, :margin_box
|
24
|
+
attr_reader :margins, :page_size, :page_layout
|
25
|
+
|
26
|
+
|
27
|
+
# Creates and renders a PDF document.
|
28
|
+
#
|
29
|
+
# The block argument is necessary only when you need to make
|
30
|
+
# use of a closure.
|
31
|
+
#
|
32
|
+
# # Using implicit block form and rendering to a file
|
33
|
+
# Prawn::Document.generate "foo.pdf" do
|
34
|
+
# font "Times-Roman"
|
35
|
+
# text "Hello World", :at => [200,720], :size => 32
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# # Using explicit block form and rendering to a file
|
39
|
+
# content = "Hello World"
|
40
|
+
# Prawn::Document.generate "foo.pdf" do |pdf|
|
41
|
+
# pdf.font "Times-Roman"
|
42
|
+
# pdf.text content, :at => [200,720], :size => 32
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
def self.generate(filename,options={},&block)
|
46
|
+
pdf = Prawn::Document.new(options)
|
47
|
+
block.arity < 1 ? pdf.instance_eval(&block) : yield(pdf)
|
48
|
+
pdf.render_file(filename)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Creates a new PDF Document. The following options are available:
|
52
|
+
#
|
53
|
+
# <tt>:page_size</tt>:: One of the Document::PageGeometry::SIZES [LETTER]
|
54
|
+
# <tt>:page_layout</tt>:: Either <tt>:portrait</tt> or <tt>:landscape</tt>
|
55
|
+
# <tt>:on_page_start</tt>:: Optional proc run at each page start
|
56
|
+
# <tt>:on_page_stop</tt>:: Optional proc run at each page stop
|
57
|
+
# <tt>:left_margin</tt>:: Sets the left margin in points [ 0.5 inch]
|
58
|
+
# <tt>:right_margin</tt>:: Sets the right margin in points [ 0.5 inch]
|
59
|
+
# <tt>:top_margin</tt>:: Sets the top margin in points [ 0.5 inch]
|
60
|
+
# <tt>:bottom_margin</tt>:: Sets the bottom margin in points [0.5 inch]
|
61
|
+
# <tt>:skip_page_creation</tt>:: Creates a document without starting the first page [false]
|
62
|
+
#
|
63
|
+
#
|
64
|
+
# # New document, US Letter paper, portrait orientation
|
65
|
+
# pdf = Prawn::Document.new
|
66
|
+
#
|
67
|
+
# # New document, A4 paper, landscaped
|
68
|
+
# pdf = Prawn::Document.new(:page_size => "A4", :page_layout => :landscape)
|
69
|
+
#
|
70
|
+
# # New document, draws a line at the start of each new page
|
71
|
+
# pdf = Prawn::Document.new(:on_page_start =>
|
72
|
+
# lambda { |doc| doc.line [0,100], [300,100] } )
|
73
|
+
#
|
74
|
+
def initialize(options={})
|
75
|
+
@objects = []
|
76
|
+
@info = ref(:Creator => "Prawn", :Producer => "Prawn")
|
77
|
+
@pages = ref(:Type => :Pages, :Count => 0, :Kids => [])
|
78
|
+
@root = ref(:Type => :Catalog, :Pages => @pages)
|
79
|
+
@page_start_proc = options[:on_page_start]
|
80
|
+
@page_stop_proc = options[:on_page_end]
|
81
|
+
@page_size = options[:page_size] || "LETTER"
|
82
|
+
@page_layout = options[:page_layout] || :portrait
|
83
|
+
|
84
|
+
@margins = { :left => options[:left_margin] || 36,
|
85
|
+
:right => options[:right_margin] || 36,
|
86
|
+
:top => options[:top_margin] || 36,
|
87
|
+
:bottom => options[:bottom_margin] || 36 }
|
88
|
+
|
89
|
+
generate_margin_box
|
90
|
+
|
91
|
+
@bounding_box = @margin_box
|
92
|
+
|
93
|
+
start_new_page unless options[:skip_page_creation]
|
94
|
+
end
|
95
|
+
|
96
|
+
# Creates and advances to a new page in the document.
|
97
|
+
# Runs the <tt>on_page_start</tt> lambda if one was provided at
|
98
|
+
# document creation time (See Document.new).
|
99
|
+
#
|
100
|
+
# Page size, margins, and layout can also be set when generating a
|
101
|
+
# new page. These values will become the new defaults for page creation
|
102
|
+
#
|
103
|
+
# pdf.start_new_page(:size => "LEGAL", :layout => :landscape)
|
104
|
+
# pdf.start_new_page(:left_margin => 50, :right_margin => 50)
|
105
|
+
#
|
106
|
+
def start_new_page(options = {})
|
107
|
+
@page_size = options[:size] if options[:size]
|
108
|
+
@page_layout = options[:layout] if options[:layout]
|
109
|
+
|
110
|
+
[:left,:right,:top,:bottom].each do |side|
|
111
|
+
if options[:"#{side}_margin"]
|
112
|
+
@margins[side] = options[:"#{side}_margin"]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
finish_page_content if @page_content
|
117
|
+
generate_margin_box
|
118
|
+
@page_content = ref(:Length => 0)
|
119
|
+
|
120
|
+
@current_page = ref(:Type => :Page,
|
121
|
+
:Parent => @pages,
|
122
|
+
:MediaBox => page_dimensions,
|
123
|
+
:Contents => @page_content)
|
124
|
+
set_current_font
|
125
|
+
update_colors
|
126
|
+
@pages.data[:Kids] << @current_page
|
127
|
+
@pages.data[:Count] += 1
|
128
|
+
|
129
|
+
add_content "q"
|
130
|
+
|
131
|
+
@y = @margin_box.absolute_top
|
132
|
+
@page_start_proc[self] if @page_start_proc
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns the number of pages in the document
|
136
|
+
#
|
137
|
+
# pdf = Prawn::Document.new
|
138
|
+
# pdf.page_count #=> 1
|
139
|
+
# 3.times { pdf.start_new_page }
|
140
|
+
# pdf.page_count #=> 4
|
141
|
+
#
|
142
|
+
def page_count
|
143
|
+
@pages.data[:Count]
|
144
|
+
end
|
145
|
+
|
146
|
+
# Renders the PDF document to string
|
147
|
+
#
|
148
|
+
def render
|
149
|
+
output = StringIO.new
|
150
|
+
finish_page_content
|
151
|
+
|
152
|
+
render_header(output)
|
153
|
+
render_body(output)
|
154
|
+
render_xref(output)
|
155
|
+
render_trailer(output)
|
156
|
+
str = output.string
|
157
|
+
str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
|
158
|
+
str
|
159
|
+
end
|
160
|
+
|
161
|
+
# Renders the PDF document to file.
|
162
|
+
#
|
163
|
+
# pdf.render_file "foo.pdf"
|
164
|
+
#
|
165
|
+
def render_file(filename)
|
166
|
+
Kernel.const_defined?("Encoding") ? mode = "wb:ASCII-8BIT" : mode = "wb"
|
167
|
+
File.open(filename,mode) { |f| f << render }
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns the current BoundingBox object, which is by default
|
171
|
+
# the box represented by the margin box. When called from within
|
172
|
+
# a <tt>bounding_box</tt> block, the box defined by that call will
|
173
|
+
# be used.
|
174
|
+
#
|
175
|
+
def bounds
|
176
|
+
@bounding_box
|
177
|
+
end
|
178
|
+
|
179
|
+
# Moves up the document by n points
|
180
|
+
#
|
181
|
+
def move_up(n)
|
182
|
+
self.y += n
|
183
|
+
end
|
184
|
+
|
185
|
+
# Moves down the document by n point
|
186
|
+
#
|
187
|
+
def move_down(n)
|
188
|
+
self.y -= n
|
189
|
+
end
|
190
|
+
|
191
|
+
# Moves down the document and then executes a block.
|
192
|
+
#
|
193
|
+
# pdf.text "some text"
|
194
|
+
# pdf.pad_top(100) do
|
195
|
+
# pdf.text "This is 100 points below the previous line of text"
|
196
|
+
# end
|
197
|
+
# pdf.text "This text appears right below the previous line of text"
|
198
|
+
#
|
199
|
+
def pad_top(y)
|
200
|
+
move_down(y)
|
201
|
+
yield
|
202
|
+
end
|
203
|
+
|
204
|
+
# Executes a block then moves down the document
|
205
|
+
#
|
206
|
+
# pdf.text "some text"
|
207
|
+
# pdf.pad_bottom(100) do
|
208
|
+
# pdf.text "This text appears right below the previous line of text"
|
209
|
+
# end
|
210
|
+
# pdf.text "This is 100 points below the previous line of text"
|
211
|
+
#
|
212
|
+
def pad_bottom(y)
|
213
|
+
yield
|
214
|
+
move_down(y)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Moves down the document by y, executes a block, then moves down the
|
218
|
+
# document by y again.
|
219
|
+
#
|
220
|
+
# pdf.text "some text"
|
221
|
+
# pdf.pad(100) do
|
222
|
+
# pdf.text "This is 100 points below the previous line of text"
|
223
|
+
# end
|
224
|
+
# pdf.text "This is 100 points below the previous line of text"
|
225
|
+
#
|
226
|
+
def pad(y)
|
227
|
+
move_down(y)
|
228
|
+
yield
|
229
|
+
move_down(y)
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
def mask(*fields) # :nodoc:
|
234
|
+
# Stores the current state of the named attributes, executes the block, and
|
235
|
+
# then restores the original values after the block has executed.
|
236
|
+
# -- I will remove the nodoc if/when this feature is a little less hacky
|
237
|
+
stored = {}
|
238
|
+
fields.each { |f| stored[f] = send(f) }
|
239
|
+
yield
|
240
|
+
fields.each { |f| send("#{f}=", stored[f]) }
|
241
|
+
end
|
242
|
+
|
243
|
+
private
|
244
|
+
|
245
|
+
def generate_margin_box
|
246
|
+
old_margin_box = @margin_box
|
247
|
+
@margin_box = BoundingBox.new(
|
248
|
+
self,
|
249
|
+
[ @margins[:left], page_dimensions[-1] - @margins[:top] ] ,
|
250
|
+
:width => page_dimensions[-2] - (@margins[:left] + @margins[:right]),
|
251
|
+
:height => page_dimensions[-1] - (@margins[:top] + @margins[:bottom])
|
252
|
+
)
|
253
|
+
|
254
|
+
# update bounding box if not flowing from the previous page
|
255
|
+
# TODO: This may have a bug where the old margin is restored
|
256
|
+
# when the bounding box exits.
|
257
|
+
@bounding_box = @margin_box if old_margin_box == @bounding_box
|
258
|
+
end
|
259
|
+
|
260
|
+
def ref(data)
|
261
|
+
@objects.push(Prawn::Reference.new(@objects.size + 1, data)).last
|
262
|
+
end
|
263
|
+
|
264
|
+
def add_content(str)
|
265
|
+
@page_content << str << "\n"
|
266
|
+
end
|
267
|
+
|
268
|
+
# Add a new type to the current pages ProcSet
|
269
|
+
def proc_set(*types)
|
270
|
+
@current_page.data[:ProcSet] ||= ref([])
|
271
|
+
@current_page.data[:ProcSet].data |= types
|
272
|
+
end
|
273
|
+
|
274
|
+
def page_resources
|
275
|
+
@current_page.data[:Resources] ||= {}
|
276
|
+
end
|
277
|
+
|
278
|
+
def page_fonts
|
279
|
+
page_resources[:Font] ||= {}
|
280
|
+
end
|
281
|
+
|
282
|
+
def page_xobjects
|
283
|
+
page_resources[:XObject] ||= {}
|
284
|
+
end
|
285
|
+
|
286
|
+
def finish_page_content
|
287
|
+
@page_stop_proc[self] if @page_stop_proc
|
288
|
+
add_content "Q"
|
289
|
+
@page_content.data[:Length] = @page_content.stream.size
|
290
|
+
end
|
291
|
+
|
292
|
+
# Write out the PDF Header, as per spec 3.4.1
|
293
|
+
def render_header(output)
|
294
|
+
# pdf version
|
295
|
+
output << "%PDF-1.3\n"
|
296
|
+
|
297
|
+
# 4 binary chars, as recommended by the spec
|
298
|
+
output << "\xFF\xFF\xFF\xFF\n"
|
299
|
+
end
|
300
|
+
|
301
|
+
# Write out the PDF Body, as per spec 3.4.2
|
302
|
+
def render_body(output)
|
303
|
+
@objects.each do |ref|
|
304
|
+
ref.offset = output.size
|
305
|
+
output << ref.object
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# Write out the PDF Cross Reference Table, as per spec 3.4.3
|
310
|
+
def render_xref(output)
|
311
|
+
@xref_offset = output.size
|
312
|
+
output << "xref\n"
|
313
|
+
output << "0 #{@objects.size + 1}\n"
|
314
|
+
output << "0000000000 65535 f \n"
|
315
|
+
@objects.each do |ref|
|
316
|
+
output.printf("%010d", ref.offset)
|
317
|
+
output << " 00000 n \n"
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
# Write out the PDF Body, as per spec 3.4.4
|
322
|
+
def render_trailer(output)
|
323
|
+
trailer_hash = {:Size => @objects.size + 1,
|
324
|
+
:Root => @root,
|
325
|
+
:Info => @info}
|
326
|
+
|
327
|
+
output << "trailer\n"
|
328
|
+
output << Prawn::PdfObject(trailer_hash) << "\n"
|
329
|
+
output << "startxref\n"
|
330
|
+
output << @xref_offset << "\n"
|
331
|
+
output << "%%EOF"
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|