pdf4tcl

pdf4tcl , originally by Frank Richter, Jens Pönisch and other parties, now maintained by Peter Spjuth, is a Tcl package for generating PDF files. It started as a port of pdf4php to the tcl language.

Attributes

name
pdf4tcl
latest release
0.9.4
release time
2021-01-25

See Also

images2pdf
A utility to create a pdf document from a bunch of images. Uses pdf4tcl.
pdflib
Trampoline!
TCLFPDF
features unicode auto text and links.

Description

Peter Spjuth 2009-01-11: Pdf4Tcl is now a reasonably complete package, except for handling fonts and encodings outside the built-ins, which I think is a big "except" for a PDF generator. I would welcome any help to fill this gap since I don't have the knowledge or the time currently.

YS 2009-08-24: I've added support for embedding TrueType and Type1 fonts. New version sent to Peter Spjuth.


SRIV 2009-01-12: I almost finished modifying pdf4tcl to use TclOO instead of snit. I'll benchmark the speed when I'm done, then send you a patch against v0.5 if it looks good.

AMG: Where is this code?


AM 2009-10-26: Here is a High-level wrapper for pdf4tcl that allows you to create documents with almost no concern for low-level details, such as putting in new pages.

RA: 2010-02-27: Are there any examples where this is used as an intermediate step to print to paper?

AM 2010-03-01: I am using it to generate a PDF file from a Wiki-like text format and as I hate reading large quantities of text from the screen, I send to the printer for reviewing (at the moment other things take up my time, but the principle works very nicely).


chd: Very, very nice package I was waiting so long for - now printing under Windows is simple :-) I have three suggestions: I have seen that there is now a putRawImage command - good :-) A small goodie would be a direct way to insert PNG, GIF, BMP like JPEG. A command to get the high of the current text line would be nice to have the option to exactly set a "tabulator" after a newLine command. Last but not least: calculating points is annoying. A command "setUnit mm/inch/points" would help a lot. (btw: I'm using pdf4tcl for my bookkeeping software to create invoices/lists/address labels - now I don't need LaTeX anymore :-)

Peter Spjuth 2008-02-06: Could you please put these into the Feature Request Tracker? As always, patches are welcome too :-) .

chd 2008-02-06: Done - I hope I will have the time (and skill! ;-) to submit some patches soon :-)


Sarnold: I have used pdf4tcl in a small tool to display some kind of report. To me it was a better way than canvas exporting (as Encapsulated PostScript). MHo: I'm using it in my photoprinter-tool, see Matthias Hoffmann - PhotoPrinter. We had exchanged emails about that months ago. Unfortunately, the tweaks you mailed me have not found their way into a new version....

jnc: Has anyone done anything more with pdf4tcl? I just got done creating a table layout process where you can easily create tables, split them across pages, repeat headers, etc... It's still very rough code. I was wondering what's been done with pdf4tcl before I go much further.

anoved: I used pdf4tcl to create strpdf [L1 ], a tool I use to generate pages with short positioned annotations.

anoved: What's new in 0.3? Peter Spjuth: I have added a changes list to the home page. Also, the SVN history starts with my 0.2 import so all changes can be seen in the history there.


MHo 2008-02-01: Just started my first experiments with v0.3:

  • When generating large amounts of data, the finish method blocks the gui / program. It seems that data is not written out to the -file specified with new before entering finish. Are there any intermediate flushes or updates within finish???
  • Worst than that, the generated pdf is - in my case - unreadable by pdf readers such as FoxitReader... I didn't change fundamental logic in my code between v0.2 and v0.3.
  • After examining the old and new source code, I wonder if perhaps there is missing some fconfigure ... binary (within write, there is one)???
  • A binary compare of my output files show some differences, one use \x0D, the other (new one) \x0D0A

Peter Spjuth: There was a fconfigure missing that was fixed a few days ago. Try the latest from SVN and see if it helps. Things should continuously be written to the file, except for channel buffering, while creating pages. Fonts and images are dumped in finish so a lot of those could slow that step down.

MHo: The PDF unfortunately almost only contains lots and lots of images (JPGs), because it's a photo album... I already tested the latest version :-(

