First steps with HPDF
I’ve been fiddling with HPDF for a new project, and ran into a bit of a documentation barrier. So i’m writing down some notes, which may some day lead to a better-documented, higher-level HPDF wrapper.
1 I’ll be using HPDF v1.4.2, hot off of
hackage.
First off, let’s write a very minimal pdf1.
module Main where import Graphics.PDF main :: IO() main = do runPdf "mydemo.pdf" standardDocInfo (PDFRect 0 0 612 792) $ do page1 <- addPage Nothing drawWithPage page1 $ do drawText $ do setFont (PDFFont Times_Roman 12) textStart 36.0 396.0 displayText (toPDFString "the quick brown fox jumps over the lazy dog")
That’s a lot of nested do‘s. Step-by-step:
runPdfis the big output function. It takes a filename, aPDFDocumentInfo, aPDFRectwhich specifies the page size, and a PDF action.- A
PDFDocumentInfogives you access to document-level info, including author and subject, but also PDF-spec stuff likepageModeand whether the document is compressed. See the PDF spec for more info on this structure, or just usestandardDocInfo. PDFRectis used in a bunch of places, but here it’s for the default paper size for your document. The numbers are points (assuming 72 points per inch), so this document is sized like a standard US Letter page (8.5“x11”). PDF coordinates are traditional cartesian (i.e., 0,0 is bottom-left, not top-left like a computer screen). We could conceivably start our page at (-20,-20) or (70,30) instead of (0,0), but doing so would make the rest of the process a pain in the butt. So let’s not.
- A
- The
PDFmonad describes the PDF actions we want to encode in the document. We just want to print a single line of text, but to do so requires some effort.- First, we create a page to play with, using
addPage. The argument toaddPageis either a page size for that page, orNothingif we’re using the document default (set above to be Letter size). drawWithPagetakes a bunch of drawing commands, and writes them to the provided page, returning the wholePDFmonad. The drawing commands themselves are pretty straightforward (and imperative).- In order to write text, we need to
setFont(which takes a font defined in Graphics.PDF.Text - We don’t technically need to set a starting point, but if we don’t, we’ll start printing at (0.0,0.0), which we probably don’t want to do (go ahead and try, you’ll see what i mean).
- Finally,
displayTextdoes what it claims (after we convert to thePDFStringtype)
- In order to write text, we need to
- First, we create a page to play with, using
Phew. Unfortunately, this is pretty verbose, and not terribly flexible. But it’s enough to write some wrappers and abstractions around. First, let’s make paper sizes look like paper sizes.
data PaperSize = PaperSize Int Int letter = PaperSize 612 792 a4 = PaperSize 595 841 a5 = PaperSize 419 595 landscape (PaperSize x y) = PaperSize y x toRect (PaperSize x y) = PDFRect 0 0 x y
Now let’s make it easier to stick text on a page.
writeOnPageAt p x y font string = drawWithPage p $ do drawText $ do setFont font textStart x y displayText (toPDFString string)
All that doesn’t seem like much, but allows us to start doing things like this:
main = do runPdf "mydemo.pdf" standardDocInfo (toRect $ landscape letter) $ do p <- addPage Nothing let header = (PDFFont Helvetica 36) bullet = (PDFFont Helvetica 18) writeOnPageAt p 36.0 540.0 header "HPDF is Powerful" writeOnPageAt p 108.0 468.0 bullet "- Low-level access to PDF primitives" writeOnPageAt p 108.0 396.0 bullet "- The power of Haskell" writeOnPageAt p 108.0 324.0 bullet "- A recipe for success"
Which is a nontrivial step forward. Still, we’re a long way from general usefulness. For one, our text doesn’t actually wrap yet (go ahead and try a long string—it will run right off the page). For another, i’d rather not have to specify the start point of every string. Paragraphs and bounding boxes would be nice.
Fortunately, HPDF provides some help here, in the form of its Container system. I’ll explore that a bit next time.
By john on July 7, 2009
Comments
No comments posted yet. Go for it!
Comments currently disabled due to impressive comment-spam efforts.