[comp.lang.postscript] Wanted: Postscript general tree display

davew@ux.acs.umn.edu (David B. Walters) (01/24/91)

	I am looking for a program that will take a general
tree structure and output a postscript graph of the tree.

/--------------------------------------------------------------------\
|David B. Walters	                Alias:  Arastin              |   
|University of Minnesota             Internet:  davew@ux.acs.umn.edu |
|Mathematics Computer Lab (the Zoo)  Office #:  (612) 625-0801       | 
|4 Vincent Hall; 206 Church Street.S.E.,                             |
|Minneapolis, MN 55455    UUCP: rutgers!umn-cs!ux.acss.umn.edu!davew |
\--------------------------------------------------------------------/

aas@aase.nr.no (Gisle Aas) (01/25/91)

In article <3105@ux.acs.umn.edu> davew@ux.acs.umn.edu (David B. Walters) writes:

>	   I am looking for a program that will take a general
>   tree structure and output a postscript graph of the tree.

You should be able to adjust this "unix-file-hierarchy-tree" program
to your needs. It is written in the perl language, which ought to be
present at any decent computer installation.

The file is also suitable input to nroff/troff for manual page
formatting.


----- cut here --------------------------------------
#!/usr/local/bin/perl
'di';
'ig00';
# pstree(1) - produce directory map in PostScript.
# $Id: pstree,v 1.1 90/11/21 12:22:15 aas Exp $

sub PAGE_TOP          { 842; }   # A4 height = 842, letter height = 792
#sub PAGE_RIGHT_EDGE   { 595; }
sub TB_MARGIN         { 50; }
sub LEFT_MARGIN       { 60; }
sub FONT              { "Helvetica"; }
sub FONT_SIZE         { 20; }
sub DIR_LEVEL_INDENT  { &FONT_SIZE * 9; }

$y = &PAGE_TOP - &TB_MARGIN;
$prev_level = 0;
$average_char_width = &FONT_SIZE / 2;
$max_x_pos = 0;  # keep track of it in order produce bounding box

open(tmp,"+>/tmp/tree$$") || die "Can't create temporary file";
unlink("/tmp/tree$$");
select(tmp);

print  "/s {show} bind def\n";
print  "/m {moveto} bind def\n";
printf "/%s findfont %d scalefont setfont\n",&FONT,&FONT_SIZE;
print  "0.1 setlinewidth\n";

push(@ARGV,'.');
if ($ARGV[0] =~ /^-/) {
   $_ = shift;
   last if (/^--$/);
   if (/f/) {
      $list_files = 1;
   }
   else {
      print STDERR "Usage: $0 [-f] [dirname]\n";
      exit(1);
   }
}
&list_dir($ARGV[0],0);

print "showpage\n";
seek(tmp,0,0); # rewind the temporary file

select(STDOUT);
print "%!PS-Adobe-2.0 EPSF-2.0\n";
print "%%Title: (Directory map of $ARGV[0])\n";
print "%%Creator: pstree, (C) 1990 Gisle Aas, NR\n";
printf "%%%%DocumentFonts: %s\n", &FONT;

if ($y < &TB_MARGIN) {
   $page_size = (&PAGE_TOP - 2 * &TB_MARGIN);
   $scale_factor = ($page_size)/((&PAGE_TOP - &TB_MARGIN ) - $y);
   printf "%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
       &LEFT_MARGIN, &TB_MARGIN + &FONT_SIZE * $scale_factor,
       &LEFT_MARGIN + $max_x_pos * $scale_factor,
       &PAGE_TOP - &TB_MARGIN + &FONT_SIZE * $scale_factor;
   printf "%.1f %.3f translate\n", &LEFT_MARGIN,
                                   (-$y)*$scale_factor + &TB_MARGIN;
   printf "%.5f dup scale\n", $scale_factor;
} else {
   printf "%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
       &LEFT_MARGIN, $y + &FONT_SIZE,
       &LEFT_MARGIN + $max_x_pos,
       &PAGE_TOP - &TB_MARGIN + &FONT_SIZE;
   printf "%.1f 0 translate\n", &LEFT_MARGIN;
};

# copy temporary file to standard out
while (<tmp>) {
   print;
}
exit;

#------------------------------------------


sub list_dir
{
   local($dirname) = shift;
   local($level) = shift;
   local(@content);
   opendir(d,$dirname);
   @content = sort(grep(!/^\.\.?$/,readdir(d)));
   closedir(d);
   while ($file = shift(@content)) {
      $file = "$dirname/$file";
      if (-d $file) {
	 if (-l $file) {     # symbolic link; do not follow these
	    &emitt(substr($file,rindex($file,'/')+1,14) . " -> " .
		  readlink($file), $level + 1);
	 }
	 else {
            &list_dir($file,$level+1);
         }
      }
      elsif ($list_files) {
	 &emitt(substr($file,rindex($file,'/')+1,14), $level+1);
      }
   }
   &emitt(substr($dirname,rindex($dirname,'/')+1,14), $level);
}