Peter Spjuth: Does the :-( imply that it still gave corrupt data or that it still was slow? If the former, file a bug report on the tracker. If the latter, I don't know. I guess it might be possible to dump images in endPage thus distributing the load on the different calls, but I do not understand the image handling code enough to change that. Patches are welcome.

MHo: Yes, the :-( means data corruption - will post a detailed bug report if I find the time later...


Peter Spjuth 2008-02-27: The development version now has support for dumping a canvas to PDF. Anyone interested is welcome to test and provide feedback.

Bryan Oakley 2008-02-27: my first attempt didn't work. I created a canvas, dumped some text on it, then did this:

  % pdf4tcl::new mypdf -paper a4
  ::mypdf
  % mypdf canvas .c
  526
  % mypdf write -file canvas.pdf
  can't read "pdf(xref,4)": no such element in array

This is from the version on the trunk of the svn repository.

Peter Spjuth: You need an explicit mypdf startPage call after creation. That is a bug, but should be enough as a workaround.

Jeff Godfrey 2008-02-27: Yep, using the above workaround, the canvas output seems to work for me - even for a relatively complex canvas. Nice work!

Bryan Oakley a few moments later: Nice! Thanks. Now all I need is a way to replicate a text widget on a canvas and I'm all set. Good work, Peter.

Peter Spjuth: It is probably just as easy to replicate the text widget with pdf4tcl's primitives directly. This is roughly how printing is done in Eskil. Taking the route around the canvas will probably be harder to get right. Unfortunately the text item of the canvas is currently the one pdf4tcl has hardest to get right due to font limitations.


See http://pdf4tcl.berlios.de/tkdemo.pdf for the result on dumping goldberg and items canvases from Tk's demo.

KPV: I notice several errors in rendering TkGoldberg.

  1. match box stippling is off
  2. string holding weight too short
  3. string has some off-by-one alignment issues
  4. weight's upper left shoulder not rounded
  5. ball hopper upper left corner not rounded
  6. ball hopper down tube walls thickness doesn't match
  7. three arcs by the boat are upside down
  8. yellow down tube has one arc drawn on top when it should be hidden below
  9. curved part of the gray water pipe is all messed up
  10. cat's face mis-drawn

On the other hand, the anti-aliased lines look much better--compare slingshot, bucket, balloon, on/off switch, etc.

Peter Spjuth: Thanks for the feedback.

  • 1 Looks to be some rounding artifact in the viewer. Zooming in, the patterns look OK.
  • 2 was a bug in 2-point spline line. Fixed.
  • 3: If you mean the vertical line up to middle pulley it is off by one in the original. It's just clearer in the PDF.
  • 4 and 5 was a bug in polygon with spline. Fixed.
  • 6: I don't understand what is meant.
  • 7, 8, 9, 10 was that arcs were flipped in y. Fixed.

Googie 2012-07-29: I needed method that would tell me what would be height of textBox if I have some text and width on input, before I actually draw the textBox. I took drawTextBox method and modified it to create getTextBoxHeight method:

method getTextBoxHeight {width txt} {
    $self Trans  0 0 x y
    $self TransR $width 0 width height

    if {! $pdf(font_set)} {
        $self SetupFont
    }

    # pre-calculate some values
    set font_height [expr {$pdf(font_size) * $pdf(line_spacing)}]
    set space_width [$self getCharWidth " " 1]

    # Displace y to put the first line within the box
    set bboxb [$self getFontMetric bboxb 1]
    set ystart $y
    set y [expr {$y - $pdf(font_size) - $bboxb}]

    set len [string length $txt]

    # run through chars until we reach end
    set start 0
    set pos 0
    set cwidth 0
    set lastbp 0
    set done false
    set req_height 0

    while {! $done} {
        set ch [string index $txt $pos]
        # test for breakable character
        if {[regexp "\[ \t\r\n-\]" $ch]} {
            set lastbp $pos
        }
        set w [$self getCharWidth $ch 1]
        if {($cwidth+$w)>$width || $pos>=$len || $ch=="\n"} {
            if {$pos>=$len} {
                set done true
            } else {
                # backtrack to last breakpoint
                if {$lastbp != $start} {
                    set pos $lastbp
                } else {
                    # Word longer than line.
                    # Back up one char if possible
                    if {$pos > $start} {
                        incr pos -1
                    }
                }
            }
            set sent [string trim [string range $txt $start $pos]]

            # Move y down to next line
            set y [expr {$y-$font_height}]

            set start $pos
            incr start
            set cwidth 0
            set lastbp $start

            set req_height [expr {$ystart - $y - $font_height}]
        } else {
            set cwidth [expr {$cwidth+$w}]
        }
        incr pos
    }
    return $req_height
}

I'm not sure if it's entirely correct - I didn't go deep into details. Feel free to fix it. I think it would be nice if it could get into official package.


escargo 2008-02-28: How does this compare to tclhpdf [L2 ]?


Googie 2012-08-05: Anyone had any luck with generating pdf with language-specific characters using this package? I tried with custom TTF font to get right charset, but even the font is right, the pdf created is still wrong :( I tried couple of different approaches, here's one:

set fontName "someFont"
pdf4tcl::loadBaseTrueTypeFont Base$fontName font.ttf
pdf4tcl::createFont Base$fontName $fontName iso8859-2

Using the above I still cannot get Polish characters. Tried the same with "unicode" encoding, "utf-8", "cp1250", "identity". None of them worked. Any ideas?

YS: Sure. First: publish full examples. ;) This works for me:

set fontName "someFont"
pdf4tcl::loadBaseTrueTypeFont Base$fontName Arial.ttf
pdf4tcl::createFont Base$fontName $fontName iso8859-2
pdf4tcl::new PDFobj -compress 1 -paper a4 -unit mm
PDFobj setFont 16 $fontName
PDFobj text "\u0104\u0106\u0118\u0141\u0143\u015A\u0179\u017B" -x 20 -y 20
PDFobj write -file exppolishfont.pdf 
PDFobj destroy

Googie 2012-08-06: Now it works. I don't know what I was doing wrong before. Thanks!


Googie 2012-08-07: I just tried to generate PDF with several different encodings applied for TTF font and this is what happend when tried "identity" encoding (the default encoding picked by my Tcl):

expected integer but got ""
    while executing
"format "<%02X> <%04X>\n" $f $uchar"
    (procedure "MakeToUnicodeCMap" line 19)
    invoked from within
"MakeToUnicodeCMap $BaseFN  $::pdf4tcl::FontsAttrs($fontname,uniset)"
    (procedure "::pdf4tcl::pdf4tcl::Snit_methodSetupFont" line 52)
    invoked from within
"$self SetupFont"
    (procedure "::pdf4tcl::pdf4tcl::Snit_methodtext" line 52)
    invoked from within
"PDFobj text "\u0104\u0106\u0118\u0141\u0143\u015A\u0179\u017B" -x 20 -y 20"

How can I fix it?

YS: I've already told you: publish full examples. I won't be able to help otherwise. BTW, why don't you ask your question in comp.lang.tcl?

Googie: Full code snippet:

set fontName "someFont"
pdf4tcl::loadBaseTrueTypeFont Base$fontName Arial.ttf
pdf4tcl::createFont Base$fontName $fontName identity
pdf4tcl::new PDFobj -compress 0 -paper a4 -unit mm
PDFobj setFont 16 $fontName
PDFobj text "\u0104\u0106\u0118\u0141\u0143\u015A\u0179\u017B" -x 20 -y 20
PDFobj write -file exppolishfont.pdf 
PDFobj destroy

I don't have a specific reason why I ask here. It just happens ;)

YS: Citing from "encoding system" page on this wiki: "The identity encoding is for testing purposes, it should not be used without very good reasons." So you shouldn't, too. It seems that it's missing chars in range 0x00-0xFF entirely, so resulting font subset is empty. The meaning of pdf4tcl::createFont is actually to make font subset from all 256 chars in given encoding (as PDF actually uses 8-bit chars inside). So, if you need, for example, to use chars from different languages in PDF document, you can:

a) Create many font subsets (using createFont) and use appropriate font for printing chars from each language, for example:

set fontName "someFont"
pdf4tcl::loadBaseTrueTypeFont Base$fontName Arial.ttf
pdf4tcl::createFont Base$fontName PolishFont iso8859-2
pdf4tcl::createFont Base$fontName RussianFont cp1251 

pdf4tcl::new PDFobj -compress 0 -paper a4 -unit mm
PDFobj setFont 16 PolishFont 
PDFobj text "\u0104\u0106\u0118\u0141\u0143\u015A\u0179\u017B" -x 20 -y 20
PDFobj setFont 16 RussianFont
PDFobj text  "\u042D\u0442\u043E \u0442\u0435\u043A\u0441\u0442" -x 20 -y 50
PDFobj write -file exp2fonts.pdf 
PDFobj destroy

b) Create custom font subset including all chars you need (it can't include more than 256 chars):

set fontName "someFont"
pdf4tcl::loadBaseTrueTypeFont Base$fontName Arial.ttf
foreach c [list \u0104 \u0106 \u0118 \u0141 \u0143 \u015A \u0179 \u017B \u0441 \u0442 \u0435 \u043A \u042D \u043E \u0020] {
   lappend subset [scan $c %c]
   }
pdf4tcl::createFontSpecEnc Base$fontName CombinedFont $subset 

pdf4tcl::new PDFobj -compress 0 -paper a4 -unit mm
PDFobj setFont 16 CombinedFont 
PDFobj text "\u0104\u0106\u0118\u0141\u0143\u015A\u0179\u017B" -x 20 -y 20
PDFobj text "\u042D\u0442\u043E \u0442\u0435\u043A\u0441\u0442" -x 20 -y 50
PDFobj write -file exp2fonts.pdf 
PDFobj destroy

But beware, second example displays incorrectly in Adobe Reader 7.0.5 at my PC (must be reader's bug, works OK in foxit reader).

Googie: Thanks Yaroslav! I think that clarifies the subject entirely ;)

Chinese font example on Windows

HaO2024-02-28: I had the task to output a one page PDF report on MS-Windows with US-ASCII and Chinese contents.

Windows Fonts

First, a font must be found which supports those characters. It is not buildin in PDF.

There are two default fonts in Windows featuring cp936 character set: "YaHei" and "YaHei bold". They are normally installed in "C:\Windows\Fonts\MSYH.TTC" and "C:\Windows\Fonts\MSYHBD.TTC".

Codepage "cp936" covers Latin1, 2, Cyrillic, Greek, Turkish and Chinese simplified Chinese for PRC and Singapore. It is available as TCL encoding, so one may test, if a character is covered by:

if {$Char ne "?" && [encoding convertto cp936 $Char] eq "?"} {

The "no problem" font for Unicode "Arial Unicode MT" is only installed with the MS-Office package and is thus not usable in general.

Build custom font

A list of characters should be set-up with the required characters. I use the following routine:

proc FilterCP936 {StrIn} {
    upvar CharacterList CharacterList
    for {set Pos 0} {$Pos < [string length $StrIn]} {incr Pos} {
        set Char [string index $StrIn $Pos]
        if {"?" ne $Char && [encoding convertto cp936 $Char] eq "?"} {
            set StrIn [string range $StrIn 0 $Pos-1]?[string\
                    range $StrIn $Pos+1 end]
        }
        set Code [scan $Char %c]
        # Filter control characters
        if {    $Code >= 32
                && -1 == [lsearch -integer -sorted $CharacterList $Code]
        } {
            lappend CharacterList $Code
            set CharacterList [lsort -integer $CharacterList]
        }
    }
    return $StrIn
}

Then, all strings are passed by this routine as a preprocessing step.

set CharacterList {}

set Text1 [FilterCP936 "扫描所有必读代码。
代码错误。
请打印并重新开始验证。
All mandatory codes scanned.
Code error present.
Please print and start new verification."]

set Text2 [FilterCP936 "条形码类型\nBarcode Type"]

WARNING: It should be made sure, that there are less than 257 items in the character list.

Then, the custom font may be created:

pdf4tcl::loadBaseTrueTypeFont BaseYaHei {C:\Windows\Fonts\MSYH.TTC}
pdf4tcl::createFontSpecEnc BaseYaHei CombinedYaHei $CharacterList

Create PDF

Now, create the pdf:

pdf4tcl::new PDFobj -compress 0 -paper a4 -unit mm
PDFobj startPage
PDFobj setFont 9 CombinedYaHei
PDFobj drawTextBox 20 20 160 200 $Text1
PDFobj drawTextBox 20 220 160 200 $Text2
PDFobj write -file exp2fonts.pdf 
PDFobj destroy

Any improvement proposals welcome !