0% found this document useful (0 votes)
63 views102 pages

Prawn: by Example

This manual is a collection of examples categorized by theme and organized from the least to the most complex. If you are new to Prawn the first chapter will teach you the most basic concepts and how to create pdf documents. Advanced users are encouraged to go beyond this manual and read the source code directly if any doubt is not directly covered on this manual.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
63 views102 pages

Prawn: by Example

This manual is a collection of examples categorized by theme and organized from the least to the most complex. If you are new to Prawn the first chapter will teach you the most basic concepts and how to create pdf documents. Advanced users are encouraged to go beyond this manual and read the source code directly if any doubt is not directly covered on this manual.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 102

Prawn

by example
Last Update: 2014-07-27
Prawn Version: 1.2.0
git commit: 730d866
How to read this manual
This manual is a collection of examples categorized by theme and organized from the least to the
most complex. While it covers most of the common use cases it is not a comprehensive guide.
The best way to read it depends on your previous knowledge of Prawn and what you need to
accomplish.
If you are beginning with Prawn the first chapter will teach you the most basic concepts and how to
create pdf documents. For an overview of the other features each chapter beyond the first either
has a Basics section (which offer enough insight on the feature without showing all the advanced
stuff you might never use) or is simple enough with only a few examples.
Once you understand the basics you might want to come back to this manual looking for examples
that accomplish tasks you need.
Advanced users are encouraged to go beyond this manual and read the source code directly if any
doubt is not directly covered on this manual.
Reading the examples
The title of each example is the relative path from the Prawn source manual/ folder.
The first body of text is the introductory text for the example. Generaly it is a short description of
the features illustrated by the example.
Next comes the example source code block in fixed width font.
Most of the example snippets illustrate features that alter the page in place. The effect of these
snippets is shown right below a dashed line. If it doesn't make sense to evaluate the snippet inline,
a box with the link for the example file is shown instead.
Note that the stroke_axis method, used occasionally in the manual, is not part of standard
Prawn and is used for demonstrative purposes. It is defined in this file:
https://github.com/prawnpdf/prawn/blob/master/manual/example_helper.rb
Basic concepts
This chapter covers the minimum amount of functionality you'll need to start using Prawn.
If you are new to Prawn this is the first chapter to read. Once you are comfortable with the
concepts shown here you might want to check the Basics section of the Graphics, Bounding Box
and Text sections.
The examples show:
How to create new pdf documents in every possible way
Where the origin for the document coordinates is. What are Bounding Boxes and how they
interact with the origin
How the cursor behaves
How to start new pages
What the base unit for measurement and coordinates is and how to use other convenient
measures
basic_concepts/creation.rb
There are three ways to create a PDF Document in Prawn: creating a new Prawn::Document
instance, or using the Prawn::Document.generate method with and without block arguments.
The following snippet showcase each way by creating a simple document with some text drawn.
When we instantiate the Prawn::Document object the actual pdf document will only be created
after we call render_file.
The generate method will render the actual pdf object after exiting the block. When we use it
without a block argument the provided block is evaluated in the context of a newly created
Prawn::Document instance. When we use it with a block argument a Prawn::Document
instance is created and passed to the block.
The generate method without block arguments requires less typing and defines and renders the
pdf document in one shot. Almost all of the examples are coded this way.
Assignment Implicit Block Explicit Block
#Assignment
pdf=Prawn::Document.new
pdf.text"HelloWorld"
pdf.render_file"assignment.pdf"
#ImplicitBlock
Prawn::Document.generate("implicit.pdf")do
text"HelloWorld"
end
#ExplicitBlock
Prawn::Document.generate("explicit.pdf")do|pdf|
pdf.text"HelloWorld"
end
This code snippet was not evaluated inline. You may see its output by running the example file located here:
http://github.com/prawnpdf/prawn/tree/master/manual/basic_concepts/creation.rb
basic_concepts/origin.rb
This is the most important concept you need to learn about Prawn:
PDF documents have the origin [0,0] at the bottom-left corner of the page.
A bounding box is a structure which provides boundaries for inserting content. A bounding box
also has the property of relocating the origin to its relative bottom-left corner. However, be aware
that the location specified when creating a bounding box is its top-left corner, not bottom-left
(hence the [100, 300] coordinates below).
Even if you never create a bounding box explictly, each document already comes with one called
the margin box. This initial bounding box is the one responsible for the document margins.
So practically speaking the origin of a page on a default generated document isn't the absolute
bottom left corner but the bottom left corner of the margin box.
The following snippet strokes a circle on the margin box origin. Then strokes the boundaries of a
bounding box and a circle on its origin.
stroke_axis
stroke_circle[0,0],10
bounding_box([100,300],:width=>300,:height=>200)do
stroke_bounds
stroke_circle[0,0],10
end
100 200 300 400 500
100
200
300
basic_concepts/cursor.rb
We normally write our documents from top to bottom and it is no different with Prawn. Even if the
origin is on the bottom left corner we still fill the page from the top to the bottom. In other words the
cursor for inserting content starts on the top of the page.
Most of the functions that insert content on the page will start at the current cursor position and
proceed to the bottom of the page.
The following snippet shows how the cursor behaves when we add some text to the page and
demonstrates some of the helpers to manage the cursor position. The cursor method returns the
current cursor position.
stroke_axis
text"thecursorishere:#{cursor}"
text"nowitishere:#{cursor}"
move_down200
text"onthefirstmovethecursorwentdownto:#{cursor}"
move_up100
text"onthesecondmovethecursorwentupto:#{cursor}"
move_cursor_to50
text"onthelastmovethecursorwentdirectlyto:#{cursor}"
100 200 300 400 500
100
200
300
the cursor is here: 383.9795
now it is here: 370.10749999999996
on the first move the cursor went down to: 156.23549999999994
on the second move the cursor went up to: 242.36349999999993
on the last move the cursor went directly to: 50.0
basic_concepts/other_cursor_helpers.rb
Another group of helpers for changing the cursor position are the pad methods. They accept a
numeric value and a block. pad will use the numeric value to move the cursor down both before
and after the block content. pad_top will only move the cursor before the block while
pad_bottom will only move after.
float is a method for not changing the cursor. Pass it a block and the cursor will remain on the
same place when the block returns.
stroke_horizontal_rule
pad(20){text"Textpaddedbothbeforeandafter."}
stroke_horizontal_rule
pad_top(20){text"Textpaddedonthetop."}
stroke_horizontal_rule
pad_bottom(20){text"Textpaddedonthebottom."}
stroke_horizontal_rule
move_down30
text"Textwrittenbeforethefloatblock."
floatdo
move_down30
bounding_box([0,cursor],:width=>200)do
text"Textwritteninsidethefloatblock."
stroke_bounds
end
end
text"Textwrittenafterthefloatblock."
Text padded both before and after.
Text padded on the top.
Text padded on the bottom.
Text written before the float block.
Text written inside the float block.
Text written after the float block.
basic_concepts/adding_pages.rb
A PDF document is a collection of pages. When we create a new document be it with
Document.new or on a Document.generate block one initial page is created for us.
Some methods might create new pages automatically like text which will create a new page
whenever the text string cannot fit on the current page.
But what if you want to go to the next page by yourself? That is easy.
Just use the start_new_page method and a shiny new page will be created for you just like in
the following snippet.
text"Wearestillontheinitialpageforthisexample.NowI'llask"+
"Prawntogentlystartanewpage.Pleasefollowmetothenextpage."
start_new_page
text"See.We'veleftthepreviouspagebehind."
We are still on the initial page for this example. Now I'll ask Prawn to gently start a new page. Please
follow me to the next page.
See. We've left the previous page behind.
basic_concepts/measurement.rb
The base unit in Prawn is the PDF Point. One PDF Point is equal to 1/72 of an inch.
There is no need to waste time converting this measures. Prawn provides helpers for converting
from other measurements to PDF Points.
Just require "prawn/measurement_extensions" and it will mix some helpers onto
Numeric for converting common measurement units to PDF Points.
require"prawn/measurement_extensions"
[:mm,:cm,:dm,:m,:in,:yd,:ft].eachdo|measurement|
text"1#{measurement}inPDFPoints:#{1.send(measurement)}pt"
move_down5.mm
end
1 mm in PDF Points: 2.834645669291339 pt
1 cm in PDF Points: 28.34645669291339 pt
1 dm in PDF Points: 283.46456692913387 pt
1 m in PDF Points: 2834.645669291339 pt
1 in in PDF Points: 72 pt
1 yd in PDF Points: 2592 pt
1 ft in PDF Points: 864 pt
Graphics
Here we show all the drawing methods provided by Prawn. Use them to draw the most beautiful
imaginable things.
Most of the content that you'll add to your pdf document will use the graphics package. Even text is
rendered on a page just like a rectangle so even if you never use any of the shapes described here
you should at least read the basic examples.
The examples show:
All the possible ways that you can fill or stroke shapes on a page
How to draw all the shapes that Prawn has to offer from a measly line to a mighty polygon or
ellipse
The configuration options for stroking lines and filling shapes
How to apply transformations to your drawing space
graphics/helper.rb
To produce this manual we use the stroke_axis helper method within the examples.
stroke_axis prints the x and y axis for the current bounding box with markers in 100
increments. The defaults can be changed with various options.
Note that the examples define a custom :height option so that only the example canvas is used
(as seen with the output of the first line of the example code).
stroke_axis
stroke_axis(:at=>[70,70],:height=>200,:step_length=>50,
:negative_axes_length=>5,:color=>'0000FF')
stroke_axis(:at=>[140,140],:width=>200,:height=>cursor.to_i-140,
:step_length=>20,:negative_axes_length=>40,:color=>'FF00')
100 200 300 400 500
100
200
300
400
500
50 100 150 200 250 300 350 400 450
50
100
150
200
20 40 60 80 100 120 140 160 180 200
20
40
60
80
100
120
140
160
180
200
220
240
260
280
300
320
340
360
380
graphics/fill_and_stroke.rb
There are two drawing primitives in Prawn: fill and stroke.
These are the methods that actually draw stuff on the document. All the other drawing shapes like
rectangle, circle or line_to define drawing paths. These paths need to be either stroked or
filled to gain form on the document.
Calling these methods without a block will act on the drawing path that has been defined prior to
the call.
Calling with a block will act on the drawing path set within the block.
Most of the methods which define drawing paths have methods of the same name starting with
stroke_ and fill_ which create the drawing path and then stroke or fill it.
stroke_axis
#Noblock
line[0,200],[100,150]
stroke
rectangle[0,100],100,100
fill
#Withblock
stroke{line[200,200],[300,150]}
fill{rectangle[200,100],100,100}
#Methodhook
stroke_line[400,200],[500,150]
fill_rectangle[400,100],100,100
100 200 300 400 500
100
200
graphics/lines_and_curves.rb
Prawn supports drawing both lines and curves starting either at the current position, or from a
specified starting position.
line_to and curve_to set the drawing path from the current drawing position to the specified
point. The initial drawing position can be set with move_to. They are useful when you want to
chain successive calls because the drawing position will be set to the specified point afterwards.
line and curve set the drawing path between the two specified points.
Both curve methods define a Bezier curve bounded by two aditional points provided as the
:bounds param.
stroke_axis
#line_toandcurve_to
strokedo
move_to0,0
line_to100,100
line_to0,100
curve_to[150,250],:bounds=>[[20,200],[120,200]]
curve_to[200,0],:bounds=>[[150,200],[450,10]]
end
#lineandcurve
strokedo
line[300,200],[400,50]
curve[500,0],[400,200],:bounds=>[[600,300],[300,390]]
end
100 200 300 400 500
100
200
graphics/common_lines.rb
Prawn provides helpers for drawing some commonly used lines:
vertical_line and horizontal_line do just what their names imply. Specify the start and
end point at a fixed coordinate to define the line.
horizontal_rule draws a horizontal line on the current bounding box from border to border,
using the current y position.
stroke_axis
stroke_color"ff0000"
strokedo
#justlowerthecurrentyposition
move_down50
horizontal_rule
vertical_line100,300,:at=>50
horizontal_line200,500,:at=>150
end
100 200 300 400 500
100
200
300
400
graphics/rectangle.rb
To draw a rectangle, just provide the upper-left corner, width and height to the rectangle
method.
There's also rounded_rectangle. Just provide an additional radius value for the rounded
corners.
stroke_axis
strokedo
rectangle[100,300],100,200
rounded_rectangle[300,300],100,200,20
end
100 200 300 400 500
100
200
300
400
500
graphics/polygon.rb
Drawing polygons in Prawn is easy, just pass a sequence of points to one of the polygon family of
methods.
Just like rounded_rectangle we also have rounded_polygon. The only difference is the
radius param comes before the polygon points.
stroke_axis
#Triangle
stroke_polygon[50,200],[50,300],[150,300]
#Hexagon
fill_polygon[50,150],[150,200],[250,150],
[250,50],[150,0],[50,50]
#Pentagram
pentagon_points=[500,100],[430,5],[319,41],[319,159],[430,195]
pentagram_points=[0,2,4,1,3].map{|i|pentagon_points[i]}
stroke_rounded_polygon(20,*pentagram_points)
100 200 300 400 500
100
200
300
400
graphics/circle_and_ellipse.rb
To define a circle all you need is the center point and the radius.
To define an ellipse you provide the center point and two radii (or axes) values. If the second
radius value is ommitted, both radii will be equal and you will end up drawing a circle.
stroke_axis
stroke_circle[100,300],100
fill_ellipse[200,100],100,50
fill_ellipse[400,100],50
100 200 300 400 500
100
200
300
400
500
graphics/line_width.rb
The line_width= method sets the stroke width for subsequent stroke calls.
Since Ruby assumes that an unknown variable on the left hand side of an assignment is a local
temporary, rather than a setter method, if you are using the block call to
Prawn::Document.generate without passing params you will need to call line_width on
self.
stroke_axis
y=250
3.timesdo|i|
casei
when0thenline_width=10#Thiscallwillhavenoeffect
when1thenself.line_width=10
when2thenself.line_width=25
end
strokedo
horizontal_line50,150,:at=>y
rectangle[275,y+25],50,50
circle[500,y],25
end
y-=100
end
100 200 300 400 500
100
200
300
graphics/stroke_cap.rb
The cap style defines how the edge of a line or curve will be drawn. There are three types: :butt
(the default), :round and :projecting_square
The difference is better seen with thicker lines. With :butt lines are drawn starting and ending at
the exact points provided. With both :round and :projecting_square the line is projected
beyond the start and end points.
Just like line_width= the cap_style= method needs an explicit receiver to work.
stroke_axis
self.line_width=25
[:butt,:round,:projecting_square].each_with_indexdo|cap,i|
self.cap_style=cap
y=250-i*100
stroke_horizontal_line100,300,:at=>y
stroke_circle[400,y],15
end
100 200 300 400 500
100
200
300
400
graphics/stroke_join.rb
The join style defines how the intersection between two lines is drawn. There are three types:
:miter (the default), :round and :bevel
Just like cap_style, the difference between styles is better seen with thicker lines.
stroke_axis
self.line_width=25
[:miter,:round,:bevel].each_with_indexdo|style,i|
self.join_style=style
y=200-i*100
strokedo
move_to(100,y)
line_to(200,y+100)
line_to(300,y)
end
stroke_rectangle[400,y+75],50,50
end
100 200 300 400 500
100
200
300
400
graphics/stroke_dash.rb
This sets the dashed pattern for lines and curves. The (dash) length defines how long each dash
will be.
The :space option defines the length of the space between the dashes.
The :phase option defines the start point of the sequence of dashes and spaces.
Complex dash patterns can be specified by using an array with alternating dash/gap lengths for
the first parameter (note that the :space option is ignored in this case).
stroke_axis
dash([1,2,3,2,1,5],:phase=>6)
stroke_horizontal_line50,500,:at=>230
dash([1,2,3,4,5,6,7,8])
stroke_horizontal_line50,500,:at=>220
base_y=210
24.timesdo|i|
length=(i/4)+1
space=length#spacebetweendashessamelengthasdash
phase=0#startwithdash
casei%4
when0thenbase_y-=5
when1thenphase=length#startwithspacebetweendashes
when2thenspace=length*0.5#spacebetweendasheshalfaslongasdash
when3
space=length*0.5#spacebetweendasheshalfaslongasdash
phase=length#startwithspacebetweendashes
end
base_y-=5
dash(length,:space=>space,:phase=>phase)
stroke_horizontal_line50,500,:at=>base_y-(2*i)
end
100 200 300 400 500
100
200
graphics/color.rb
We can change the stroke and fill colors providing an HTML rgb 6 digit color code string
("AB1234") or 4 values for CMYK.
stroke_axis
#FillwithYellowusingRGB
fill_color"FFFFCC"
fill_polygon[50,150],[150,200],[250,150],
[250,50],[150,0],[50,50]
#StrokewithPurpleusingCMYK
stroke_color50,100,0,0
stroke_rectangle[300,300],200,100
#Bothtogether
fill_and_stroke_circle[400,100],50
100 200 300 400 500
100
200
300
400
graphics/gradients.rb
Note that because of the way PDF renders radial gradients in order to get solid fill your start circle
must be fully inside your end circle. Otherwise you will get triangle fill like illustrated in the example
below.
stroke_axis
self.line_width=10
fill_gradient[50,300],[150,200],'ff0000','0000ff'
fill_rectangle[50,300],100,100
stroke_gradient[200,200],[300,300],'00ffff','ffff00'
stroke_rectangle[200,300],100,100
fill_gradient[350,300],[450,200],'ff0000','0000ff'
stroke_gradient[350,200],[450,300],'00ffff','ffff00'
fill_and_stroke_rectangle[350,300],100,100
fill_gradient[100,100],0,[100,100],70.71,'ff0000','0000ff'
fill_rectangle[50,150],100,100
stroke_gradient[250,100],45,[250,100],70.71,'00ffff','ffff00'
stroke_rectangle[200,150],100,100
stroke_gradient[400,100],45,[400,100],70.71,'00ffff','ffff00'
fill_gradient[400,100],0,[400,100],70.71,'ff0000','0000ff'
fill_and_stroke_rectangle[350,150],100,100
fill_gradient[500,300],15,[500,50],0,'ff0000','0000ff'
fill_rectangle[485,300],30,250
100 200 300 400 500
100
200
300
graphics/transparency.rb
Although the name of the method is transparency, what we are actually setting is the opacity for
fill and stroke. So 0 means completely transparent and 1.0 means completely opaque
You may call it providing one or two values. The first value sets fill opacity and the second value
sets stroke opacity. If the second value is omitted fill and stroke will have the same opacity.
stroke_axis
self.line_width=5
fill_color"ff0000"
fill_rectangle[0,100],500,100
fill_color"000000"
stroke_color"ffffff"
base_x=100
[[0.5,1],0.5,[1,0.5]].eachdo|args|
transparent(*args)do
fill_circle[base_x,100],50
stroke_rectangle[base_x-20,100],40,80
end
base_x+=150
end
100 200 300 400 500
100
200
300
graphics/soft_masks.rb
Soft masks are user for more complex alpha channel manipulations. You can use arbitrary drawing
functions for creation of soft masks. The resulting alpha channel is made of greyscale version of
the drawing (luminosity channel to be precise). So while you can use any combination of colors for
soft masks it's easier to use greyscales. Black will result in full transparency and white will make
region fully opaque.
Soft mask is a part of page graphic state. So if you want to apply soft mask only to a part of page
you need to enclose drawing instructions in save_graphics_state block.
save_graphics_statedo
soft_maskdo
0.upto15do|i|
fill_color0,0,0,100.0/16.0*(15-i)
fill_circle[75+i*25,100],60
end
end
fill_color'009ddc'
fill_rectangle[0,60],600,20
fill_color'963d97'
fill_rectangle[0,80],600,20
fill_color'e03a3e'
fill_rectangle[0,100],600,20
fill_color'f5821f'
fill_rectangle[0,120],600,20
fill_color'fdb827'
fill_rectangle[0,140],600,20
fill_color'61bb46'
fill_rectangle[0,160],600,20
end
graphics/fill_rules.rb
Prawn's fill operators (fill and fill_and_stroke both accept a :fill_rule option. These
rules determine which parts of the page are counted as "inside" vs. "outside" the path. There are
two fill rules:
* :nonzero_winding_number (default): a point is inside the path if a ray from that point to
infinity crosses a nonzero "net number" of path segments, where path segments intersecting in
one direction are counted as positive and those in the other direction negative.
* :even_odd: A point is inside the path if a ray from that point to infinity crosses an odd number of
path segments, regardless of direction.
The differences between the fill rules only come into play with complex paths; they are identical for
simple shapes.
pentagram=[[181,95],[0,36],[111,190],[111,0],[0,154]]
stroke_color'ff0000'
line_width2
text_box"NonzeroWindingNumber",:at=>[50,215],:width=>170,
:align=>:center
polygon(*pentagram.map{|x,y|[x+50,y]})
fill_and_stroke
text_box"Even-Odd",:at=>[330,215],:width=>170,:align=>:center
polygon(*pentagram.map{|x,y|[x+330,y]})
fill_and_stroke(:fill_rule=>:even_odd)
Nonzero Winding Number Even-Odd
graphics/rotate.rb
This transformation is used to rotate the user space. Give it an angle and an :origin point about
which to rotate and a block. Everything inside the block will be drawn with the rotated coordinates.
The angle is in degrees.
If you omit the :origin option the page origin will be used.
stroke_axis
fill_circle[250,200],2
12.timesdo|i|
rotate(i*30,:origin=>[250,200])do
stroke_rectangle[350,225],100,50
draw_text"Rotated#{i*30}",:size=>10,:at=>[360,205]
end
end
100 200 300 400 500
100
200
300
400
Rotated 0
R
o
t
a
t
e
d

