[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Groff] pdfmark strikes back + general script/groff handshake

From: Carlos J. G. Duarte
Subject: Re: [Groff] pdfmark strikes back + general script/groff handshake
Date: Mon, 15 Dec 2003 04:43:58 +0000
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.5) Gecko/20031014 Thunderbird/0.3

Hi again, on the weekend I recovered the solution proposed by Werner
LEMBERG to make HREF pdfmarks work correctly. WL proposed to have a
macro like this:

  .nr pshref-count 0
  .de PSHREF
  .  tmc pshref: \\n[pshref-count]-start: \"
  .  nop \&\O1\m[white]|\m[]\h'-\w'|'u'\O2\c
  .  tmc pshref: \\n[pshref-count]-end: \"
  .  ie !'\\$3'' \
  .    nop \\$3\O1\m[white]|\m[]\h'-\w'|'u'\O2\c
  .  el \
  .    nop \\$1\O1\m[white]|\m[]\h'-\w'|'u'\O2\c
  .  nr pshref-count +1

And then parsing its output to generate the inline PS code. There is
actually a minor problem with the above stuff: it was pretended to
output lines like this
pshref: 1-start: grohtml-info:page 1 ...
pshref: 1-end: grohtml-info...
but the output does not come in order, i.e. on the example I was trying
the "pshref: 1-start" kind of lines appear first all together and then
the "grohtml-info" lines.

I have simplified a bit the macro to the following:
.tm TM:pshref: \\$1

Then I have a perl script that takes this output, process it and feed
the results to the source again... Well, this is the general
script/groff handshake I was talking about on the subject. The general
idea is to have an iterative way of making documents. Each run takes the
results of the processed information and generates more information.
When the new information is equal to the previous, the build process
stops. Hmm... I'm having some trouble to explain this clearly, so I'm
leaving the source code that implements this stuff.

To make the xx.pdf from let this to be the Makefile:
T = xx
W = $(wildcard zp.out)

# no rules

$(T).pdf: $(T).me zp $(W)
./zp -in zp.out $(T).me | groff -me -Tps 2> zp.out0 |ps2pdf - - >$@
./zp -in zp.out0 -out zp.out

zp is the script that handles the TM: prefixed lines. It only writes
zp.out if existing zp.out differs from zp.out0. To make a build it is
just a matter of runnig make. Usually it takes two to four times until
it stops. The script zp runs in two modes:
-in -- loads the information file and the groff source, it then acts as
a filter changing or adding stuff to the original source on the fly;
-in -out: loads in; load out; compares them and if different writes into
out (and the make will see this last one changed, and then it will
restart the building process)

This process is general and can be used for other things as well, like
indexes, references and so on. The script I'm sending handles two cases
for now: this PSHREF junk and the total number of pages-- it collects
the total number of pages to allow one to produce a 1/5 2/5 3/5 etc...
page numbering.

I'm sending the following (small) files:
Makefile (as above)
zp (the marvelous script) (the PSHREF macro and some junk text to test its usage)

If someone finds this to be useful, feel free to use. Just a final note:
the PSHREF with this \O schemes actually HREF's the space following the
URL, for example, if I have this "... on you can
find" the space between ".org" and "you" also triggers the link, but I
can live with that ;-)


T = xx
W = $(wildcard zp.out)

# no rules

$(T).pdf: $(T).me zp $(W)
        ./zp -in zp.out $(T).me | groff -me -Tps 2> zp.out0 |ps2pdf - - >$@
        ./zp -in zp.out0 -out zp.out

        rm -f $(T).pdf zp.out zp.out0

Description: Troff ME-macros document

#! /usr/bin/perl
# Carlos Duarte, 031202

# usage:
#       -in file -out file
#               load input, load output, then compare both structs
#               if they differ, write in into out; else nothing
#       -in file
#               load input and apply struct into input

use strict;

# extracted info
my $npages;
my @pshref; 
my @pshref_pos; 

my $in_file; 
my $out_file;
O: while (defined (my $o = shift)) {
        $o eq "-out" and do {
                defined($o = shift) or usage();
                $out_file = $o;
                next O; 
        $o eq "-in" and do {
                defined($o = shift) or usage();
                $in_file = $o;
                next O; 
        $o =~ /^-/ and usage(); 
        unshift(@ARGV, $o); 
        last O; 
$in_file eq "" and usage(); 

if ($out_file ne "") {
        # -in f1 -out f2
        cmp_and_save($in_file, $out_file);
} else {
        # -in f
        while (<>) {
                if ($npages and s/^\.nr Npages .*//) {
                        $_ = ".nr Npages " . $npages;
                        undef $npages; 
                if (@pshref and /^\.PSHREF/) {
                        # output PDF marks
                        my $name = shift @pshref; 
                        # @pshref_pos: x y x y | x y x y 
                        my $x0 = $pshref_pos[0];
                        my $y0 = $pshref_pos[1];
                        my $x1 = $pshref_pos[6];
                        my $y1 = $pshref_pos[7];
                        splice @pshref_pos, 0, 8; 
                        pshref_output($name, $x0, $y0, $x1, $y1);
                print $_, "\n";

sub load {
        my $fn = shift; 
        local ($.,$_,*F);
        open F, $fn or return;
        while (<F>) {
                s/^grohtml-info:/TM:grohtml-info:/; # special case for these one
                s/^TM:\s*// or do {
                        print STDERR;
                s/^(.*?):// or do { bad($fn,$.,"bad line: $_"); next; };
                my $name = $1; 
                $name eq "NPAGES" and do { $npages = $_; next; };
                $name eq "pshref" and do { push @pshref, $_;  next; }; 
                $name eq "grohtml-info" and do { 
                        my @a = split " ", $_; 
                        push @pshref_pos, $a[2], $a[3], $a[4], $a[5]; 

                bad($fn,$.,"$name: unrecognized tag"); 
        close F; 

sub bad {
        print STDERR join(':',@_); 
        address@hidden =~ /\n$/s or print STDERR "\n"; 

sub cmp_and_save {
        my $in = shift;
        my $out = shift; 

        my @in_data;
        my @out_data; 

        local ($.,$_,*F);
        if (open F, $in) {
                while (<F>) {
                        if (/^TM:/ or /^grohtml-info:/) {
                                push @in_data, $_; 
                close F; 
        if (open F, $out) {
                while (<F>) {
                        if (/^TM:/ or /^grohtml-info:/) {
                                push @out_data, $_; 
                close F; 
        if ("@in_data" ne "@out_data") {
                open F, ">$out" or die "$out: $!"; 
                for (@in_data) {
                        print F;
                close F; 
                touch_with_time(time+1, $out); # for make to work

sub touch_with_time {
        my $now = shift;
        foreach my $file (@_) {
                utime ($now, $now, $file) or die "Couldn't touch file: $!\n"; 

sub usage {
        print STDERR "$0 -in file -out file\n";
        print STDERR "$0 -in file \n";
        exit 1; 

sub pshref_output {
        my $name = shift; 
        my $x0 = shift; 
        my $y0 = shift; 
        my $x1 = shift; 
        my $y1 = shift; 
\\X'ps: exec [ /Rect [$x0 u $y0 u $x1 u $y1 u] \\
/Border [0 0 0] /Action << /Subtype /URI /URI ($name) >> \\
/Subtype /Link /ANN pdfmark'\\c

reply via email to

[Prev in Thread] Current Thread [Next in Thread]