Open Source im professionellen Einsatz
Linux-Magazin 11/2003

Serienbrief-Umschläge mit Perl und Postscript

Beschriftungs-Himmel

Um Kuverts für normale Postbriefe zu erstellen, braucht man weder ein Office-Paket noch Latex. Auch mit Perls Postscript-Modulen, einer Adressdatenbank und dem im Folgenden vorgestellten Skript lassen sich massenweise Umschläge beschriften.

1025

Mit Linux gelingt mittlerweile alles: digitale Bilder von der Kamera einlesen, digitale Musik hören, CDs brennen, sogar der USB-Scanner brummt zufrieden mit, wenn er von »xsane« seine Aufträge bekommt. Doch für eine Aufgabe musste ich bislang noch Windows booten: Um einmal im Monat etwa 20 Umschläge für einen Serienbrief mit Absender und Empfängeradressen aus einer Datenbank zu beschriften, nutzte ich bis vor kurzem noch ein Uraltprogramm aus der Windows-Welt. Schluss damit! Mit Ghostscript wird aus dem heimischen 08/15-Drucker schnell ein zwölfzylindriger Postscript-Bolide.

Falls die Distribution nicht schon dessen Einrichtung übernommen hat, zeigt[2] wie's geht. Die bislang unter Windows genutzte Adressdatenbank ließ sich, wie die Abbildung 1 zeigt, problemlos im Komma-separierten CSV-Format exportieren. Was zu tun blieb war, für jeden Briefumschlag eine Postscript-Datei zu generieren und diese dann an den Drucker zu schicken. Kinderleicht mit den beiden Modulen »PostScript::File« und »PostScript::TextBlock« vom CPAN, wie aus[3] zu erfahren. Postscript ist im Prinzip auch nur eine Programmiersprache. Die Postscript-Dateien bestehen aus lesbarem Ascii-Text, der die zur Erzeugung von Seiten notwendigen Kommandos aneinander reiht.

Malen nach Zahlen

Postscript arbeitet allerdings mit einem so genannten mathematischen Koordinatensystem, was für die Layout-Branche eher ungewöhnlich ist. Der Koordinatenursprung ist die linke untere Ecke des Papiers. Die x-Achse führt von dort aus nach rechts, die y-Achse zeigt nach oben, also genau so, wie man es noch aus der Schule gewöhnt ist. Maßeinheit ist der in der amerikanischen Typographie übliche Pica-Punkt oder auch Postscript-Point, ein Zweiundsiebzigstel Inch, das wiederum etwa 2,54 Zentimeter misst. 100 Postscript-Punkte entsprechen also ungefähr 3,5 Zentimetern.

Um zum Beispiel den Text »Max Schuster« ins Adressfeld des Briefumschlags zu platzieren, sind folgende Kommandos notwendig:

0 setgray 401.95 156 moveto
/Helvetica-iso findfont
18 scalefont setfont
(Max Schuster) show

Hierbei geht es zunächst fast 402 Points (etwa 14,2 Zentimeter) von der linken unteren Ecke des Umschlags nach rechts und dann 156 Points (etwa 5,5 Zentimeter) nach oben, um dann dort die angegebene Buchstabenkette im genannten Font (Helvetica-iso) und in der gesetzten Größe 18 von links nach rechts aufs Papier zu setzen.

Etwas vereinfacht wird dieses Kauderwelsch durch die vom CPAN erhältlichen Module »PostScript::File« und »PostScript::TextBlock«. Ersteres übernimmt die Aufgabe, den Postscript-Header, mit

%!PS-Adobe-3.0

beginnend, zu schreiben und sich um die Seitenorientierung, die Randmaße und die Seitenfolge zu kümmern. »PostScript::TextBlock« nimmt mehrzeilige Strings entgegen und fängt bei einer angegebenen Koordinate an zu schreiben. Allerdings verlangen diese Module stundenlanges Herumtüfteln, damit das Layout auch einigermaßen an der richtigen Stelle landet.

Für die Briefumschläge soll folgendes Layout gelten: Der Textblock mit dem Absender soll in beiden Richtungen jeweils zwei Zentimeter von der linken oberen Ecke des Kuverts entfernt beginnen. Der Textblock für das Adressfeld dagegen soll sich in x- wie in y-Richtung bis 2 Zentimeter vor die rechte untere Ecke des Umschlags erstrecken. Wir legen also nicht den Schreibanfang des Adressfelds fest, sondern die Lage der rechten unteren Ecke des Textblocks. Das gewährleistet, dass der Block unabhängig von der variablen Namens- oder Adressenlänge sauber rechts unten im Umschlag hängt.

Das Skript in Listing 1 definiert als Beispiel einen konstanten Absender in »$SENDER« (Zeile 14). Die Adressaten liest es aus einer Adressdatei und gibt zu jeder gefundenen Adresse einen Umschlag nach Abbildung 2 oder 3 auf dem Drucker aus.

Listing 1: »envelope«