3
0

R
o
t
a
t
e
d

6
0

R
o
t
a
t
e
d

9
0

R
o
t
a
t
e
d

1
2
0

R
o
t
a
t
e
d

1
5
0

R o t a t e d 1 8 0
R
o
t
a
t
e
d

2
1
0

R
o
t
a
t
e
d

2
4
0

R
o
t
a
t
e
d

2
7
0

R
o
t
a
t
e
d

3
0
0

R
o
t
a
t
e
d

3
3
0

graphics/translate.rb
This transformation is used to translate the user space. Just provide the x and y coordinates for
the new origin.
stroke_axis
1.upto(3)do|i|
x=i*50
y=i*100
translate(x,y)do
#Drawapointontheneworigin
fill_circle[0,0],2
draw_text"Neworiginaftertranslationto[#{x},#{y}]",
:at=>[5,-2],:size=>8
stroke_rectangle[100,75],100,50
text_box"Topleftcornerat[100,75]",
:at=>[110,65],:width=>80,:size=>8
end
end
100 200 300 400 500
100
200
300
400
New origin after translation to [50, 100]
Top left corner at
[100,75]
New origin after translation to [100, 200]
Top left corner at
[100,75]
New origin after translation to [150, 300]
Top left corner at
[100,75]
graphics/scale.rb
This transformation is used to scale the user space. Give it an scale factor and an :origin point
and everything inside the block will be scaled using the origin point as reference.
If you omit the :origin option the page origin will be used.
stroke_axis
width=100
height=50
x=50
y=200
stroke_rectangle[x,y],width,height
text_box"referencerectangle",:at=>[x+10,y-10],:width=>width-20
scale(2,:origin=>[x,y])do
stroke_rectangle[x,y],width,height
text_box"rectanglescaledfromupper-leftcorner",
:at=>[x,y-height-5],:width=>width
end
x=350
stroke_rectangle[x,y],width,height
text_box"referencerectangle",:at=>[x+10,y-10],:width=>width-20
scale(2,:origin=>[x+width/2,y-height/2])do
stroke_rectangle[x,y],width,height
text_box"rectanglescaledfromcenter",
:at=>[x,y-height-5],:width=>width
end
100 200 300 400 500
100
200
reference
rectangle
rectangle scaled
from upper-left
corner
reference
rectangle
rectangle scaled
from center
Text
This is probably the feature people will use the most. There is no shortage of options when it
comes to text. You'll be hard pressed to find a use case that is not covered by one of the text
methods and configurable options.
The examples show:
Text that flows from page to page automatically starting new pages when necessary
How to use text boxes and place them on specific positions
What to do when a text box is too small to fit its content
Flowing text in columns
How to change the text style configuring font, size, alignment and many other settings
How to style specific portions of a text with inline styling and formatted text
How to define formatted callbacks to reuse common styling definitions
How to use the different rendering modes available for the text methods
How to create your custom text box extensions
How to use external fonts on your pdfs
What happens when rendering text in different languages
text/free_flowing_text.rb
Text rendering can be as simple or as complex as you want.
This example covers the most basic method: text. It is meant for free flowing text. The provided
string will flow according to the current bounding box width and height. It will also flow onto the
next page if the bottom of the bounding box is reached.
The text will start being rendered on the current cursor position. When it finishes rendering, the
cursor is left directly below the text.
This example also shows text flowing across pages following the margin box and other bounding
boxes.
move_cursor_to50
text"Thistextwillflowtothenextpage."*20
y_position=cursor-50
bounding_box([0,y_position],:width=>200,:height=>150)do
transparent(0.5){stroke_bounds}
text"Thistextwillflowalongthisboundingboxwecreatedforit."*5
end
bounding_box([300,y_position],:width=>200,:height=>150)do
transparent(0.5){stroke_bounds}#Thiswillstrokeononepage
text"Nowlookwhathappenswhenthefreeflowingtextreachestheend"+
"ofaboundingboxthatisnarrowerthanthemarginbox."+
"."*200+
"Itcontinuesonthenextpageasifthepreviousboundingbox"+
"wascloned.Ifwewantittohavethesameborderastheoneon"+
"thepreviouspagewewillneedtostroketheboundariesagain."
transparent(0.5){stroke_bounds}#Andthiswillstrokeonthenext
end
move_cursor_to200
span(350,:position=>:center)do
text"Spanisadifferentkindofboundingboxasitletsthetext"+
"flowgracefullyontothenextpage.Itdoesn'tmatterifthetext"+
"startedonthemiddleofthepreviouspage,whenitflowstothe"+
"nextpageitwillstartatthebeginning."+"_"*500+
"Itoldyouitwouldstartonthebeginningofthispage."
end
This text will flow to the next page. This text will flow to the next page. This text will flow to the next
page. This text will flow to the next page. This text will flow to the next page. This text will flow to the
next page. This text will flow to the next page. This text will flow to the next page. This text will flow to
the next page. This text will flow to the next page. This text will flow to the next page. This text will flow
to the next page. This text will flow to the next page. This text will flow to the next page. This text will
flow to the next page. This text will flow to the next page. This text will flow to the next page. This text
will flow to the next page. This text will flow to the next page. This text will flow to the next page.
This text will flow along this bounding
box we created for it. This text will
flow along this bounding box we
created for it. This text will flow along
this bounding box we created for it.
This text will flow along this bounding
box we created for it. This text will
flow along this bounding box we
created for it.
Now look what happens when the
free flowing text reaches the end of a
bounding box that is narrower than
the margin box. . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . It continues on the
next page as if the previous bounding
box was cloned. If we want it to have
the same border as the one on the
previous page we will need to stroke
the boundaries again.
Span is a different kind of bounding box as it lets the text flow
gracefully onto the next page. It doesn't matter if the text started
on the middle of the previous page, when it flows to the next page
it will start at the beginning. _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ I told you it would start
on the beginning of this page.
text/positioned_text.rb
Sometimes we want the text on a specific position on the page. The text method just won't help
us.
There are two other methods for this task: draw_text and text_box.
draw_text is very simple. It will render text starting at the position provided to the :at option. It
won't flow to a new line even if it hits the document boundaries so it is best suited for short text.
text_box gives us much more control over the output. Just provide :width and :height
options and the text will flow accordingly. Even if you don't provide a :width option the text will
flow to a new line if it reaches the right border.
Given that said, text_box is the better option available.
draw_text"Thisdraw_textlineisabsolutepositioned.Howeverdon't"+
"expectittoflowevenifithitsthedocumentborder",
:at=>[200,300]
text_box"Thisisatextbox,youcancontrolwhereitwillflowby"+
"specifyingthe:heightand:widthoptions",
:at=>[100,250],
:height=>100,
:width=>100
text_box"Anothertextboxwithno:widthoptionpassed,soitwill"+
"flowtoanewlinewheneveritreachestherightmargin.",
:at=>[200,100]
This draw_text line is absolute positioned. However don't expect it to flow even if it hits the document border
This is a text box,
you can control
where it will flow
by specifying the
:height and :width
options
Another text box with no :width option passed, so it will flow to a
new line whenever it reaches the right margin.
text/text_box_overflow.rb
The text_box method accepts both :width and :height options. So what happens if the text
doesn't fit the box?
The default behavior is to truncate the text but this can be changed with the :overflow option.
Available modes are :expand (the box will increase to fit the text) and :shrink_to_fit (the text
font size will be shrunk to fit).
If :shrink_to_fit mode is used with the :min_font_size option set. The font size will not be
reduced to less than the value provided even if it means truncating some text.
If the :disable_wrap_by_char is set to true then any text wrapping done while using the
:shrink_to_fit mode will not break up the middle of words.
string="Thisisthesampletextusedforthetextboxes.Seehowit"+
"behavewiththevariousoverflowoptionsused."
textstring
y_position=cursor-20
[:truncate,:expand,:shrink_to_fit].each_with_indexdo|mode,i|
text_boxstring,:at=>[i*150,y_position],
:width=>100,:height=>50,
:overflow=>mode
end
string="Iftheboxistoosmallforthetext,:shrink_to_fit"+
"canrenderthetextinareallysmallfontsize."
move_down120
textstring
y_position=cursor-20
[nil,8,10,12].each_with_indexdo|value,index|
text_boxstring,:at=>[index*150,y_position],
:width=>50,:height=>50,
:overflow=>:shrink_to_fit,
:min_font_size=>value
end
This is the sample text used for the text boxes. See how it behave with the various overflow options
used.
This is the sample
text used for the
text boxes. See
This is the sample
text used for the
text boxes. See
how it behave with
the various
overflow options
used.
This is the sample text
used for the text boxes.
See how it behave with
the various overflow
options used.
If the box is too small for the text, :shrink_to_fit can render the text in a really small font size.
If the box is too
small for the text,
:shrink_to_fit can
render the text in
a really small
font size.
If the box is
too small for
the text,
:shrink_to_fit
can render
If the box
is too
small for
the text,
If the box
is too
small for
text/text_box_excess.rb
Whenever the text_box method truncates text, this truncated bit is not lost, it is the method
return value and we can take advantage of that.
We just need to take some precautions.
This example renders as much of the text as will fit in a larger font inside one text_box and then
proceeds to render the remaining text in the default size in a second text_box.
string="Thisisthebeginningofthetext.Itwillbecutsomewhereand"+
"therestofthetextwillprocedetoberenderedthistimeby"+
"callinganothermethod."+"."*50
y_position=cursor-20
excess_text=text_boxstring,
:width=>300,
:height=>50,
:overflow=>:truncate,
:at=>[100,y_position],
:size=>18
text_boxexcess_text,
:width=>300,
:at=>[100,y_position-100]
This is the beginning of the text. It will
be cut somewhere and the rest of the
text will procede to be rendered this time by calling
another method. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
text/column_box.rb
The column_box method allows you to define columns that flow their contents from one section
to the next. You can have a number of columns on the page, and only when the last column
overflows will a new page be created.
text"ThePrince",:align=>:center,:size=>18
text"NiccolMachiavelli",:align=>:center,:size=>14
move_down12
column_box([0,cursor],:columns=>2,:width=>bounds.width)do
text((<<-END.gsub(/\s+/,'')+"\n\n")*3)
AlltheStatesandGovernmentsbywhichmenareoreverhavebeenruled,
havebeenandareeitherRepublicsorPrincedoms.Princedomsareeither
hereditary,inwhichthesovereigntyisderivedthroughanancientline
ofancestors,ortheyarenew.NewPrincedomsareeitherwhollynew,as
thatofMilantoFrancescoSforza;ortheyarelikelimbsjoinedonto
thehereditarypossessionsofthePrincewhoacquiresthem,asthe
KingdomofNaplestothedominionsoftheKingofSpain.TheStatesthus
acquiredhaveeitherbeenusedtoliveunderaPrinceorhavebeenfree;
andhewhoacquiresthemdoessoeitherbyhisownarmsorbythearmsof
others,andeitherbygoodfortuneorbymerit.
END
end
The Prince
Niccol Machiavelli
All the States and Governments by which men
are or ever have been ruled, have been and are
either Republics or Princedoms. Princedoms are
either hereditary, in which the sovereignty is
derived through an ancient line of ancestors, or
they are new. New Princedoms are either wholly
new, as that of Milan to Francesco Sforza; or they
are like limbs joined on to the hereditary
possessions of the Prince who acquires them, as
the Kingdom of Naples to the dominions of the
King of Spain. The States thus acquired have
either been used to live under a Prince or have
been free; and he who acquires them does so
either by his own arms or by the arms of others,
and either by good fortune or by merit.
All the States and Governments by which men
are or ever have been ruled, have been and are
either Republics or Princedoms. Princedoms are
either hereditary, in which the sovereignty is
derived through an ancient line of ancestors, or
they are new. New Princedoms are either wholly
new, as that of Milan to Francesco Sforza; or they
are like limbs joined on to the hereditary
possessions of the Prince who acquires them, as
the Kingdom of Naples to the dominions of the
King of Spain. The States thus acquired have
either been used to live under a Prince or have
been free; and he who acquires them does so
either by his own arms or by the arms of others,
and either by good fortune or by merit.
All the States and Governments by which men
are or ever have been ruled, have been and are
either Republics or Princedoms. Princedoms are
either hereditary, in which the sovereignty is
derived through an ancient line of ancestors, or
they are new. New Princedoms are either wholly
new, as that of Milan to Francesco Sforza; or they
are like limbs joined on to the hereditary
possessions of the Prince who acquires them, as
the Kingdom of Naples to the dominions of the
King of Spain. The States thus acquired have
either been used to live under a Prince or have
been free; and he who acquires them does so
either by his own arms or by the arms of others,
and either by good fortune or by merit.
text/font.rb
The font method can be used in three different ways.
If we don't pass it any arguments it will return the current font being used to render text.
If we just pass it a font name it will use that font for rendering text through the rest of the document.
It can also be used by passing a font name and a block. In this case the specified font will only be
used to render text inside the block.
The default font is Helvetica.
text"Let'sseewhichfontweareusing:#{font.inspect}"
move_down20
font"Times-Roman"
text"WritteninTimes."
move_down20
font("Courier")do
text"WritteninCourierbecauseweareinsidetheblock."
end
move_down20
text"WritteninTimesagainasweleftthepreviousblock."
move_down20
text"Let'sseewhichfontweareusingagain:#{font.inspect}"
move_down20
font"Helvetica"
text"Backtonormal."
Let's see which font we are using: Prawn::Font::AFM< Helvetica: 12 >
Written in Times.
Written in Courier because we are inside the block.
Written in Times again as we left the previous block.
Let's see which font we are using again: Prawn::Font::AFM< Times-Roman: 12 >
Back to normal.
text/font_size.rb
The font_size method works just like the font method.
In fact we can even use font with the :size option to declare which size we want.
Another way to change the font size is by supplying the :size option to the text methods.
The default font size is 12.
text"Let'sseewhichisthecurrentfont_size:#{font_size.inspect}"
move_down10
font_size16
text"Yeah,somethingbigger!"
move_down10
font_size(25){text"Evenbigger!"}
move_down10
text"Backto16again."
move_down10
text"Singlelineon20usingthe:sizeoption.",:size=>20
move_down10
text"Backto16oncemore."
move_down10
font("Courier",:size=>10)do
text"Yeah,usingCourier10courtesyofthefontmethod."
end
move_down10
font("Helvetica",:size=>12)
text"Backtonormal"
Let's see which is the current font_size: 12
Yeah, something bigger!
Even bigger!
Back to 16 again.
Single line on 20 using the :size option.
Back to 16 once more.
Yeah, using Courier 10 courtesy of the font method.
Back to normal
text/font_style.rb
Most font families come with some styles other than normal. Most common are bold, italic and
bold_italic.
The style can be set the using the :style option, with either the font method which will set the
font and style for rest of the document, or with the inline text methods.
["Courier","Helvetica","Times-Roman"].eachdo|example_font|
move_down20
[:bold,:bold_italic,:italic,:normal].eachdo|style|
fontexample_font,:style=>style
text"I'mwritingin#{example_font}(#{style})"
end
end
I'm writing in Courier (bold)
I'm writing in Courier (bold_italic)
I'm writing in Courier (italic)
I'm writing in Courier (normal)
I'm writing in Helvetica (bold)
I'm writing in Helvetica (bold_italic)
I'm writing in Helvetica (italic)
I'm writing in Helvetica (normal)
I'm writing in Times-Roman (bold)
I'm writing in Times-Roman (bold_italic)
I'm writing in Times-Roman (italic)
I'm writing in Times-Roman (normal)
text/color.rb
The :color attribute can give a block of text a default color, in RGB hex format or 4-value CMYK.
text"Defaultcolorisblack"
move_down25
text"Changedtored",:color=>"FF0000"
move_down25
text"CMYKcolor",:color=>[22,55,79,30]
move_down25
text"Alsoworkswith<colorrgb='ff0000'>inline</color>formatting",
:color=>"0000FF",
:inline_format=>true
Default color is black
Changed to red
CMYK color
Also works with inline formatting
text/alignment.rb
Horizontal text alignment can be achieved by supplying the :align option to the text methods.
Available options are :left (default), :right, :center, and :justify.
Vertical text alignment can be achieved using the :valign option with the text methods. Available
options are :top (default), :center, and :bottom.
Both forms of alignment will be evaluated in the context of the current bounding_box.
text"Thistextshouldbeleftaligned"
text"Thistextshouldbecentered",:align=>:center
text"Thistextshouldberightaligned",:align=>:right
bounding_box([0,220],:width=>250,:height=>220)do
text"Thistextisflowingfromtheleft."*4
move_down15
text"Thistextisflowingfromthecenter."*3,:align=>:center
move_down15
text"Thistextisflowingfromtheright."*4,:align=>:right
move_down15
text"Thistextisjustified."*6,:align=>:justify
transparent(0.5){stroke_bounds}
end
bounding_box([300,220],:width=>250,:height=>220)do
text"Thistextshouldbeverticallytopaligned"
text"Thistextshouldbeverticallycentered",:valign=>:center
text"Thistextshouldbeverticallybottomaligned",:valign=>:bottom
transparent(0.5){stroke_bounds}
end
This text should be left aligned
This text should be centered
This text should be right aligned
This text is flowing from the left. This text is
flowing from the left. This text is flowing from
the left. This text is flowing from the left.
This text is flowing from the center. This text is
flowing from the center. This text is flowing
from the center.
This text is flowing from the right. This text is
flowing from the right. This text is flowing from
the right. This text is flowing from the right.
This text is justified. This text is justified. This
text is justified. This text is justified. This text is
justified. This text is justified.
This text should be vertically top aligned
This text should be vertically centered
This text should be vertically bottom aligned
text/leading.rb
Leading is the additional space between lines of text.
The leading can be set using the default_leading method which applies to the rest of the
document or until it is changed, or inline in the text methods with the :leading option.
The default leading is 0.
string="Hey,whatdidyoudowiththespacebetweenmylines?"*10
textstring,:leading=>0
move_down20
default_leading5
textstring
move_down20
textstring,:leading=>10
Hey, what did you do with the space between my lines? Hey, what did you do with the space between
my lines? Hey, what did you do with the space between my lines? Hey, what did you do with the
space between my lines? Hey, what did you do with the space between my lines? Hey, what did you
do with the space between my lines? Hey, what did you do with the space between my lines? Hey,
what did you do with the space between my lines? Hey, what did you do with the space between my
lines? Hey, what did you do with the space between my lines?
Hey, what did you do with the space between my lines? Hey, what did you do with the space between
my lines? Hey, what did you do with the space between my lines? Hey, what did you do with the
space between my lines? Hey, what did you do with the space between my lines? Hey, what did you
do with the space between my lines? Hey, what did you do with the space between my lines? Hey,
what did you do with the space between my lines? Hey, what did you do with the space between my
lines? Hey, what did you do with the space between my lines?
Hey, what did you do with the space between my lines? Hey, what did you do with the space between
my lines? Hey, what did you do with the space between my lines? Hey, what did you do with the
space between my lines? Hey, what did you do with the space between my lines? Hey, what did you
do with the space between my lines? Hey, what did you do with the space between my lines? Hey,
what did you do with the space between my lines? Hey, what did you do with the space between my
lines? Hey, what did you do with the space between my lines?
text/kerning_and_character_spacing.rb
Kerning is the process of adjusting the spacing between characters in a proportional font. It is
usually done with specific letter pairs. We can switch it on and off if it is available with the current
font. Just pass a boolean value to the :kerning option of the text methods.
Character Spacing is the space between characters. It can be increased or decreased and will
have effect on the whole text. Just pass a number to the :character_spacing option from the
text methods.
font_size(30)do
text_box"Withkerning:",:kerning=>true,:at=>[0,y-40]
text_box"Withoutkerning:",:kerning=>false,:at=>[0,y-80]
text_box"Tomato",:kerning=>true,:at=>[250,y-40]
text_box"Tomato",:kerning=>false,:at=>[250,y-80]
text_box"WAR",:kerning=>true,:at=>[400,y-40]
text_box"WAR",:kerning=>false,:at=>[400,y-80]
text_box"F.",:kerning=>true,:at=>[500,y-40]
text_box"F.",:kerning=>false,:at=>[500,y-80]
end
move_down80
string="Whathaveyoudonetothespacebetweenthecharacters?"
[-2,-1,0,0.5,1,2].eachdo|spacing|
move_down20
text"#{string}(character_spacing:#{spacing})",
:character_spacing=>spacing
end
With kerning:
Without kerning:
Tomato
Tomato
WAR
WAR
F.
F.
What have you done to the space between the characters? (character_spacing: -2)
What have you done to the space between the characters? (character_spacing: -1)
What have you done to the space between the characters? (character_spacing: 0)
What have you done to the space between the characters? (character_spacing: 0.5)
What have you done to the space between the characters? (character_spaci ng: 1)
What have you done t o t he s pac e bet ween t he c har ac t er s ?
( c har ac t er _s pac i ng: 2)
text/paragraph_indentation.rb
Prawn strips all whitespace from the beginning and the end of strings so there are two ways to
indent paragraphs:
One is to use non-breaking spaces which Prawn won't strip. One shortcut to using them is the
Prawn::Text::NBSP.
The other is to use the :indent_paragraphs option with the text methods. Just pass a number
with the space to indent the first line in each paragraph.
#Usingnon-breakingspaces
text""*10+"Thisparagraphwon'tbeindented."*10+
"\n#{Prawn::Text::NBSP*10}"+"ThisonewillwithNBSP."*10
move_down20
text"Thisparagraphwillbeindented."*10+
"\n"+"Thisonewilltoo."*10,
:indent_paragraphs=>60
This paragraph won't be indented. This paragraph won't be indented. This paragraph won't be
indented. This paragraph won't be indented. This paragraph won't be indented. This paragraph won't
be indented. This paragraph won't be indented. This paragraph won't be indented. This paragraph
won't be indented. This paragraph won't be indented.
This one will with NBSP. This one will with NBSP. This one will with NBSP. This one will with
NBSP. This one will with NBSP. This one will with NBSP. This one will with NBSP. This one will with
NBSP. This one will with NBSP. This one will with NBSP.
This paragraph will be indented. This paragraph will be indented. This paragraph will be
indented. This paragraph will be indented. This paragraph will be indented. This paragraph will be
indented. This paragraph will be indented. This paragraph will be indented. This paragraph will be
indented. This paragraph will be indented.
This one will too. This one will too. This one will too. This one will too. This one will too.
This one will too. This one will too. This one will too. This one will too. This one will too.
text/rotation.rb
Rotating text is best avoided on free flowing text, so this example will only use the text_box
method as we can have much more control over its output.
To rotate text all we need to do is use the :rotate option passing an angle in degrees and an
optional :rotate_around to indicate the origin of the rotation (the default is :upper_left).
width=100
height=60
angle=30
x=200
y=cursor-30
stroke_rectangle[0,y],width,height
text_box("Thistextwasnotrotated",
:at=>[0,y],:width=>width,:height=>height)
stroke_rectangle[0,y-100],width,height
text_box("Thistextwasrotatedaroundthecenter",
:at=>[0,y-100],:width=>width,:height=>height,
:rotate=>angle,:rotate_around=>:center)
[:lower_left,:upper_left,
:lower_right,:upper_right].each_with_indexdo|corner,index|
y=y-100ifindex==2
stroke_rectangle[x+(index%2)*200,y],width,height
text_box("Thistextwasrotatedaroundthe#{corner}corner.",
:at=>[x+(index%2)*200,y],
:width=>width,
:height=>height,
:rotate=>angle,
:rotate_around=>corner)
end
This text was not
rotated
T
h
i
s