# Uses the following global variables:
#    $y          : current vertical position (initial value = 'top of page')
#    $prev_level : the level reportet last time on emit (init value = 0)
#    @top        : current top position at different levels
#    @bottom     : current bottom position at different levels
#    @pos        : string of positions at different levels
sub emitt
{
   local($text) = shift;
   local($level) = shift;

   # Do some substitutions on the $text so that it can be used as a
   # PostScript string constant.
   $text =~ s/[\\\(\)]/\\$&/g;

   if ($level == $prev_level) {
      &write($level,$y,$text);
      $pos[$level] .= " $y";
      $bottom[$level] = $y;
      $y -= &FONT_SIZE;
   }
   elsif ($level > $prev_level) {
      &write($level,$y,$text);
      local($i);
      for ($i=$prev_level+1;$i<$level;$i++) {
          $pos[$i] = '';
      }
      $pos[$level] = "$y";
      $top[$level] = $y;
      $bottom[$level] = $y;
      $y -= &FONT_SIZE;
   }
   elsif ($level == ($prev_level - 1)) {
      local($ypos) = ($top[$level+1] - $bottom[$level+1]) / 2 + 
                     $bottom[$level+1];
      &write($level,$ypos,$text);
      &lines($level,$ypos,$pos[$level+1],$text);
      if ($pos[$level]) {
         $pos[$level] .= " $ypos";
         $bottom[$level] = $ypos;
      }
      else {
         $pos[$level] = "$ypos";
         $top[$level] = $ypos;
         $bottom[$level] = $ypos;
      }
   }
   else {
      die "Humm..., jump from level $prev_level to level $level";
   }
   $prev_level = $level;
}

sub write
{
   local($x,$y,$text) = @_;
   $x = $x * &DIR_LEVEL_INDENT;
   printf "%.f %.f m(%s)s\n", $x, $y, $text;
   $x += length($text) * $average_char_width;
   $max_x_pos = $x if ($x > $max_x_pos);
}

sub lines
{
   local($x,$y,$to,$text) = @_;
   local(@to) = split(/ /,$to);
   $x = $x * &DIR_LEVEL_INDENT;
   $y += &FONT_SIZE/3;
   printf "($text) stringwidth pop %.1f add %.1f m\n",$x+1,$y;
   printf "[";
   for (@to) { printf " %.1f", $_ + &FONT_SIZE/3; }
   printf "]\n";
   printf "{gsave %.1f exch lineto stroke grestore} forall\n",
          $x + &DIR_LEVEL_INDENT - 4;
}

###########################################################################
	# These next few lines are legal in both Perl and nroff.

.00;			# finish .ig
 
'di			\" finish diversion--previous line must be blank
.nr nl 0-1		\" fake up transition to first page again
.nr % 0			\" start at page 1
';<<'.ex'; #__END__ #### From here on it's a standard manual page #########
.TH PSTREE 1 "November 1990"
.SH NAME
pstree \- produce directory map in PostScript
.SH SYNOPSIS
.B pstree
[
.B \-f
] [
.I dirname
]
.SH DESCRIPTION
The output from this program is a PostScript program that will
produce a "map" of the directory tree from the current directory
and down. If a 
.I dirname
is given the directory map from the given
directory and down is produced.
The output conforms to Adobe's 
document structuring conventions (version 2.1), and the EPSF
specification version 2.0.
.SH OPTIONS
.TP 5
.B \-f
Include ordinary files in the map. Without this flag only
the overall directory structure is shown.
.SH SEE ALSO
.BR find (1),
.BR ls (1),
.BR perl(1),
.BR postscript(7)
.SH BUGS
Pstree truncates all directory names to 14 characters.
The image is not scaled down if it overflows the right edge of the page.
.SH AUTHOR
(C) Gisle Aas, Norwegian Computing Centre (NR), 1990. <Gisle.Aas@nr.no>
.SH NOTES
PostScript is a trademark of Adobe Systems, Incorporated.
Perl is written by Larry Wall and is distributed under the
terms of the GNU General Public License.
.ex

--
Gisle Aas               |  snail: Boks 114 Blindern, N-0314 Oslo, Norway
Norsk Regnesentral      |  X.400: G=Gisle;S=Aas;O=nr;P=uninett;C=no
voice: +47-2-453561     |  inet:  Gisle.Aas@nr.no