chiark / gitweb /
segment labelling work-in-progress - yet to do are labels and graph colouring
[trains.git] / layout / layout
index 5e1a01b2f5531ca083b6f200501a30d93d06c00e..c8df20a3c5cf69d0cface18d9f424afecad0c696 100755 (executable)
@@ -7,6 +7,9 @@ use IO::File;
 use strict;
 no strict 'subs';
 
+our $file_lineno= 0;
+our $file_filename;
+
 our $scale= 7.0;
 our $page_x= 0;
 our $page_y= 0;
@@ -14,6 +17,8 @@ our $quiet=0;
 our $debug=0;
 our $output_layer= '*';
 our $subsegcmapreq=0;
+our $subsegmovfeatpos='f';
+our $subsegcmapangscale;
 
 our $ps_page_shift= 615;
 our $ps_page_xmul= 765.354;
@@ -21,17 +26,21 @@ our $ps_page_ymul= 538.583;
 
 our @eopts;
 our @segments= ('/');
+our @ident_strings= ();
 our %subsegcmap;
-
-our $drawers= 'arsclmnog';
-our %chdraw_emap= qw(A ARScg
-                    R aRscg
-                    S aRScg
-                    C arsCg
-                    c Arscg
+our %segcmap;
+
+our $drawers= 'arqscldmnog';
+our %chdraw_emap= qw(A ARScgd
+                    R aRscgD
+                    S aRScgd
+                    C arsCgd
+                    c Arscgd
                     r arcs
                     L LMg
                     l l
+                    D D
+                    d d
                     M Mnog
                     N MNog
                     O MNOg
@@ -51,9 +60,10 @@ while (@ARGV && $ARGV[0] =~ m/^\-/) {
        elsif (s/^S([0-9.]+)$//) { $scale= $1 * 1.0; }
        elsif (s/^P(\d+)x(\d+)$//) { $page_x= $1; $page_y= $2; }
        elsif (s/^GR//) { $subsegcmapreq=1; }
+       elsif (s/^GP(\d+|f)$//) { $subsegmovfeatpos=$1; }
        elsif (s/^GL(.*)$//) {
            my ($sscmfn) = $1;
-           my ($sscmf);
+           my ($sscmf, $datum, $csss, $angbits);
            local ($_);
            $sscmf= new IO::File $sscmfn, 'r'
                or die "$sscmfn: cannot open: $!\n";
@@ -61,9 +71,21 @@ while (@ARGV && $ARGV[0] =~ m/^\-/) {
                $!=0; $_= <$sscmf>; die $! unless defined $_;
                last if m/^E/;
                next unless m/^C/;
-               m,^C\s+(\w*/(?:[A-Za-z_]+\d+)?)\s+(\S.*\S)\s*$,
+               m,^C\s+(\w*/(?:[A-Za-z_]+)?)\s+(0x[0-9a-f]+)\s+(\d+)\s*$,
                    or die "$sscmfn:$.: syntax error in subseg cmap\n";
-               $subsegcmap{$1}= $2;
+               ($csss,$datum,$angbits)= ($1,$2,$3);
+               if (!defined $subsegcmapangscale) {
+                   $subsegcmapangscale= 1<<$angbits;
+               } else {
+                   die "angbits varies" if $subsegcmapangscale != 1<<$angbits;
+               }
+               $datum= hex($datum);
+               if ($datum & 0x0ff) {
+                   die "sorry, cannot put any movfeatpos or segment in red";
+               }
+               $subsegcmap{$csss}= sprintf("%.6f %.6f",
+                                           (($datum >> 8) & 0xff)/255.0,
+                                           (($datum >> 16) & 0xff)/255.0);
            }
            $sscmf->error and die "$sscmfn: error reading: $!\n";
            close $sscmf;
@@ -142,7 +164,10 @@ our $psu_sleeperlen= 17;
 our $psu_sleeperlw= 15;
 our $psu_raillw= 1.0;
 our $psu_thinlw= 1.0;
-our $psu_subseglw= 10.0;
+our %psu_subseglw;
+$psu_subseglw{'e'}= 20.0;
+$psu_subseglw{'m'}= 15.0;
+$psu_subseglw{'q'}= 20.0;
 
 our $lmu_marklw= 4;
 our $lmu_marktpt= 11;
@@ -151,10 +176,12 @@ our $lmu_txtboxh= $lmu_marktpt * 1.100;
 our $lmu_txtboxpadx= $lmu_marktpt * 0.335;
 our $lmu_txtboxoff= $lmu_marklw / 2;
 our $lmu_txtboxlw= 1;
+our $lmu_lenlabeloffctr= -$lmu_marklw * 1.0;
+our $lmu_lenlabeloff=     $lmu_marklw * 0.5;
 
 our $olu_left= 10 * $scale;
 our $olu_right= 217 * $scale - $olu_left;
-our $olu_bottom= 20 * $scale;
+our $olu_bottom= 25 * $scale;
 our $olu_top= 270 * $scale - $olu_bottom;
 our $olu_gap_x= 30;
 our $olu_gap_y= 60;
@@ -179,6 +206,7 @@ our $allwidthmin= allwidth(undef);
 # Data structures:
 #  $ctx->{CmdLog}= undef                  } not in defobj
 #  $ctx->{CmdLog}[]= [ command args ]     } in defobj
+#  $ctx->{Parent}= $parent_ctx or undef
 #  $ctx->{LocsMade}[]{Id}= $id
 #  $ctx->{LocsMade}[]{Neg}= 1 or 0
 #  $ctx->{Loc}{$id}{X}
@@ -198,6 +226,8 @@ our $allwidthmin= allwidth(undef);
 #  $ctx->{SegName}        # initial segment name (at start of object or file)
 #                         #  or nonexistent if in object in unknown segment
 #                         #  may have leading `-'
+#  $ctx->{SegMapN}{$s}= $o
+#  $ctx->{SegMapNM}{$s}= $o
 #  $ctx->{SavedSegment}   # exists iff segment command used, is a $csss
 #  $ctx->{Layer}{Level}
 #  $ctx->{Layer}{Kind}
@@ -223,7 +253,7 @@ our @al; # current cmd
 our $o='';
 our $ol='';
 
-our $param; # for parametric_curve
+our $param; # for parametric_segment
 
 # ev_... functions
 #
@@ -412,18 +442,6 @@ sub cva_idstr ($) {
     die "invalid id" unless $sp =~ m/^[a-z][_0-9A-Za-z]*$/;
     return $&;
 }
-sub cva_subsegspec ($) {
-    my ($sp)=@_;
-    die "invalid subsegment spec" unless
-       $sp =~ m,^(\-?)([0-9A-Za-z_]*)(?:/(?:([A-Za-z_]+)(\d+))?)?$,;
-    my ($sign,$segname,$movfeat,$movconf)=($1,$2,$3,$4);
-    $segname= exists $ctx->{SegName} ?
-       $sign.$ctx->{SegName}.$segname
-           : '';
-    $segname =~ s/^\-(.*)\-/$1/;
-    return $segname.'/'.
-       (defined $movfeat ? sprintf "%s%d", $movfeat, $movconf : '');
-}
 sub cva_idex ($) {
     my ($sp)=@_;
     my ($id,$r,$d,$k,$neg,$na,$obj_id,$vflip,$locs);
@@ -558,6 +576,7 @@ sub o ($) { $o .= $_[0]; }
 sub ol ($) { $ol .= $_[0]; }
 sub oflushpage () {
     return if $subsegcmapreq;
+
     print $o, $ol, "  showpage\n"
        or die $!;
     $o=$ol='';
@@ -617,24 +636,74 @@ sub parametric__o_pt ($) {
     o_path_point("$pt->{X} $pt->{Y}");
 }
 
-sub segment_used_len ($) {
-    my ($used) = @_;
+our $segused_incurrent;
+our $segused_currentpt;
+our $segmentpart_counter=0;
+our $segused_restorecounter;
+
+sub segment_used__print ($) {
+    my ($pt) = @_;
+    if ($segused_incurrent > 0 && $segused_restorecounter==1) {
+       o("%L segmentpart ".
+         $segmentpart_counter++." ".
+         $ctx->{Layer}{Level}.$ctx->{Layer}{Kind}." ".
+         $segments[0]." ".
+         $segused_incurrent." ".
+         loc2dbg($segused_currentpt)." ".
+         loc2dbg($pt)."\n");
+    }
+    $segused_incurrent= undef;
+    $segused_currentpt= undef;
+}
+    
+sub segment_used__len ($$) {
+    my ($used,$pt) = @_;
+    $segused_incurrent += $used;
+
     return if @segments < 3;
     $segments[1] -= $used;
     return if $segments[1] > 0;
+
+    segment_used__print($pt);
+    segment_used_begin($pt);
+
     @segments= @segments[2..$#segments];
     o("% segments @segments\n");
 }
     
+sub segment_state_save () {
+    return [ 0, $segused_incurrent, $segused_currentpt,
+            $segmentpart_counter, @segments ];
+}
+sub segment_state_restore ($) {
+    my ($r) = @_;
+    ($segused_restorecounter, $segused_incurrent, $segused_currentpt,
+     $segmentpart_counter, @segments) = @$r;
+    $r->[0]++;
+}
+
+sub segment_used_begin ($) {
+    $segused_incurrent= 0;
+    $segused_currentpt= $_[0];
+}
+sub segment_used_middle ($$) {
+    my ($used,$pt) = @_;
+    segment_used__len($used,$pt);
+}
+sub segment_used_end ($$) {
+    my ($used,$pt) = @_;
+    segment_used__len($used,$pt);
+    segment_used__print($pt);
+}
 sub parametric_segment ($$$$$) {
     my ($p0,$p1,$lenperp,$minradius,$calcfn) = @_;
-    # makes $p (global) go from $p0 to $p1  ($p1>$p0)
+    # makes $param (global) go from $p0 to $p1  ($p1>$p0)
     # $lenperp is the length of one unit p, ie the curve
     # must have a uniform `density' in parameter space
-    # $calcfn is invoked with $p set and should return a loc
+    # $calcfn is invoked with $param set and should return a loc
     # (ie, ref to X =>, Y =>, A =>).
     my ($pa,$pb,@ends,$side,$ppu,$e,$v,$tick,$draw,$allwidth);
-    return unless $ctx->{Draw} =~ m/[ARSCG]/;
+    return unless $ctx->{Draw} =~ m/[ARSCGQ]/;
     $ppu= $psu_ulen/$lenperp;
     $allwidth= allwidth($minradius);
     my ($railctr)=($psu_gauge + $psu_raillw)*0.5;
@@ -644,48 +713,74 @@ sub parametric_segment ($$$$$) {
     my ($sleeperend)=($psu_sleeperlen*0.5);
 print DEBUG "ps $p0 $p1 $lenperp ($ppu)\n";
     $draw= current_draw();
-    if ($draw =~ m/G/) {
-       my ($pt,$going,$red,$csegbare);
-       $going=0;
-       o("% segments @segments\n");
-       o("    $psu_subseglw setlinewidth\n");
-       $param=$p0;
-       $pt= &$calcfn;
-       for (;;) {
-           $csegbare= $segments[0];
-           $csegbare =~ s/^\-//;
-           if ($subsegcmapreq) {
-               if (!exists $subsegcmap{$csegbare}) {
-                   print "$csegbare\n" or die $!;
-                   $subsegcmap{$csegbare}++;
+    if ($draw =~ m/[QG]/) {
+       my ($pt,$going,$red,$csegbare,$movfeat,$movstroke);
+       my ($used_last,$me,$segsave);
+       o("gsave\n");
+       $segsave= segment_state_save();
+       foreach $me ($draw =~ m/Q/ ? qw(q) : qw(e m)) {
+           segment_state_restore($segsave);
+           $going=0;
+           o("% segments @segments\n");
+           $param=$p0;
+           $pt= &$calcfn;
+           segment_used_begin($pt);
+           for (;;) {
+               $movstroke= "      cmapreq-stroke\n";
+               $csegbare= $segments[0];
+               $csegbare =~ s/^\-//;
+               if ($subsegcmapreq) {
+                   if (!exists $subsegcmap{$csegbare}) {
+                       print "$csegbare\n" or die $!;
+                       $subsegcmap{$csegbare}++;
+                   }
+               } else {
+                   if ($draw =~ m/Q/) {
+                       $csegbare =~ m,^[^/]*,;
+#print STDERR "looking for \`$&' $me\n";
+                       $movstroke= $segcmap{$&};
+                       $movstroke= "%     no-colour    "
+                           unless defined $movstroke;
+                   } elsif ($draw =~ m/G/) {
+                       $movfeat= $csegbare =~ s,(/\D+)(\d+)$,$1, ? $2 : 'f';
+                       die "unknown subsegment colour for $csegbare\n"
+                           unless exists $subsegcmap{$csegbare};
+                       $red= $pt->{A} / (2*$pi);
+                       $red *= $subsegcmapangscale;
+                       $red += $subsegcmapangscale*2;
+                       $red += $subsegcmapangscale/2
+                           if $segments[0] =~ m/^\-/;
+                       $red %= $subsegcmapangscale;
+                       $red += $subsegcmapangscale if $me eq 'e';
+                       $red= sprintf("%f", $red / 255.0);
+                       $movstroke= "    $red $subsegcmap{$csegbare}".
+                                   " setrgbcolor\n";
+                       if ($subsegmovfeatpos ne $movfeat ||
+                           ($me eq 'e' && $csegbare =~ m,^/,)) {
+                           $movstroke= "%     no-stroke    ";
+                       }
+                   }
+                   $movstroke .=
+                       "    $psu_subseglw{$me} setlinewidth stroke\n";
                }
-           } elsif (exists $subsegcmap{$csegbare}) {
-               $red= $pt->{A} / (2*$pi);
-               $red *= 64;
-               $red += 128;
-               $red += 32 if $segments[0] =~ m/^\-/;
-               $red %= 64;
-               $red <<= 2;
-               $red /= 255.0;
-               $red= sprintf("%f", $red);
-               o("    $red $subsegcmap{$csegbare} setrgbcolor\n");
-           } else {
-               die "unknown subsegment colour for $csegbare\n";
+               o_path_begin();
+               parametric__o_pt($pt);
+
+               $param += $ppu;
+               last if $param>=$p1;
+               $pt= &$calcfn;
+               segment_used_middle($psu_ulen,$pt);
+               parametric__o_pt($pt);
+               o($movstroke);
            }
-           o_path_begin();
-           parametric__o_pt($pt);
-
-           $param += $ppu;
-           last if $param>=$p1;
-           segment_used_len($psu_ulen);
+           $used_last= $p1-($param-$ppu);
+           $param=$p1;
            $pt= &$calcfn;
+           segment_used_end($used_last * $lenperp, $pt);
            parametric__o_pt($pt);
-           o_path_strokeonly();
+           o($movstroke);
        }
-       segment_used_len(($p1-($param-$ppu)) * $lenperp);
-       $param=$p1;
-       parametric__o_pt(&$calcfn);
-       o_path_strokeonly();
+       o("grestore\n");
     }
     if ($draw =~ m/C/) {
        my ($pt);
@@ -698,8 +793,7 @@ print DEBUG "ps $p0 $p1 $lenperp ($ppu)\n";
        parametric__o_pt(&$calcfn);
        o("      stroke\n");
     }
-    return unless $draw =~ m/[ARS]/;
-    for ($pa= $p0; $pa<$p1; $pa=$pb) {
+    if ($draw =~ m/[ARS]/) { for ($pa= $p0; $pa<$p1; $pa=$pb) {
        $pb= $pa + $ppu;
        $param= $pa; $ends[0]= @ends ? $ends[1] : &$calcfn;
        $param= $pb; $ends[1]= &$calcfn;
@@ -739,7 +833,22 @@ print DEBUG "ps $p0 $p1 $lenperp ($ppu)\n";
            }
        }
        o("      grestore\n");
-    }
+    } }
+    if ($draw =~ m/D/) {
+       my ($pt,$ad,$len,$off);
+       $param= ($p0+$p1)*0.5;
+       $pt= &$calcfn;
+       $ad= ang2deg($pt->{A});
+       $len= sprintf "%.0f", $lenperp * abs($p1-$p0);
+       $off= $draw =~ m/C/ ? $lmu_lenlabeloff : $lmu_lenlabeloffctr;
+       ol("      gsave\n".
+          "        $pt->{X} $pt->{Y} translate\n".
+          "        $ad rotate\n".
+          "        lf setfont\n".
+          "        0 $off moveto\n".
+          "        ($len) show\n".
+          "      grestore\n");
+    }    
 }
 
 sub arc ($$$$$) {
@@ -987,7 +1096,10 @@ sub cmd_join {
 
 sub line ($$$) {
     my ($from,$to,$len) = @_;
-    parametric_segment(0.0, 1.0, abs($len) + 1e-6, undef, sub {
+    if ($len < 0) {
+       ($from,$to,$len) = ($to,$from,-$len);
+    }
+    parametric_segment(0.0, 1.0, $len + 1e-6, undef, sub {
        ev_lincomb({}, $from, $to, $param);
     });
 }
@@ -1079,10 +1191,14 @@ sub newctx (;$) {
     $ctx= {
        Trans => { X => 0.0, Y => 0.0, A => 0.0, R => 1.0 },
        InRunObj => "",
-       DrawMap => sub { $_[0]; }
+       DrawMap => sub { $_[0]; },
+       SegMapN => { },
+       SegMapNM => { }
        };
-    %{ $ctx->{Layer} }= %{ $ctx_save->{Layer} }
-        if defined $ctx_save;
+    if (defined $ctx_save) {
+       %{ $ctx->{Layer} }= %{ $ctx_save->{Layer} };
+       $ctx->{Parent}= $ctx_save;
+    }
 }
 
 our $defobj_save;
@@ -1131,6 +1247,48 @@ sub cmd__runobj ($) {
     }
 }
 
+sub cva_segment ($) {
+    my ($sp)=@_;
+    die "invalid segment" if $sp =~ m/[^0-9A-Za-z_]/;
+    return $sp;
+}
+
+sub cva_subsegspec ($) {
+    my ($sp)=@_;
+    die "invalid subsegment spec" unless
+       $sp =~ m,^(\-?)([0-9A-Za-z_]*)(?:/(?:([A-Za-z_]+)(\d+))?)?$,;
+    my ($sign,$segname,$movfeat,$movconf)=($1,$2,$3,$4);
+
+    if (!exists $ctx->{SegName}) {
+       $segname= '';
+       $sign= '';
+    } else {
+       my ($map_ctx);
+       
+       $ctx->{SegName} =~ m/^\-?/ or die;
+       $sign .= $&;
+       $segname= $'.$segname;
+       
+       for ($map_ctx= $ctx;
+            defined $map_ctx;
+            $map_ctx= $map_ctx->{Parent}) {
+           if (defined $movfeat &&
+               exists $map_ctx->{SegMapNM}{"$segname/$movfeat"}) {
+               $movfeat= $map_ctx->{SegMapNM}{"$segname/$movfeat"};
+           }
+           if (exists $map_ctx->{SegMapN}{$segname}) {
+               $map_ctx->{SegMapN}{$segname} =~ m/^\-?/ or die;
+               $sign .= $&;
+               $segname= $';
+           }
+       }
+       $sign =~ s/\-\-//g;
+    }
+
+    return $sign.$segname.'/'.
+       (defined $movfeat ? sprintf "%s%d", $movfeat, $movconf : '');
+}
+
 sub cmd_segment {
     my ($csss,$length);
     $ctx->{SavedSegment}= pop @segments
@@ -1145,6 +1303,48 @@ sub cmd_segment {
     push @segments, $csss;
 }
 
+sub cva_segmap_s {
+    my ($sp) = @_;
+    $sp =~ m,^\w+(?:/[a-zA-Z_]+)?$,
+        or die "invalid (sub)segment mapping S \`$sp'";
+    return $sp;
+}
+
+sub cva_segmap_n {
+    my ($sp) = @_;
+    $sp =~ m,^\-?\w+$, or die "invalid segment mapping N' \`$sp'";
+    return $sp;
+}
+    
+sub cva_segmap_m {
+    my ($sp) = @_;
+    $sp =~ m,^[a-zA-Z_]+$, or die "invalid segment mapping M' \`$sp'";
+    return $sp;
+}
+    
+sub cmd_segmap {
+    my ($s,$d);
+    while (@al) {
+       $s= can(\&cva_segmap_s);
+       if ($s =~ m,/,) {
+           $ctx->{SegMapNM}{$s}= can(\&cva_segmap_m);
+       } else {
+           $ctx->{SegMapN}{$s}= can(\&cva_segmap_n);
+       }
+    }
+}
+
+sub cmd_segend {
+    @al=();
+};
+
+sub cmd_segcmap {
+    my ($seg,$colour);
+    $seg= can(\&cva_segment);
+    $segcmap{$seg}= "@al";
+    @al= ();
+};
+
 sub layer_draw ($$) {
     my ($k,$l) = @_;
     my ($eo,$cc, $r);
@@ -1226,7 +1426,9 @@ sub cmd__obj ($) {
     } else {
        $ctx->{InRunObj}= $ctx_save->{InRunObj}."${obj_id}::";
     }
-    $ctx->{SegName}= $1 if $segments[0] =~ m,([^-]+)/,;
+    if ($segments[0] =~ m,(.*[^-]+)/,) {
+       $ctx->{SegName}= $1;
+    }
     $ctx->{DrawMap}= sub {
        my ($i) = @_;
        $i= &{ $ctx_save->{DrawMap} }($i);
@@ -1331,6 +1533,23 @@ dv("cmd__do $ctx @al ",'$ctx',$ctx);
     }
 }
 
+sub cmd_ident {
+    my ($vs, @lt, $inf, $strft);
+    $vs= "@al";
+    $vs= $1 if $vs =~ m/^\$Revision\: ([0-9.]+)\ \$$/;
+    if (!defined $file_filename) {
+       $inf= "$vs (unknown file: $file_lineno)";
+    } elsif (!stat $file_filename ||
+            !(@lt= localtime((stat _)[9]))) {
+       $inf= "$file_filename ($1 $!)";
+    } else {
+       $strft= strftime "%Y-%m-%d %H:%M:%S +%Z", @lt;
+       $inf= "$file_filename ($1 $strft)";
+    }
+    push @ident_strings, $inf;
+    @al= ();
+}
+
 sub cmd_showlibrary {
     my ($obj_id, $y, $x, $ctx_save, $width, $height);
     my ($max_x, $min_x, $max_y, $min_y, $nxty, $obj, $loc, $pat, $got, $glob);
@@ -1405,7 +1624,8 @@ sub cmd__one {
 
 o("%!\n".
   "  /lf /Courier-New findfont $lmu_marktpt scalefont def\n".
-  "  $ps_page_shift 0 translate 90 rotate\n");
+  "  $ps_page_shift 0 translate 90 rotate\n".
+  "  gsave\n");
 
 if ($page_x || $page_y) {
     o("  /Courier-New findfont 15 scalefont setfont\n".
@@ -1431,6 +1651,14 @@ $ctx->{SegName}= '';
 cmd__one();
 
 while (<>) {
+    $file_lineno++;
+    if (m/^\#line (\d+)$/) { $file_lineno= $1; next; }
+    if (m/^\#line (\d+) (.*)$/) {
+       $file_lineno= $1;
+       $file_filename= $2;
+       $file_filename =~ s/^\"(.*)\"$/$1/;
+       next;
+    }
     next if m/^\s*\#/;
     chomp; s/^\s+//; s/\s+$//;
     @al= split /\s+/, $_;
@@ -1459,24 +1687,32 @@ while (<>) {
     if ($scale < 1.5) {
        my ($tick_x, $tick_y, $ticklen);
        $ticklen= 10;
-       printf("    gsave 0.5 setgray 0.33 setlinewidth\n".
-              "      /regmark {\n".
-              "        newpath moveto\n".
-              "        -%d 0 rmoveto %d 0 rlineto\n".
-              "        -%d -%d rmoveto 0 %d rlineto stroke\n".
-              "      } def\n",
-              $ticklen, $ticklen*2, $ticklen, $ticklen, $ticklen*2)
-           or die $!;
+       o(sprintf
+         "    gsave 0.5 setgray 0.33 setlinewidth\n".
+         "      /regmark {\n".
+         "        newpath moveto\n".
+         "        -%d 0 rmoveto %d 0 rlineto\n".
+         "        -%d -%d rmoveto 0 %d rlineto stroke\n".
+         "      } def\n",
+         $ticklen, $ticklen*2, $ticklen, $ticklen, $ticklen*2);
        for ($tick_x= $min_x; $tick_x < $max_x; $tick_x += 150) {
            for ($tick_y= $min_y; $tick_y < $max_y; $tick_y += 150) {
-               printf("      %f %f regmark\n",
-                      $tick_x, $tick_y)
-                   or die $!;
+               o(sprintf "      %f %f regmark\n", $tick_x, $tick_y);
            }
        }
-       printf("    grestore\n")
-           or die $!;
+       o("    grestore\n");
     }
 }
 
+ol("grestore\n");
+
+if (@ident_strings) {
+    my ($is);
+    $is= join('; ', @ident_strings);
+    $is =~ s/[()\\]/\\$&/g;
+    ol("25 50 moveto".
+       "/Courier-New findfont 6 scalefont setfont\n".
+       " ($is) show\n");
+}
+
 oflushpage();