Version 1 of High-level wrapper for pdf4tcl

Updated 2009-10-27 08:03:39 by makr

Arjen Markus (26 october 2009) Having experimented a bit with the pdf4tcl package I found it a very useful package for generating PDF files, but there were too many details to take care of: the commands it offers are somewhat low-level. Luckily it is easy to wrap them into commands that hide the details.

While this is a very first, incomplete version of what I have in mind, I thought it might be useful enough to put it on the Wiki. The text I use in the example is simply nonsense, but it suffices for my testing purposes.

Note: I have used Snit to create the wrapper interface, as pdf4tcl itself uses Snit too. As it is my first serious use of Snit, I can probably improve on it.

package require snit
package require pdf4tcl

# document --
#     Snit type defining a document with high-level methods
#
::snit::type document {
    variable pdf
    variable yCoord
    variable lineHeight

    constructor {args} {

        #set pdf [::pdf4tcl::new %AUTO% {*}$args]
        set pdf [::pdf4tcl::new %AUTO%]
        $pdf configure -margin {5m 5m}

        set lineHeight [expr {1.3*12}]
    }

    method chapter {title} {

        $pdf startPage

        $pdf setFont 24 Helvetica-Bold
        set yCoord [$pdf getFontMetric ascend]
        $pdf setTextPosition 0 $yCoord
        $pdf text $title

        $pdf setFont 12 Times-Roman

        set yCoord 36
    }

    method section {title} {

        $pdf setFont 18 Times-Bold
        set lineHeight [expr {1.3*18}]

        $self paragraph $title

        $pdf setFont 12 Times-Roman
        set lineHeight [expr {1.3*12}]
    }

    method subsection {title} {

        $pdf setFont 14 Times-Italic
        set lineHeight [expr {1.3*14}]

        $self paragraph $title

        $pdf setFont 12 Times-Roman
        set lineHeight [expr {1.3*12}]
    }

    method paragraph {text {margin 0}} {

        set text [string map {\n " "} $text]

        foreach {pageWidth pageHeight} [$pdf getDrawableArea] {break}

        while { $text != "" } {
            set text [$pdf drawTextBox $margin $yCoord [expr {$pageWidth-$margin}] $lineHeight $text -align justify]
            set yCoord [expr {$yCoord + $lineHeight}]

            if { $yCoord > $pageHeight } {
                $pdf startPage
                set yCoord $lineHeight
            }
        }
        $pdf newLine
    }

    method newLine {} {

        set yCoord [expr {$yCoord + $lineHeight}]
    }

    method bullet {text} {

        set xcentre [expr {0.5 * $lineHeight}]
        set ycentre [expr {$yCoord + 0.3 * $lineHeight}]

        $pdf circle $xcentre $ycentre 2p -filled 1

        $self paragraph $text $lineHeight
    }

    method bullet2 {text} {

        set xcentre [expr {1.5 * $lineHeight}]
        set ycentre [expr {$yCoord + 0.3 * $lineHeight}]

        $pdf circle $xcentre $ycentre 1.2p -filled 1

        $self paragraph $text [expr {2.0*$lineHeight}]
    }

    method code {text} {

        $pdf setFont 10 Courier
        set lineHeight [expr {1.3 * 10}]
        set charWidth  [$pdf getCharWidth " "]

        foreach {pageWidth pageHeight} [$pdf getDrawableArea] {break}

        foreach line [split $text \n] {
            set margin [expr {$charWidth * ([string length [regexp -inline {^ *} $line]] - 2)}]
            $pdf drawTextBox $margin $yCoord [expr {$pageWidth-$margin}] $lineHeight $line
            set yCoord [expr {$yCoord + $lineHeight}]

            if { $yCoord > $pageHeight } {
                $pdf startPage
                set yCoord $lineHeight
            }
        }
        $pdf newLine

        $pdf setFont 12 Times-Roman
        set lineHeight [expr {1.3 * 12}]
    }

    method getpdf {} {

        return $pdf
    }

    method write {filename} {

        $pdf write -file $filename
        $pdf destroy
    }
}

document doc

doc chapter "Chapter 1"

doc paragraph "Some longish text that clearly wraps around the right margin of the
page, so that we can demonstrate the aligning behaviour of this command. And of course
how you put in a paragraph of text into the document, using a simple, high-level method."

doc section "Demonstration of a section"
doc subsection "Demonstration of a subsection"

doc paragraph "Does it do pages?"

for { set i 0 } { $i < 50 } { incr i } {
    doc paragraph "Line $i"
}

doc chapter "Chapter 2"
doc paragraph "Again some text - mostly for fun"
doc paragraph "What about two paragraphs?"

doc bullet "Bullet 1, but let's demonstrate the wrapping for this type of
text entity too. After all we do want it to be useful in a fairly general context"
doc newLine

doc bullet "Bullet 2"
doc bullet "Bullet 3"
doc bullet2 "Second level bullet - no more levels"

doc code {
What about writing some text in the style of "code"?
proc putTitle {string} {
    global currentY

    book setFont 24 Times-BoldItalic
    book text [string range $string 3 end-3]
    book setFont 12 Times-Roman

    set currentY [expr {$currentY + 24}]
}
}  ;# End of "doc code"

doc write "mydocument.pdf"