t
e
x
t

w
a
s
r
o
t
a
t
e
d

a
r
o
u
n
d

t
h
e
c
e
n
t
e
r
T
h
i
s

t
e
x
t

w
a
s
r
o
t
a
t
e
d

a
r
o
u
n
d

t
h
e
l
o
w
e
r
_
l
e
f
t

c
o
r
n
e
r
.
T
h
i
s

t
e
x
t

w
a
s
r
o
t
a
t
e
d

a
r
o
u
n
d

t
h
e
u
p
p
e
r
_
l
e
f
t

c
o
r
n
e
r
.
T
h
i
s

t
e
x
t

w
a
s
r
o
t
a
t
e
d

a
r
o
u
n
d

t
h
e
l
o
w
e
r
_
r
i
g
h
t

c
o
r
n
e
r
.
T
h
i
s

t
e
x
t

w
a
s
r
o
t
a
t
e
d

a
r
o
u
n
d

t
h
e
u
p
p
e
r
_
r
i
g
h
t
c
o
r
n
e
r
.
text/inline.rb
Inline formatting gives you the option to format specific portions of a text. It uses HTML-esque
syntax inside the text string. Supported tags are: b (bold), i (italic), u (underline),
strikethrough, sub (subscript), sup (superscript)
The following tags accept specific attributes: font accepts size, name, and
character_spacing; color accepts rgb and cmyk; link accepts href for external links.
%w[biustrikethroughsubsup].eachdo|tag|
text"Justyourregulartext<#{tag}>exceptthisportion</#{tag}>"+
"isusingthe#{tag}tag",
:inline_format=>true
move_down10
end
text"This<fontsize='18'>line</font>uses"+
"<fontname='Courier'>allthefonttag</font>attributesin"+
"<fontcharacter_spacing='2'>asingleline</font>.",
:inline_format=>true
move_down10
text"Coloringin<colorrgb='FF00FF'>bothRGB</color>"+
"<colorc='100'm='0'y='0'k='0'>andCMYK</color>",
:inline_format=>true
move_down10
text"Thisanexternallinktothe"+
"<u><linkhref='https://github.com/prawnpdf/prawn/wiki'>Prawnwiki"+
"</link></u>",
:inline_format=>true
Just your regular text except this portion is using the b tag
Just your regular text except this portion is using the i tag
Just your regular text except this portion is using the u tag
Just your regular text except this portion is using the strikethrough tag
Just your regular text
except this portion
is using the sub tag
Just your regular text
except this portion
is using the sup tag
This line uses all the font tag attributes in a s i ngl e l i ne.
Coloring in both RGB and CMYK
This an external link to the Prawn wiki
text/formatted_text.rb
There are two other text methods available: formatted_text and formatted_text_box.
These are useful when the provided text has numerous portions that need to be formatted
differently. As you might imply from their names the first should be used for free flowing text just
like the text method and the last should be used for positioned text just like text_box.
The main difference between these methods and the text and text_box methods is how the
text is provided. The formatted_text and formatted_text_box methods accept an array of
hashes. Each hash must provide a :text option which is the text string and may provide the
following options: :styles (an array of symbols), :size (the font size), :character_spacing
(additional space between the characters), :font (the name of a registered font), :color (the
same input accepted by fill_color and stroke_color), :link (an URL to create a link), and
:local (a link to a local file).
formatted_text[{:text=>"Somebold.",:styles=>[:bold]},
{:text=>"Someitalic.",:styles=>[:italic]},
{:text=>"Bolditalic.",:styles=>[:bold,:italic]},
{:text=>"BiggerText.",:size=>20},
{:text=>"Morespacing.",:character_spacing=>3},
{:text=>"DifferentFont.",:font=>"Courier"},
{:text=>"Somecoloring.",:color=>"FF00FF"},
{:text=>"Linktothewiki.",
:color=>"0000FF",
:link=>"https://github.com/prawnpdf/prawn/wiki"},
{:text=>"Linktoalocalfile.",
:color=>"0000FF",
:local=>"./local_file.txt"}
]
formatted_text_box[{:text=>"Justyourregular"},
{:text=>"text_box",:font=>"Courier"},
{:text=>"withsomeadditionalformattingoptions"+
"addedtothemix.",
:color=>[50,100,0,0],
:styles=>[:italic]}
],:at=>[100,100],:width=>200,:height=>100
Some bold. Some italic. Bold italic. Bigger Text. M o r e s p a c i n g . Different Font.
Some coloring. Link to the wiki. Link to a local file.
Just your regular text_box with
some additional formatting options
added to the mix.
text/formatted_callbacks.rb
The :callback option is also available for the formatted text methods.
This option accepts an object (or array of objects) on which two methods will be called if defined:
render_behind and render_in_front. They are called before and after rendering the text
fragment and are passed the fragment as an argument.
This example defines two new callback classes and provide callback objects for the formatted_text
classHighlightCallback
definitialize(options)
@color=options[:color]
@document=options[:document]
end
defrender_behind(fragment)
[email protected]_color
@document.fill_color=@color
@document.fill_rectangle(fragment.top_left,fragment.width,
fragment.height)
@document.fill_color=original_color
end
end
classConnectedBorderCallback
definitialize(options)
@radius=options[:radius]
@document=options[:document]
end
defrender_in_front(fragment)
@document.stroke_polygon(fragment.top_left,fragment.top_right,
fragment.bottom_right,fragment.bottom_left)
@document.fill_circle(fragment.top_left,@radius)
@document.fill_circle(fragment.top_right,@radius)
@document.fill_circle(fragment.bottom_right,@radius)
@document.fill_circle(fragment.bottom_left,@radius)
end
end
highlight=HighlightCallback.new(:color=>'ffff00',:document=>self)
border=ConnectedBorderCallback.new(:radius=>2.5,:document=>self)
formatted_text[{:text=>"hello",:callback=>highlight},
{:text=>""},
{:text=>"world",:callback=>border},
{:text=>""},
{:text=>"helloworld",:callback=>[highlight,border]}
],:size=>20
hello world hello world
text/rendering_and_color.rb
You have already seen how to set the text color using both inline formatting and the format text
methods. There is another way by using the graphics methods fill_color and stroke_color.
When reading the graphics reference you learned about fill and stroke. If you haven't read it before,
read it now before continuing.
Text can be rendered by being filled (the default mode) or just stroked or both filled and stroked.
This can be set using the text_rendering_mode method or the :mode option on the text
methods.
fill_color"00ff00"
stroke_color"0000ff"
font_size(40)do
#normalrenderingmode:fill
text"Thistextisfilledwithgreen."
move_down20
#inlinerenderingmode:stroke
text"Thistextisstrokedwithblue",:mode=>:stroke
move_down20
#blockrenderingmode:fillandstroke
text_rendering_mode(:fill_stroke)do
text"Thistextisfilledwithgreenandstrokedwithblue"
end
end
This text is filled with green.
text/text_box_extensions.rb
We've already seen one way of using text boxes with the text_box method. Turns out this
method is just a convenience for using the Prawn::Text::Box class as it creates a new object
and call render on it.
Knowing that any extensions we add to Prawn::Text::Box will take effect when we use the
text_box method. To add an extension all we need to do is append the
Prawn::Text::Box.extensions array with a module.
moduleTriangleBox
defavailable_width
height+25
end
end
y_position=cursor-10
width=100
height=100
Prawn::Text::Box.extensions<<TriangleBox
stroke_rectangle([0,y_position],width,height)
text_box("A"*100,
:at=>[0,y_position],
:width=>width,
:height=>height)
Prawn::Text::Formatted::Box.extensions<<TriangleBox
stroke_rectangle([200,y_position],width,height)
formatted_text_box([:text=>"A"*100,:color=>"009900"],
:at=>[200,y_position],
:width=>width,
:height=>height)
#Herewecleartheextensionsarray
Prawn::Text::Box.extensions.clear
Prawn::Text::Formatted::Box.extensions.clear
AAA
AAAA
AAAAAA
AAAAAAA
AAAAAAAAA
AAAAAAAAAAA
AAAAAAAAAAAAA
AAA
AAAA
AAAAAA
AAAAAAA
AAAAAAAAA
AAAAAAAAAAA
AAAAAAAAAAAAA
text/single_usage.rb
The PDF format has some built-in font support. If you want to use other fonts in Prawn you need to
embed the font file.
Doing this for a single font is extremely simple. Remember the Styling font example? Another use
of the font method is to provide a font file path and the font will be embedded in the document
and set as the current font.
This is reasonable if a font is used only once, but, if a font used several times, providing the path
each time it is used becomes cumbersome. The example on the next page shows a better way to
deal with fonts which are used several times in a document.
#UsingaTTFfontfile
font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")do
text"WrittenwiththeDejaVuSansTTFfont."
end
move_down20
text"Writtenwiththedefaultfont."
move_down20
#UsinganDFONTfontfile
font("#{Prawn::DATADIR}/fonts/Panic+Sans.dfont")do
text"WrittenwiththePanicSansDFONTfont"
end
move_down20
text"Writtenwiththedefaultfontoncemore."
Written with the DejaVu Sans TTF font.
Written with the default font.
Written with the Panic Sans DFONT font
Written with the default font once more.
text/registering_families.rb
Registering font families will help you when you want to use a font over and over or if you would
like to take advantage of the :style option of the text methods and the b and i tags when using
inline formatting.
To register a font family update the font_families hash with the font path for each style you
want to use.
#RegisteringasingleTTFfont
font_families.update("DejaVuSans"=>{
:normal=>"#{Prawn::DATADIR}/fonts/DejaVuSans.ttf"
})
font("DejaVuSans")do
text"UsingtheDejaVuSansfontprovidingonlyitsnametothefontmethod"
end
move_down20
#RegisteringaDFONTpackage
font_path="#{Prawn::DATADIR}/fonts/Panic+Sans.dfont"
font_families.update("PanicSans"=>{
:normal=>{:file=>font_path,:font=>"PanicSans"},
:italic=>{:file=>font_path,:font=>"PanicSans-Italic"},
:bold=>{:file=>font_path,:font=>"PanicSans-Bold"},
:bold_italic=>{:file=>font_path,:font=>"PanicSans-BoldItalic"}
})
font"PanicSans"
text"AlsousingPanicSansbyprovidingonlyitsname"
move_down20
text"Taking<b>advantage</b>ofthe<i>inlineformatting</i>",
:inline_format=>true
move_down20
[:bold,:bold_italic,:italic,:normal].eachdo|style|
text"Usingthe#{style}styleoption.",
:style=>style
move_down10
end
Using the DejaVu Sans font providing only its name to the font method
Also using Panic Sans by providing only its name
Taking advantage of the inline formatting
Using the bold style option.
Using the bold_italic style option.
Using the italic style option.
Using the normal style option.
text/utf8.rb
Multilingualization isn't much of a problem on Prawn as its default encoding is UTF-8. The only
thing you need to worry about is if the font support the glyphs of your language.
text"Takethisexample,asimpleEurosign:"
text"",:size=>32
move_down20
text"Seemsok.Nowlet'strysomethingmorecomplex:"
text"!"#$%&"'()%*+%","-./$0/$$1,(2#3/(-."
move_down20
text"Lookslikethecurrentfont(#{font.inspect})doesn'tsupportthose."
text"Let'strythemwithanotherfont."
move_down20
font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")do
text"!"#$%&"'()%*+%","-./$0/$$1,(2#3/(-."
text"Thereyougo."
end
Take this example, a simple Euro sign:

Seems ok. Now let's try something more complex:


_____ ______ ________ _____ __ __ _______.
Looks like the current font (Prawn::Font::AFM< Helvetica: 12 >) doesn't support those.
Let's try them with another font.
!"#$% &"'()% *+%","-. /$0/$ $1 ,( 2#3/(-.
There you go.
text/line_wrapping.rb
Line wrapping happens on white space or hyphens. Soft hyphens can be used to indicate where
words can be hyphenated. Non-breaking spaces can be used to display space without allowing for
a break.
For writing styles that do not make use of spaces, the zero width space serves to mark word
boundaries. Zero width spaces are available only with TTF fonts.
text"Hardhyphens:\n"+
"Slip-slidingaway,slipslidingawaaaay.Youknowthe"+
"neareryourdestinationthemoreyou'reslip-slidingaway."
move_down20
shy=Prawn::Text::SHY
text"Softhyphens:\n"+
"Slipslid#{shy}ingaway,slipslid#{shy}ingaway.Youknowthe"+
"neareryourdestinat#{shy}ionthemoreyou'reslipslid#{shy}ingaway."
move_down20
nbsp=Prawn::Text::NBSP
text"Non-breakingspaces:\n"+
"Slip#{nbsp}slidingaway,slip#{nbsp}slidingawaaaay.Youknowthe"+
"neareryourdestinationthemoreyou'reslip#{nbsp}slidingaway."
move_down20
font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf",:size=>16)do
long_text="Nowordboundaries:\n!"#$%&'()*+,-"./0URL12345ID6
789$DB1$:;&<5=>?@A$B3C$DEFGHI>J6KL%MNOP&4Q$R&ASTU
VWXY!"#$%&'()*+,-"./0URL12345ID6789$DB1$:;&<5=>?@A$
B3C$DEFGHI>J6KL%MNOP&4Q$R&ASTUVWXY"
textlong_text
move_down20
zwsp=Prawn::Text::ZWSP
long_text="Invisiblewordboundaries:\n!#{zwsp}"#$#{zwsp}%&#{zwsp}'()#
{zwsp}*+#{zwsp},#{zwsp}-#{zwsp}".#{zwsp}/0#{zwsp}URL#{zwsp}1#{zwsp}23#{zw
sp}45#{zwsp}ID#{zwsp}6#{zwsp}78#{zwsp}9$#{zwsp}DB#{zwsp}1$#{zwsp}:;&#{zw
sp}<5#{zwsp}=>?#{zwsp}@#{zwsp}A$#{zwsp}B3#{zwsp}C$#{zwsp}D#{zwsp}E#{zws
p}F#{zwsp}GH#{zwsp}I#{zwsp}>#{zwsp}J6#{zwsp}K#{zwsp}L%#{zwsp}MN#{zwsp}O
P&#{zwsp}4Q$R&#{zwsp}A#{zwsp}S#{zwsp}TU#{zwsp}VWXY#{zwsp}!#{zwsp}"#$
#{zwsp}%&#{zwsp}'()#{zwsp}*+#{zwsp},#{zwsp}-#{zwsp}".#{zwsp}/0#{zwsp}URL
#{zwsp}1#{zwsp}23#{zwsp}45#{zwsp}ID#{zwsp}6#{zwsp}78#{zwsp}9$#{zwsp}DB#{zw
sp}1$#{zwsp}:;&#{zwsp}<5#{zwsp}=>?#{zwsp}@#{zwsp}A$#{zwsp}B3#{zwsp}C$
#{zwsp}D#{zwsp}E#{zwsp}F#{zwsp}GH#{zwsp}I#{zwsp}>#{zwsp}J6#{zwsp}K#{zwsp}
L%#{zwsp}MN#{zwsp}OP&#{zwsp}4Q$R&#{zwsp}A#{zwsp}S#{zwsp}TU#{zwsp}VWX
Y"
textlong_text
end
Hard hyphens:
Slip-sliding away, slip sliding awaaaay. You know the nearer your destination the more you're slip-
sliding away.
Soft hyphens:
Slip sliding away, slip sliding away. You know the nearer your destination the more you're slip slid-
ing away.
Non-breaking spaces:
Slipsliding away, slipsliding awaaaay. You know the nearer your destination the more you're
slipsliding away.
No word boundaries:
!"#$%&'()*+,-"./0URL12345ID6789$DB1$:
;&<5=>?@A$B3C$DEFGHI>J6KL%MNOP&4Q
$R&ASTUVWXY!"#$%&'()*+,-"./0URL1234
5ID6789$DB1$:;&<5=>?@A$B3C$DEFGHI>J
6KL%MNOP&4Q$R&ASTUVWXY
Invisible word boundaries:
!"#$%&'()*+,-"./0URL12345ID6789$DB1$
:;&<5=>?@A$B3C$DEFGHI>J6KL%MNOP&
4Q$R&ASTUVWXY!"#$%&'()*+,-"./0URL1
2345ID6789$DB1$:;&<5=>?@A$B3C$DEFGH
I>J6KL%MNOP&4Q$R&ASTUVWXY
text/right_to_left_text.rb
Prawn can be used with right-to-left text. The direction can be set document-wide, on particular
text, or on a text-box. Setting the direction to :rtl automatically changes the default alignment to
:right
You can even override direction on an individual fragment. The one caveat is that two fragments
going against the main direction cannot be placed next to each other without appearing in the
wrong order.
#setthedirectiondocument-wide
self.text_direction=:rtl
font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf",:size=>16)do
long_text="<5=>?@A$B3C$DEFGHI>J6KL%MNOP<5=>?@A$B3C
$DEFGHI>J6KL%MNOP<5=>?@A$B3C$DEFGHI>J6KL%MNOP<5=
"
textlong_text
move_down20
text"Youcanoverridethedocumentdirection.",:direction=>:ltr
move_down20
formatted_text[{:text=>"!"#$%&'()*+,-"./0"},
{:text=>"URL",:direction=>:ltr},
{:text=>"12345"},
{:text=>"ID",:direction=>:ltr},
{:text=>"6789$"},
{:text=>"DB",:direction=>:ltr},
{:text=>"1$:;&<5=>?@A$B3C$DEFGHI>J6KL%M
NOP&4Q$R&ASTUVWXY"}]
move_down20
formatted_text[{:text=>"!"#$%&'()*+,-"./0"},
{:text=>"this",:direction=>:ltr},
{:text=>"won't",:direction=>:ltr,:size=>24},
{:text=>"work",:direction=>:ltr},
{:text=>"1$:;&<5=>?@A$B3C$DEFGHI>J6KL%M
NOP"}]
end
?>=5<PONM%LK6J>IHGFED$C3B$A@?>=5<
3B$A@?>=5<PONM%LK6J>IHGFED$C3B$A@
=5<PONM%LK6J>IHGFED$C
You can override the document direction.
:$1DB$9876ID54321URL0/."-,+*)('&%$#"!
Q4&PONM%LK6J>IHGFED$C3B$A@?>=5<&;
YXWVUTSA&R$
>=5<&;:$1workwon'tthis0/."-,+*)('&%$#"!
PONM%LK6J>IHGFED$C3B$A@?
text/fallback_fonts.rb
Prawn enables the declaration of fallback fonts for those glyphs that may not be present in the
desired font. Use the :fallback_fonts option with any of the text or text box methods, or set
fallback_fonts document-wide.
file="#{Prawn::DATADIR}/fonts/gkai00mp.ttf"
font_families["Kai"]={
:normal=>{:file=>file,:font=>"Kai"}
}
file="#{Prawn::DATADIR}/fonts/Panic+Sans.dfont"
font_families["PanicSans"]={
:normal=>{:file=>file,:font=>"PanicSans"},
}
font("PanicSans")do
text("Whenfallbackfontsareincluded,eachglyphwillberendered"+
"usingthefirstfontthatincludestheglyph,startingwiththe"+
"currentfontandthenmovingthroughthefallbackfontsfromleft"+
"toright."+
"\n\n"+
"helloAZ\n[\goodbye",
:fallback_fonts=>["Times-Roman","Kai"])
end
move_down20
formatted_text([{:text=>"Fallbackfontscanevenoverride"},
{:text=>"fragmentfonts(AZ)",:font=>"Times-Roman"},
],
:fallback_fonts=>["Times-Roman","Kai"])
When fallback fonts are included, each glyph will be rendered using the
first font that includes the glyph, starting with the current font and
then moving through the fallback fonts from left to right.
hello !"
#$ goodbye
Fallback fonts can even overridefragment fonts (!")
text/win_ansi_charset.rb
Prints a list of all of the glyphs that can be rendered by Adobe's built in fonts, along with their
character widths and WinAnsi codes. Be sure to pass these glyphs as UTF-8, and Prawn will
transcode them for you.
FONT_SIZE=9.5
x=0
y=bounds.top
fields=[[20,:right],[8,:left],[12,:center],[30,:right],[8,:left],
[0,:left]]
font"Helvetica",:size=>FONT_SIZE
move_down30
text"(SeenextpageforWinAnsitable)",:align=>:center
start_new_page
Prawn::Encoding::WinAnsi::CHARACTERS.each_with_indexdo|name,index|
nextifname==".notdef"
y-=FONT_SIZE
ify<FONT_SIZE
y=bounds.top-FONT_SIZE
x+=170
end
code="%d."%index
char=index.chr
width=1000*width_of(char,:size=>FONT_SIZE)/FONT_SIZE
size="%d"%width
data=[code,nil,char,size,nil,name]
dx=x
fields.zip(data).eachdo|(total_width,align),field|
iffield
width=width_of(field,:size=>FONT_SIZE)
casealign
when:leftthenoffset=0
when:rightthenoffset=total_width-width
when:centerthenoffset=(total_width-width)/2
end
text_box(field,:at=>[dx+offset,y],:skip_encoding=>true)
end
dx+=total_width
end
end
(See next page for WinAnsi table)
32. 278 space
33. ! 278 exclam
34. " 355 quotedbl
35. # 556 numbersign
36. $ 556 dollar
37. % 889 percent
38. & 667 ampersand
39. ' 191 quotesingle
40. ( 333 parenleft
41. ) 333 parenright
42. * 389 asterisk
43. + 584 plus
44. , 278 comma
45. - 333 hyphen
46. . 278 period
47. / 278 slash
48. 0 556 zero
49. 1 556 one
50. 2 556 two
51. 3 556 three
52. 4 556 four
53. 5 556 five
54. 6 556 six
55. 7 556 seven
56. 8 556 eight
57. 9 556 nine
58. : 278 colon
59. ; 278 semicolon
60. < 584 less
61. = 584 equal
62. > 584 greater
63. ? 556 question
64. @ 1015 at
65. A 667 A
66. B 667 B
67. C 722 C
68. D 722 D
69. E 667 E
70. F 611 F
71. G 778 G
72. H 722 H
73. I 278 I
74. J 500 J
75. K 667 K
76. L 556 L
77. M 833 M
78. N 722 N
79. O 778 O
80. P 667 P
81. Q 778 Q
82. R 722 R
83. S 667 S
84. T 611 T
85. U 722 U
86. V 667 V
87. W 944 W
88. X 667 X
89. Y 667 Y
90. Z 611 Z
91. [ 278 bracketleft
92. \ 278 backslash
93. ] 278 bracketright
94. ^ 469 asciicircum
95. _ 556 underscore
96. ` 333 grave
97. a 556 a
98. b 556 b
99. c 500 c
100. d 556 d
101. e 556 e
102. f 278 f
103. g 556 g
104. h 556 h
105. i 222 i
106. j 222 j
107. k 500 k
108. l 222 l
109. m 833 m
110. n 556 n
111. o 556 o
112. p 556 p
113. q 556 q
114. r 333 r
115. s 500 s
116. t 278 t
117. u 556 u
118. v 500 v
119. w 722 w
120. x 500 x
121. y 500 y
122. z 500 z
123. { 334 braceleft
124. | 259 bar
125. } 334 braceright
126. ~ 584 asciitilde
128. 556 Euro
130. 222 quotesinglbase
131. 556 florin
132. 333 quotedblbase
133. 1000 ellipsis
134. 556 dagger
135. 556 daggerdbl
136. 333 circumflex
137. 1000 perthousand
138. 667 Scaron
139. 333 guilsinglleft
140. 1000 OE
142. 611 Zcaron
145. 222 quoteleft
146. 222 quoteright
147. 333 quotedblleft
148. 333 quotedblright
149. 349 bullet
150. 556 endash
151. 1000 emdash
152. 333 tilde
153. 1000 trademark
154. 500 scaron
155. 333 guilsinglright
156. 944 oe
158. 500 zcaron
159. 500 ydieresis
160. 278 space
161. 333 exclamdown
162. 556 cent
163. 556 sterling
164. 556 currency
165. 556 yen
166. 259 brokenbar
167. 556 section
168. 333 dieresis
169. 737 copyright
170. 370 ordfeminine
171. 556 guillemotleft
172. 584 logicalnot
173. - 333 hyphen
174. 737 registered
175. 333 macron
176. 400 degree
177. 584 plusminus
178. 333 twosuperior
179. 333 threesuperior
180. 333 acute
181. 556 mu
182. 537 paragraph
183. 278 periodcentered
184. 333 cedilla
185. 333 onesuperior
186. 365 ordmasculine
187. 556 guillemotright
188. 834 onequarter
189. 834 onehalf
190. 834 threequarters
191. 611 questiondown
192. 667 Agrave
193. 667 Aacute
194. 667 Acircumflex
195. 667 Atilde
196. 667 Adieresis
197. 667 Aring
198. 1000 AE
199. 722 Ccedilla
200. 667 Egrave
201. 667 Eacute
202. 667 Ecircumflex
203. 667 Edieresis
204. 278 Igrave
205. 278 Iacute
206. 278 Icircumflex
207. 278 Idieresis
208. 722 Eth
209. 722 Ntilde
210. 778 Ograve
211. 778 Oacute
212. 778 Ocircumflex
213. 778 Otilde
214. 778 Odieresis
215. 584 multiply
216. 778 Oslash
217. 722 Ugrave
218. 722 Uacute
219. 722 Ucircumflex
220. 722 Udieresis
221. 667 Yacute
222. 667 Thorn
223. 611 germandbls
224. 556 agrave
225. 556 aacute
226. 556 acircumflex
227. 556 atilde
228. 556 adieresis
229. 556 aring
230. 889 ae
231. 500 ccedilla
232. 556 egrave
233. 556 eacute
234. 556 ecircumflex
235. 556 edieresis
236. 278 igrave
237. 278 iacute
238. 278 icircumflex
239. 278 idieresis
240. 556 eth
241. 556 ntilde
242. 556 ograve
243. 556 oacute
244. 556 ocircumflex
245. 556 otilde
246. 556 odieresis
247. 584 divide
248. 611 oslash
249. 556 ugrave
250. 556 uacute
251. 556 ucircumflex
252. 556 udieresis
253. 500 yacute
254. 556 thorn
255. 500 ydieresis
Bounding box
Bounding boxes are the basic containers for structuring the content flow. Even being low level
building blocks sometimes their simplicity is very welcome.
The examples show:
How to create bounding boxes with specific dimensions
How to inspect the current bounding box for its coordinates
Stretchy bounding boxes
Nested bounding boxes
Indent blocks
bounding_box/creation.rb
If you've read the basic concepts examples you probably know that the origin of a page is on the
bottom left corner and that the content flows from top to bottom.
You also know that a Bounding Box is a structure for helping the content flow.
A bounding box can be created with the bounding_box method. Just provide the top left corner,
a required :width option and an optional :height.
bounding_box([200,cursor-100],:width=>200,:height=>100)do
text"Justyourregularboundingbox"
transparent(0.5){stroke_bounds}
end
Just your regular bounding box
bounding_box/bounds.rb
The bounds method returns the current bounding box. This is useful because the
Prawn::BoundinBox exposes some nice boundary helpers.
top, bottom, left and right methods return the bounding box boundaries relative to its
translated origin. top_left, top_right, bottom_left and bottom_right return those
boundaries pairs inside arrays.
All these methods have an "absolute" version like absolute_right. The absolute version
returns the same boundary relative to the page absolute coordinates.
The following snippet shows the boundaries for the margin box side by side with the boundaries for
a custom bounding box.
defprint_coordinates
text"top:#{bounds.top}"
text"bottom:#{bounds.bottom}"
text"left:#{bounds.left}"
text"right:#{bounds.right}"
move_down10
text"absolutetop:#{sprintf"%.2f",bounds.absolute_top}"
text"absolutebottom:#{sprintf"%.2f",bounds.absolute_bottom}"
text"absoluteleft:#{sprintf"%.2f",bounds.absolute_left}"
text"absoluteright:#{sprintf"%.2f",bounds.absolute_right}"
end
text"Marginboxbounds:"
move_down5
print_coordinates
bounding_box([250,cursor+140],:width=>200,:height=>150)do
text"Thisboundingboxbounds:"
move_down5
print_coordinates
transparent(0.5){stroke_bounds}
end
Margin box bounds:
top: 864.0
bottom: 0
left: 0
right: 540.0
absolute top: 900.00
absolute bottom: 36.00
absolute left: 36.00
absolute right: 576.00
This bounding box bounds:
top: 150
bottom: 0
left: 0
right: 200
absolute top: 263.17
absolute bottom: 113.17
absolute left: 286.00
absolute right: 486.00
bounding_box/stretchy.rb
Bounding Boxes accept an optional :height parameter. Unless it is provided the bounding box
will be stretchy. It will expand the height to fit all content generated inside it.
y_position=cursor
bounding_box([0,y_position],:width=>200,:height=>100)do
text"Thisboundingboxhasaheightof100.Ifthistextgetstoolarge"+
"itwillflowtothenextpage."
transparent(0.5){stroke_bounds}
end
bounding_box([300,y_position],:width=>200)do
text"Thisboundingboxhasvariableheight.Nomatterhowmuchtextis"+
"writtenhere,theheightwillexpandtofit."
text"_"*100
text"*"*100
transparent(0.5){stroke_bounds}
end
This bounding box has a height of
100. If this text gets too large it will
flow to the next page.
This bounding box has variable
height. No matter how much text is
written here, the height will expand to
fit.
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
* * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * *
bounding_box/nesting.rb
Normally when we provide the top left corner of a bounding box we express the coordinates
relative to the margin box. This is not the case when we have nested bounding boxes. Once
nested the inner bounding box coordinates are relative to the outter bounding box.
This example shows some nested bounding boxes with fixed and stretchy heights. Note how the
cursor method returns coordinates relative to the current bounding box.
defbox_content(string)
textstring
transparent(0.5){stroke_bounds}
end
gap=20
bounding_box([50,cursor],:width=>400,:height=>200)do
box_content("Fixedheight")
bounding_box([gap,cursor-gap],:width=>300)do
text"Stretchyheight"
bounding_box([gap,bounds.top-gap],:width=>100)do
text"Stretchyheight"
transparent(0.5){dash(1);stroke_bounds;undash}
end
bounding_box([gap*7,bounds.top-gap],:width=>100,:height=>50)do
box_content("Fixedheight")
end
transparent(0.5){dash(1);stroke_bounds;undash}
end
bounding_box([gap,cursor-gap],:width=>300,:height=>50)do
box_content("Fixedheight")
end
end
Fixed height
Stretchy height
Stretchy height Fixed height
Fixed height
bounding_box/indentation.rb
Sometimes you just need to indent a portion of the contents of a bounding box, and using a nested
bounding box is pure overkill. The indent method is what you might need.
Just provide a number for it to indent all content generated inside the block.
text"Noindentationonthemarginbox."
indent(20)do
text"Someindentationinsideanindentblock."
end
move_down20
bounding_box([50,cursor],:width=>400,:height=>cursor)do
transparent(0.5){stroke_bounds}
move_down10
text"Noindentationinsidethisboundingbox."
indent(40)do
text"Insideanindentblock.Andsoisthishorizontalline:"
stroke_horizontal_rule
end
move_down10
text"Noindentation"
move_down20
indent(60)do
text"Anotherindentblock."
bounding_box([0,cursor],:width=>200)do
text"Notethatthisboundingboxcoordinatesarerelativetothe"+
"indentblock"
transparent(0.5){stroke_bounds}
end
end
end
No indentation on the margin box.
Some indentation inside an indent block.
No indentation inside this bounding box.
Inside an indent block. And so is this horizontal line:
No indentation
Another indent block.
Note that this bounding box
coordinates are relative to the indent
block
bounding_box/canvas.rb
The origin example already mentions that a new document already comes with a margin box
whose bottom left corner is used as the origin for calculating coordinates.
What has not been told is that there is one helper for "bypassing" the margin box: canvas. This
method is a shortcut for creating a bounding box mapped to the absolute coordinates and
evaluating the code inside it.
The following snippet draws a circle on each of the four absolute corners.
canvasdo
fill_circle[bounds.left,bounds.top],30
fill_circle[bounds.right,bounds.top],30
fill_circle[bounds.right,bounds.bottom],30
fill_circle[0,0],30
end
bounding_box/russian_boxes.rb
This example is mostly just for fun, and shows how nested bounding boxes can simplify
calculations. See the "Bounding Box" section of the manual for more basic uses.
defcombine(a1,a2)
output=[]
a1.eachdo|i1|
a2.eachdo|i2|
output+=[[i1,i2]]
end
end
output
end
defrecurse_bounding_box(max_depth=4,depth=1)
width=(bounds.width-15)/2
height=(bounds.height-15)/2
left_top_corners=combine([5,bounds.right-width-5],
[bounds.top-5,height+5])
left_top_corners.eachdo|lt|
bounding_box(lt,:width=>width,:height=>height)do
stroke_bounds
recurse_bounding_box(max_depth,depth+1)ifdepth<max_depth
end
end
end
#Setupabboxfromthedashedlinetothebottomofthepage
bounding_box([0,cursor],:width=>bounds.width,:height=>cursor)do
recurse_bounding_box
end
Layout
Prawn has support for two-dimensional grid based layouts out of the box.
The examples show:
How to define the document grid
How to configure the grid rows and columns gutters
How to create boxes according to the grid
layout/simple_grid.rb
The document grid on Prawn is just a table-like structure with a defined number of rows and
columns. There are some helpers to create boxes of content based on the grid coordinates.
define_grid accepts the following options which are pretty much self-explanatory: :rows,
:columns, :gutter, :row_gutter, :column_gutter
#Thegridonlyneedtobedefinedonce,butsincealltheexamplesshouldbe
#abletorunalonewearerepeatingitoneveryexample
define_grid(:columns=>5,:rows=>8,:gutter=>10)
text"Wedefinedthegrid,rollovertothenextpagetoseeitsoutline"
start_new_page
grid.show_all
We defined the grid, roll over to the next page to see its outline
0,0 0,1 0,2 0,3 0,4
1,0 1,1 1,2 1,3 1,4
2,0 2,1 2,2 2,3 2,4
3,0 3,1 3,2 3,3 3,4
4,0 4,1 4,2 4,3 4,4
5,0 5,1 5,2 5,3 5,4
6,0 6,1 6,2 6,3 6,4
7,0 7,1 7,2 7,3 7,4
layout/boxes.rb
After defined the grid is there but nothing happens. To start taking effect we need to use the grid
boxes.
grid has three different return values based on the arguments received. With no arguments it will
return the grid itself. With integers it will return the grid box at those indices. With two arrays it will
return a multi-box spanning the region of the two grid boxes at the arrays indices.
#Thegridonlyneedtobedefinedonce,butsincealltheexamplesshouldbe
#abletorunalonewearerepeatingitoneveryexample
define_grid(:columns=>5,:rows=>8,:gutter=>10)
grid(4,0).show
grid(5,1).show
grid([6,2],[7,3]).show
grid([4,4],[7,4]).show
grid([7,0],[7,1]).show
4,0
5,1
6,2:7,3
4,4:7,4
7,0:7,1
layout/content.rb
Now that we know how to access the boxes we might as well add some content to them.
This can be done by taping into the bounding box for a given grid box or multi-box with the
bounding_box method.
#Thegridonlyneedtobedefinedonce,butsincealltheexamplesshouldbe
#abletorunalonewearerepeatingitoneveryexample
define_grid(:columns=>5,:rows=>8,:gutter=>10)
grid([5,0],[7,1]).bounding_boxdo
text"Addingsomecontenttothismulti_box.\n"+"_"*200
end
grid(6,3).bounding_boxdo
text"Justalittlesnippethere.\n"+"_"*10
end
Adding some content to this multi_box.
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _
Just a little snippet
here.
_ _ _ _ _ _ _
_ _ _
Prawn::Table
As of Prawn 1.2.0, Prawn::Table has been extracted into its own semi-officially supported gem.
Please see https://github.com/prawnpdf/prawn-table for more details.
Images
Embedding images on PDF documents is fairly easy. Prawn supports both JPG and PNG images.
The examples show:
How to add an image to a page
How place the image on a specific position
How to configure the image dimensions by setting the width and height or by scaling it
images/plain_image.rb
To embed images onto your PDF file use the image method. It accepts the file path of the image
to be loaded and some optional arguments.
If only the image path is provided the image will be rendered starting on the cursor position. No
manipulation is done with the image even if it doesn't fit entirely on the page like the following
snippet.
text"Theimagewillgorightbelowthislineoftext."
image"#{Prawn::DATADIR}/images/pigs.jpg"
The image will go right below this line of text.
images/absolute_position.rb
One of the options that the image method accepts is :at. If you've read some of the graphics
examples you are probably already familiar with it. Just provide it the upper-left corner where you
want the image placed.
While sometimes useful this option won't be practical. Notice that the cursor won't be moved after
the image is rendered and there is nothing forbidding the text to overlap with the image.
y_position=cursor
text"Theimagewon'tgobelowthislineoftext."
image"#{Prawn::DATADIR}/images/fractal.jpg",:at=>[200,y_position]
text"Andthislineoftextwillgojustbelowthepreviousone."
The image won't go below this line of text.
And this line of text will go just below the previous one.
images/horizontal.rb
The image may be positioned relatively to the current bounding box. The horizontal position may
be set with the :position option.
It may be :left, :center, :right or a number representing an x-offset from the left boundary.
bounding_box([50,cursor],:width=>400,:height=>450)do
stroke_bounds
[:left,:center,:right].eachdo|position|
text"Imagealignedtothe#{position}."
image"#{Prawn::DATADIR}/images/stef.jpg",:position=>position
end
text"Thenextimagehasa50pointoffsetfromtheleftboundary"
image"#{Prawn::DATADIR}/images/stef.jpg",:position=>50
end
Image aligned to the left.
Image aligned to the center.
Image aligned to the right.
The next image has a 50 point offset from the left boundary
images/vertical.rb
To set the vertical position of an image use the :vposition option.
It may be :top, :center, :bottom or a number representing the y-offset from the top boundary.
bounding_box([0,cursor],:width=>500,:height=>450)do
stroke_bounds
[:top,:center,:bottom].eachdo|vposition|
text"Imageverticallyalignedtothe#{vposition}.",:valign=>vposition
image"#{Prawn::DATADIR}/images/stef.jpg",:position=>250,
:vposition=>vposition
end
text_box"Thenextimagehasa100pointoffsetfromthetopboundary",
:at=>[bounds.width-110,bounds.top-10],:width=>100
image"#{Prawn::DATADIR}/images/stef.jpg",:position=>:right,
:vposition=>100
end
Image vertically aligned to the top.
Image vertically aligned to the center.
Image vertically aligned to the bottom.
The next image
has a 100 point
offset from the top
boundary
images/width_and_height.rb
The image size can be set with the :width and :height options.
If only one of those is provided, the image will be scaled proportionally. When both are provided,
the image will be stretched to fit the dimensions without maintaining the aspect ratio.
text"Scalebysettingonlythewidth"
image"#{Prawn::DATADIR}/images/pigs.jpg",:width=>150
move_down20
text"Scalebysettingonlytheheight"
image"#{Prawn::DATADIR}/images/pigs.jpg",:height=>100
move_down20
text"Stretchtofitthewidthandheightprovided"
image"#{Prawn::DATADIR}/images/pigs.jpg",:width=>500,:height=>100
Scale by setting only the width
Scale by setting only the height
Stretch to fit the width and height provided
images/scale.rb
To scale an image use the :scale option.
It scales the image proportionally given the provided value.
text"Normalsize"
image"#{Prawn::DATADIR}/images/stef.jpg"
move_down20
text"Scaledto50%"
image"#{Prawn::DATADIR}/images/stef.jpg",:scale=>0.5
move_down20
text"Scaledto200%"
image"#{Prawn::DATADIR}/images/stef.jpg",:scale=>2
Normal size
Scaled to 50%
Scaled to 200%
images/fit.rb
:fit option is useful when you want the image to have the maximum size within a container
preserving the aspect ratio without overlapping.
Just provide the container width and height pair.
size=300
text"Usingthefitoption"
bounding_box([0,cursor],:width=>size,:height=>size)do
image"#{Prawn::DATADIR}/images/pigs.jpg",:fit=>[size,size]
stroke_bounds
end
Using the fit option
Document and page options
So far we've already seen how to create new documents and start new pages. This chapter
expands on the previous examples by showing other options avialable. Some of the options are
only available when creating new documents.
The examples show:
How to configure page size
How to configure page margins
How to use a background image
How to add metadata to the generated PDF
document_and_page_options/page_size.rb
Prawn comes with support for most of the common page sizes so you'll only need to provide
specific values if your intended format is not supported. To see a list with all supported sizes take a
look at PDF::Core::PageGeometry
# To define the size use :page_size when creating new documents and :size when starting
new pages. The default page size for new documents is LETTER (612.00 x 792.00).
You may also define the orientation of the page to be either portrait (default) or landscape. Use
:page_layout when creating new documents and :layout when starting new pages.
Prawn::Document.generate("page_size.pdf",
:page_size=>"EXECUTIVE",
:page_layout=>:landscape
)do
text"EXECUTIVElandscapepage."
custom_size=[275,326]
["A4","TABLOID","B7",custom_size].eachdo|size|
start_new_page(:size=>size,:layout=>:portrait)
text"#{size}portraitpage."
start_new_page(:size=>size,:layout=>:landscape)
text"#{size}landscapepage."
end
end
This code snippet was not evaluated inline. You may see its output by running the example file located here:
http://github.com/prawnpdf/prawn/tree/master/manual/document_and_page_options/page_size.rb
document_and_page_options/page_margins.rb
The default margin for pages is 0.5 inch but you can change that with the :margin option or if
you'd like to have different margins you can use the :left_margin, :right_margin,
:top_margin, :bottom_margin options.
These options are available both for starting new pages and creating new documents.
Prawn::Document.generate("page_margins.pdf",
:margin=>100
)do
text"100ptsmargins."
stroke_bounds
start_new_page(:left_margin=>300)
text"300ptsmarginontheleft."
stroke_bounds
start_new_page(:top_margin=>300)
text"300ptsmarginbothonthetopandontheleft.Noticethatwhenever"+
"yousetanoptionforanewpageitwillremainthedefaultforthe"+
"followingpages."
stroke_bounds
start_new_page(:margin=>50)
text"50ptsmargins.Usingthemarginoptionwillresetpreviousspecific"+
"callstoleft,right,topandbottommargins."
stroke_bounds
start_new_page(:margin=>[50,100,150,200])
text"ThereisalsotheshorthandCSSlikesyntaxusedhere."
stroke_bounds
end
This code snippet was not evaluated inline. You may see its output by running the example file located here:
http://github.com/prawnpdf/prawn/tree/master/manual/document_and_page_options/page_margins.rb
document_and_page_options/background.rb
Pass an image path to the :background option and it will be used as the background for all
pages. This option can only be used on document creation.
img="#{Prawn::DATADIR}/images/letterhead.jpg"
Prawn::Document.generate("background.pdf",
:background=>img,
:margin=>100
)do
text"Myreportcaption",:size=>18,:align=>:right
move_downfont.height*2
text"Hereismytextexplaningthisreport."*20,
:size=>12,:align=>:left,:leading=>2
move_downfont.height
text"I'musingasoftbackground."*40,
:size=>12,:align=>:left,:leading=>2
end
This code snippet was not evaluated inline. You may see its output by running the example file located here:
http://github.com/prawnpdf/prawn/tree/master/manual/document_and_page_options/background.rb
document_and_page_options/metadata.rb
To set the document metadata just pass a hash to the :info option when creating new
documents. The keys in the example below are arbitrary, so you may add whatever keys you want
Prawn::Document.generate("metadata.pdf",
:info=>{
:Title=>"Mytitle",
:Author=>"JohnDoe",
:Subject=>"MySubject",
:Keywords=>"testmetadatarubypdfdry",
:Creator=>"ACMESoftApp",
:Producer=>"Prawn",
:CreationDate=>Time.now
})do
text"Thisisatestofsettingmetadatapropertiesviatheinfooption."
text"Whilethekeysarearbitrary,theaboveexamplesetscommonattributes."
end
This code snippet was not evaluated inline. You may see its output by running the example file located here:
http://github.com/prawnpdf/prawn/tree/master/manual/document_and_page_options/metadata.rb
document_and_page_options/print_scaling.rb
(Optional; PDF 1.6) The page scaling option to be selected when a print dialog is displayed for this
document. Valid values are None, which indicates that the print dialog should reflect no page
scaling, and AppDefault, which indicates that applications should use the current print scaling. If
this entry has an unrecognized value, applications should use the current print scaling. Default
value: AppDefault.
Note: If the print dialog is suppressed and its parameters are provided directly by the application,
the value of this entry should still be used.
Prawn::Document.generate("print_scaling.pdf",
:page_layout=>:landscape,
:print_scaling=>:none
)do
text"Whenyouprintthisdocument,thescaletofitinprintpreviewshouldb
edisabledbydefault."
end
This code snippet was not evaluated inline. You may see its output by running the example file located here:
http://github.com/prawnpdf/prawn/tree/master/manual/document_and_page_options/print_scaling.rb
Outline
The outline of a PDF document is the table of contents tab you see to the right or left of your PDF
viewer.
The examples include:
How to define sections and pages
How to insert sections and/or pages to a previously defined outline structure
outline/sections_and_pages.rb
The document outline tree is the set of links used to navigate through the various document
sections and pages.
To define the document outline we first use the outline method to lazily instantiate an outline
object. Then we use the define method with a block to start the outline tree.
The basic methods for creating outline nodes are section and page. The only difference
between the two is that page doesn't accept a block and will only create leaf nodes while
section accepts a block to create nested nodes.
section accepts the title of the section and two options: :destination - a page number to link
and :closed - a boolean value that defines if the nested outline nodes are shown when the
document is open (defaults to true).
page is very similar to section. It requires a :title option to be set and accepts a
:destination.
section and page may also be used without the define method but they will need to instantiate
the outline object every time.
#Firstwecreate10pagesjusttohavesomethingtolinkto
(1..10).eachdo|index|
text"Page#{index}"
start_new_page
end
outline.definedo
section("Section1",:destination=>1)do
page:title=>"Page2",:destination=>2
page:title=>"Page3",:destination=>3
end
section("Section2",:destination=>4)do
page:title=>"Page5",:destination=>5
section("Subsection2.1",:destination=>6,:closed=>true)do
page:title=>"Page7",:destination=>7
end
end
end
#Outsideofthedefineblock
outline.section("Section3",:destination=>8)do
outline.page:title=>"Page9",:destination=>9
end
outline.page:title=>"Page10",:destination=>10
#SectionandPageswithoutlinks.Whileasectionwithoutalinkmaybe
#usefultogroupsomepages,apagewithoutalinkisuseless
outline.updatedo#updateisanaliastodefine
section("Sectionwithoutlink")do
page:title=>"Pagewithoutlink"
end
end
This code snippet was not evaluated inline. You may see its output by running the example file located here:
http://github.com/prawnpdf/prawn/tree/master/manual/outline/sections_and_pages.rb
outline/add_subsection_to.rb
We have already seen how to define an outline tree sequentially.
If you'd like to add nodes to the middle of an outline tree the add_subsection_to may help you.
It allows you to insert sections to the outline tree at any point. Just provide the title of the parent
section, the position you want the new subsection to be inserted :first or :last (defaults to
:last) and a block to declare the subsection.
The add_subsection_to block doesn't necessarily create new sections, it may also create new
pages.
If the parent title provided is the title of a page. The page will be converted into a section to receive
the subsection created.
#Firstwecreate10pagesandsomedefaultoutline
(1..10).eachdo|index|
text"Page#{index}"
start_new_page
end
outline.definedo
section("Section1",:destination=>1)do
page:title=>"Page2",:destination=>2
page:title=>"Page3",:destination=>3
end
end
#Nowwewillstartaddingnodestothepreviousoutline
outline.add_subsection_to("Section1",:first)do
outline.section("Addedlater-firstposition")do
outline.page:title=>"Page4",:destination=>4
outline.page:title=>"Page5",:destination=>5
end
end
outline.add_subsection_to("Section1")do
outline.page:title=>"Addedlater-lastposition",
:destination=>6
end
outline.add_subsection_to("Addedlater-firstposition")do
outline.page:title=>"Anotherpageaddedlater",
:destination=>7
end
#Thetitleprovidedisforapagewhichwillbeconvertedintoasection
outline.add_subsection_to("Page3")do
outline.page:title=>"Lastpageadded",
:destination=>8
end
This code snippet was not evaluated inline. You may see its output by running the example file located here:
http://github.com/prawnpdf/prawn/tree/master/manual/outline/add_subsection_to.rb
outline/insert_section_after.rb
Another way to insert nodes into an existing outline is the insert_section_after method.
It accepts the title of the node that the new section will go after and a block declaring the new
section.
As is the case with add_subsection_to the section added doesn't need to be a section, it may
be just a page.
#Firstwecreate10pagesandsomedefaultoutline
(1..10).eachdo|index|
text"Page#{index}"
start_new_page
end
outline.definedo
section("Section1",:destination=>1)do
page:title=>"Page2",:destination=>2
page:title=>"Page3",:destination=>3
end
end
#Nowwewillstartaddingnodestothepreviousoutline
outline.insert_section_after("Page2")do
outline.section("SectionafterPage2")do
outline.page:title=>"Page4",:destination=>4
end
end
outline.insert_section_after("Section1")do
outline.section("SectionafterSection1")do
outline.page:title=>"Page5",:destination=>5
end
end
#Addingjustapage
outline.insert_section_after("Page3")do
outline.page:title=>"PageafterPage3",:destination=>6
end
This code snippet was not evaluated inline. You may see its output by running the example file located here:
http://github.com/prawnpdf/prawn/tree/master/manual/outline/insert_section_after.rb
Repeatable content
Prawn offers two ways to handle repeatable content blocks. Repeater is useful for content that gets
repeated at well defined intervals while Stamp is more appropriate if you need better control of
when to repeat it.
There is also one very specific helper for numbering pages.
The examples show:
How to repeat content on several pages with a single invocation
How to create a new Stamp
How to "stamp" the content block on the page
How to number the document pages with one simple call
repeatable_content/repeater.rb
The repeat method is quite versatile when it comes to define the intervals at which the content
block should repeat.
The interval may be a symbol (:all, :odd, :even), an array listing the pages, a range or a Proc
that receives the page number as an argument and should return true if the content is to be
repeated on the given page.
You may also pass an option :dynamic to reevaluate the code block on every call which is useful
when the content changes based on the page number.
It is also important to say that no matter where you define the repeater it will be applied to all
matching pages.
repeat(:all)do
draw_text"Allpages",:at=>bounds.top_left
end
repeat(:odd)do
draw_text"Onlyoddpages",:at=>[0,0]
end
repeat(:even)do
draw_text"Onlyevenpages",:at=>[0,0]
end
repeat([1,3,7])do
draw_text"Onlyonpages1,3and7",:at=>[100,0]
end
repeat(2..4)do
draw_text"Fromthe2ndtothe4thpage",:at=>[300,0]
end
repeat(lambda{|pg|pg%3==0})do
draw_text"Everythirdpage",:at=>[250,20]
end
repeat(:all,:dynamic=>true)do
draw_textpage_number,:at=>[500,0]
end
10.timesdo
start_new_page
draw_text"Awonderfulpage",:at=>[400,400]
end
This code snippet was not evaluated inline. You may see its output by running the example file located here:
http://github.com/prawnpdf/prawn/tree/master/manual/repeatable_content/repeater.rb
repeatable_content/stamp.rb
Stamps should be used when you have content that will be included multiple times in a document.
Its advantages over creating the content anew each time are:
1. Faster document creation
2. Smaller final document
3. Faster display on subsequent displays of the repeated element because the viewer application
can cache the rendered results
The create_stamp method does just what it says. Pass it a block with the content that should be
generated and the stamp will be created.
There are two methods to render the stamp on a page stamp and stamp_at. The first will render
the stamp as is while the second accepts a point to serve as an offset to the stamp content.
create_stamp("approved")do
rotate(30,:origin=>[-5,-5])do
stroke_color"FF3333"
stroke_ellipse[0,0],29,15
stroke_color"000000"
fill_color"993333"
font("Times-Roman")do
draw_text"Approved",:at=>[-23,-3]
end
fill_color"000000"
end
end
stamp"approved"
stamp_at"approved",[200,200]
A
p
p
r
o
v
e
d
A
p
p
r
o
v
e
d
repeatable_content/page_numbering.rb
The number_pages method is a simple way to number the pages of your document. It should be
called towards the end of the document since pages created after the call won't be numbered.
It accepts a string and a hash of options:
start_count_at is the value from which to start numbering pages
total_pages If provided, will replace total with the value given. Useful for overriding the total
number of pages when using the start_count_at option.
page_filter, which is one of: :all, :odd, :even, an array, a range, or a Proc that receives the
page number as an argument and should return true if the page number should be printed on that
page.
color which accepts the same values as fill_color
As well as any option accepted by text_box
text"Thisisthefirstpage!"
10.timesdo
start_new_page
text"Herecomesyetanotherpage."
end
string="page<page>of<total>"
#Greenpagenumbers1to7
options={:at=>[bounds.right-150,0],
:width=>150,
:align=>:right,
:page_filter=>(1..7),
:start_count_at=>1,
:color=>"007700"}
number_pagesstring,options
#Graypagenumbersfrom8onup
options[:page_filter]=lambda{|pg|pg>7}
options[:start_count_at]=8
options[:color]="333333"
number_pagesstring,options
start_new_page
text"See.Thispageisn'tnumberedanddoesn'tcounttowardsthetotal."
This code snippet was not evaluated inline. You may see its output by running the example file located here:
http://github.com/prawnpdf/prawn/tree/master/manual/repeatable_content/page_numbering.rb
Security
Security lets you control who can read the document by defining a password.
The examples include:
How to encrypt the document without the need for a password
How to configure the regular user permitions
How to require a password for the regular user
How to set a owner password that bypass the document permissions
security/encryption.rb
The encrypt_document method, as you might have already guessed, is used to encrypt the
PDF document.
Once encrypted whoever is using the document will need the user password to read the
document. This password can be set with the :user_password option. If this is not set the
document will be encrypted but a password will not be needed to read the document.
There are some caveats when encrypting your PDFs. Be sure to read the source documentation
(you can find it here: https://github.com/prawnpdf/prawn/blob/master/lib/prawn/security.rb ) before
using this for anything super serious.
Bare encryption. No password needed. Simple password. All permissions granted.
#Bareencryption.Nopasswordneeded.
Prawn::ManualBuilder::Example.generate("bare_encryption.pdf")do
text"See,nopasswordwasaskedbutthedocumentisstillencrypted."
encrypt_document
end
#Simplepassword.Allpermissionsgranted.
Prawn::ManualBuilder::Example.generate("simple_password.pdf")do
text"Youwasaskedforapassword."
encrypt_document(:user_password=>'foo',:owner_password=>'bar')
end
This code snippet was not evaluated inline. You may see its output by running the example file located here:
http://github.com/prawnpdf/prawn/tree/master/manual/security/encryption.rb
security/permissions.rb
Some permissions may be set for the regular user with the following options: :print_document,
:modify_contents, :copy_contents, :modify_annotations. All this options default to
true, so if you'd like to revoke just set them to false.
A user may bypass all permissions if he provides the owner password which may be set with the
:owner_password option. This option may be set to :random so that users will never be able to
bypass permissions.
There are some caveats when encrypting your PDFs. Be sure to read the source documentation
(you can find it here: https://github.com/prawnpdf/prawn/blob/master/lib/prawn/security.rb ) before
using this for anything super serious.
User cannot print the document. All permissions revoked and owner password set to random
#Usercannotprintthedocument.
Prawn::ManualBuilder::Example.generate("cannot_print.pdf")do
text"Ifyouusedtheuserpasswordyouwon'tbeabletoprintthedoc."
encrypt_document(:user_password=>'foo',:owner_password=>'bar',
:permissions=>{:print_document=>false})
end
#Allpermissionsrevokedandownerpasswordsettorandom
Prawn::ManualBuilder::Example.generate("no_permissions.pdf")do
text"Youmayonlyviewthisandwon'tbeabletousetheownerpassword."
encrypt_document(:user_password=>'foo',:owner_password=>:random,
:permissions=>{:print_document=>false,
:modify_contents=>false,
:copy_contents=>false,
:modify_annotations=>false})
end
This code snippet was not evaluated inline. You may see its output by running the example file located here:
http://github.com/prawnpdf/prawn/tree/master/manual/security/permissions.rb

You might also like