001 #!/usr/bin/perl
002 ###########################################
003 # envelope - Print paper envelopes
004 # Mike Schilli, 2003 (m@perlmeister.com)
005 ###########################################
006 use warnings;
007 use strict;
008
009 use PostScript::File;
010 use PostScript::TextBlock;
011 use File::Temp qw(tempfile);
012
013 my $ADDR_CSV  = "mailaddr.csv";
014 my $SENDER    = q{Ansel Absender
015 Amselweg 9
016 D-78333 Ansbach};
017 my $PRINT_CMD = "lpr";
018
019 open FILE, $ADDR_CSV or
020     die "Cannot open $ADDR_CSV";
021
022 while(<FILE>) {
023   next if /^s*#/;
024   my @addr = split /,/, $_;
025   @addr = map { s/"//g; $_; } @addr;
026
027   my $ps = PostScript::File->new(
028     landscape   => 1,
029     reencode    => 'ISOLatin1Encoding',
030     paper       => "Envelope-DL",
031   );
032
033   my ($tmp_fh, $tmp_file) =
034                  tempfile(SUFFIX => ".ps");
035
036   my($last, $first, $city, $str) = @addr;
037
038     # Sender
039   my($bw, $bh, $b) = textbox($SENDER,
040                   "Helvetica-iso", 10, 12);
041   my ($code) = $b->Write($bw, $bh, cm(2),
042                  $ps->get_width() - cm(2));
043   $ps->add_to_page($code);
044
045     # Recipient
046   my $to = "$first $lastn$strnn$cityn";
047   ($bw, $bh, $b) = textbox($to,
048                   "Helvetica-iso", 18, 20);
049   ($code) = $b->Write($bw, $bh,
050            $ps->get_height() - $bw - cm(2),
051            $bh + cm(2));
052   $ps->add_to_page($code);
053
054     # Print to temporary file
055   (my $base = $tmp_file) =~ s/.ps$//;
056   $ps->output($base);
057
058     # Send to printer
059   system("$PRINT_CMD $tmp_file") and
060       die "$PRINT_CMD $tmp_file: $!";
061
062     # Delete
063   unlink "$tmp_file" or
064       die "Cannot unlink $tmp_file: $!";
065 }
066
067 ###########################################
068 sub textbox {
069 ###########################################
070     my($text, $font, $size, $leading) = @_;
071
072     my $b = PostScript::TextBlock->new();
073
074     $b->addText(
075         font    => $font,
076         text    => $text,
077         size    => $size,
078         leading => $leading);
079
080     return(tb_width($text, $font, $size),
081            tb_height($text, $leading),
082            $b);
083 }
084
085 ###########################################
086 sub cm {
087 ###########################################
088     return int($_[0]*72/2.54);
089 }
090
091 ###########################################
092 sub tb_width {
093 ###########################################
094     my($text, $font, $size) = @_;
095
096     $font =~ s/-iso//;
097
098     my $max_width = 0;
099
100     for(split /n/, $text) {
101         s/[äÄöÖüÜß]/A/ig;
102         my $w =
103           PostScript::Metrics::stringwidth(
104                          $_, $font, $size);
105         $max_width = $w if $w > $max_width;
106     }
107
108     return $max_width;
109 }
110
111 ###########################################
112 sub tb_height {
113 ###########################################
114     my($text, $leading) = @_;
115
116     my $lines = 1;
117     $lines++ for $text =~ /n/g;
118
119     return $lines*$leading;
120 }

Abbildung 2: Ob der Absender einen kurzen oder ...

Abbildung 3: ... einen langen Namen hat: Der seitliche Abstand bleibt konstant.

Import aus der Windows-Welt

Zeile 13 definiert in »$ADDR_CSV« den Namen der Adressdatei, die ein Format nach Abbildung 1 aufweisen sollte. Das Kommando, um eine Postscript-Datei durch den Drucker zu jagen, legt »$PRINT_CMD« in Zeile 17 fest. Wer das Skript nur im Trockenlauf ausprobieren möchte ohne gleich Tonnen von Papier zu produzieren, ersetze »"lpr"« einfach durch »"ghostview"«, dann erscheinen die Kuverts auf dem Bildschirm.

Abbildung 1: Die Felder der Komma-separierten Adressdatei.

Der Code in Zeile 19 öffnet die Adressdatei und der »while«-Block ab Zeile 22 iteriert durch die Einträge, die jeweils mittels regulärer Ausdrücke extrahiert werden. Stattdessen wäre auch der Einsatz des CPAN-Moduls »Text::CSV_XS« denkbar, aber da die Adresseinträge im vorliegenden Fall denkbar einfach sind und ohne Komplikationen wie Wörtliche-Rede-Anführungszeichen oder eingebettete Kommas auskommen, könnte das etwas übertrieben sein.

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Druck machen

    Bis das papierlose Büro Wirklichkeit wird, obliegt es dem Administrator, das Linux-Drucksystem einzurichten. Zwei Systeme aus zwei Generationen treten das Erbe Gutenbergs an.

  • Schädlingsbekämpfung

    Spammer senden nicht nur E-Mails, sie nisten sich auch in Diskussionsforen und Blogs ein, um mit Link-reichen Pseudopostings die großen Suchmaschinen hinters Licht zu führen. Ein Perl-Skript macht sauber.

  • Perl-Snapshot

    HTML 5 bringt Websockets, über die Webserver mit ihren Clients in einen Dialog treten können. Die im Folgenden vorgestellte kleine Webapplikation zeigt in Echtzeit im Browser, welche Seiten beliebige User von einem Webserver im Moment aufrufen.

  • Rund-Umschlag

    Wer einfache Aufgaben wie das Bedrucken von Briefkuverts nicht mit dem Office-Hammer lösen will, greift zu Perl und schreibt selbst ein passendes Programm. Weitere Themen: Programme mit Tcl steuern, Java mit KDE-Oberfläche ausstatten und C-Funktionsaufrufe beschleunigen.

  • Textarbeit

    Ob als Ascii-Editor oder für interaktive Anzeigen mit verschiedenen Schriften und Bildern - das Textwidget von Tk ist sehr flexibel einsetzbar. Diese Federlesen-Folge erklärt das Universalwidget anhand eines einfachen Editors und eines Readers für RSS-Dateien.

comments powered by Disqus

Ausgabe 09/2017

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.