15.4. Creating PDF Documents with PDF::WriterPDF::Writer is a library to create PDF documents in Ruby. It can be installed as a RubyGem or downloaded from RubyForge. Basic document creation is pretty simple: require 'rubygems' require 'pdf/writer' pdf = PDF::Writer.new 15.4.1. Basic Concepts and TechniquesOne of the primary issues facing any document designer is that of text fonts (or type-faces). PDF::Writer supports five basic fonts; the first three have bold and italic (or oblique) variants.
If no font is selected, Helvetica will be used by default. When selecting a font, a character substitution table can be created, allowing normally nonprintable characters or code pages to be simulated. There are 315 printable characters in Times-Roman, Helvetica, and Courier (149 have preassigned byte values); 190 printable characters in Symbol (189 have preassigned byte values); and 202 printable (and assigned) characters in ZapfDingbats. The fonts are encoded with an Adobe encoding, but individual characters may be reassigned during font selection. It is not currently possible to print all 315 characters that are defined in a font file because after a font is selected, a new character substitution table cannot be provided. This should be fixed in a future version of PDF::Writer. Here, we set the font for the PDF document we are creating to Times-Roman. The PDF reader will translate our text, which is assumed to be in WinAnsiEncoding, but it will substitute character 0x01 with the glyph called lozenge. We will see this character used later (in Listing 15.11). pdf.select_font "Times-Roman", { :encoding => "WinAnsiEncoding", :differences => {0x01 => "lozenge"} } PDF::Writer sports facilities to automatically format text and create tables that are well documented. What may not be as clear is that, as long as none of the automatic pagination is triggered, PDF::Writer can be used to manually format pages in interesting ways. With judicious axis translation and scaling, we can draw four pages of content on a single page. Under the current version of PDF::Writer (1.1.3), each of these "pages" must exactly fit one page. If the library's automatic pagination kicks in, a new physical page will be created. An enhanced version of this technique will be added to a future version PDF::Writer to work similarly to multicolumn support. To demonstrate this technique, we'll create a method quadrant (shown in Listing 15.10). This will be incorporated into the lengthy example in the upcoming section. (The purpose is twofold: It shows how to produce a 4-up document, and it allows us to display four PDF pages on a single page of this book, as a space-saving measure.) Listing 15.10. The quadrant Method
In this method, we force each page's construction to be completely contained within a block; in this way, we can transparently manage the drawing scale and axis without the wrapped page construction code knowing anything about it. The very first thing we do, though, is save the current state. This will save us from having to manually reset the axis scale and origin when we've finished. To prepare, we must move the origin point of the quadrant to the appropriate location on the drawing page (this is pdf.translate_axis x, y). Suppose I move the origin point from (0, 0) to (50, 50). A line from (15, 20) to (35, 40) will be effectively drawn from (65, 70) to (85, 90). But the code that draws the line does not need to know this. After translating the axis (moving the origin), the axis will be resized, effectively changing the size of the ruler that will be used. To achieve a quadrant effect, the axis is being scaled by half on the X scale and half on the Y scale (pdf.scale_axis 0.5, 0.5). This means that if I were now to draw my line from (0, 0) to (90, 90), it would effectively draw only from (0, 0) to (45, 45) without a translated axis, and from (90, 90) to (135, 135) with the translated axis. The code that drew the line still drew a 90-unit diagonal line. It just happens because of the rescaling that the units are half as big as they used to be. We then yield to the block; after the block has finished, we restore the state using a built-in feature. If we did not do this, we would need code similar to the following to restore the axis scale and origin. The scale would need to be doubled, and the origin would need to be moved by the negative value of the original amount that it had been moved. 15.4.2. An Example DocumentTo demonstrate the techniques we just discussed, four different pages will be created and drawn within individual quadrant blocks. Three of these are minor variants of demonstration programs provided with PDF::Writer:
The fourth (quadrant 2) is also a variant but does not have a direct analogue to any demonstration program; its closest variant is the program chunkybacon.rb. The entire code is in Listing 15.11, and the resulting output (a 4-up document page) is shown in Figure 15.4. This is a long listing; we will examine it in detail in the upcoming discussion. Listing 15.11. Code for the Example Document
Figure 15.4. Example document output (4-up).To summarize, the four quadrants are as follows:
Let's refer to the four pieces of the final page by abbreviating the quadrant name as UL, UR, LL, and LR. The code uses the corresponding symbol names (:ul and so on). The first quadrant (UL) is filled with vertical lines that progressively shrink from 40 units wide in progressively lighter shades. This is followed by circles that increase in radius as they reduce in line thickness, and the shades lighten. Finally, two sets of text are drawn: one in decreasing font sizes down the circles, and one with the text rotating around a central axis just off the edge of the vertical lines. In the second quadrant (UR), we insert an image into the page and describe it. Of particular interest, note the date line. We are inserting byte 0x01 into the output stream here; when rendered, a lozenge character (diamond) will be substituted, as defined in our substitution table when we selected our font. In the third quadrant (LL), the Individual-I demonstration program further demonstrates axis translation and scaling. The most interesting feature demonstrated here is axis inversion. When an axis is scaled by a negative value, the writing and drawing commands are inverted from the original direction. That means that in drawing the body of the "I", it's only necessary to define the drawing rules for half of the body; we can invert the X axis with pdf.scale_axis(-1, 1). This final quadrant (LR) is relatively simple. The text of President Lincoln's speech at Gettysburg is formatted and enclosed in a box with rounded corners. Saving the PDF at this point is simplicity itself. If we want to save it to disk, we simply use the save_as method on the PDF object: pdf.save_as("4page.pdf") It's also easy to send the PDF to a web browser from a CGI program: require 'cgi' cgi = CGI.new out = pdf.render puts <<-EOS Content-Type: application/pdf Content-Disposition: inline; filename="4page.pdf" Size: #{out.size} EOS Of course, this section has touched on only a fraction of the functionality of PDF::Writer. For more information, consult the online documentation. If you are familiar with the PDF format, be aware that PDF::Writer is not yet fully mature and does not support all features of the PDF specification. |