#!/usr/bin/perl -w
#
# Format ImageMagick comments into POD-format or HTML format
# documentation
# Produces *.pod or *.html files corresponding to *.c files
#
# Written by Bob Friesenhahn, April 1997
#

$opt_format='html';
$opt_srcdir='';
$opt_outdir='';

use Getopt::Long;
if ( ! GetOptions(
                  'format=s'	=> \$opt_format,
                  'srcdir=s'	=> \$opt_srcdir,
                  'outdir=s'	=> \$opt_outdir,
                 )
   ) {
  print("Usage: fmtdocs [-srcdir srcdir] [-outdir outdir] [-format format] \n");
  exit(1);
}

#
# Source files to use
#
@srcs = ('animate.c',
	 'annotate.c',
	 'attribute.c',
	 'blob.c',
	 'cache.c',
	 'cache_view.c',
	 'color.c',
	 'composite.c',
	 'constitute.c',
	 'decorate.c',
         'deprecate.c',
	 'render.c',
	 'draw.c',
	 'display.c',
	 'effect.c',
	 'enhance.c',
	 'error.c',
	 'fx.c',
	 'image.c',
	 'list.c',
	 'magick.c',
	 'memory.c',
	 'monitor.c',
	 'montage.c',
         'paint.c',
         'profile.c',
	 'quantize.c',
	 'registry.c',
         'resource.c',
	 'segment.c',
	 'shear.c',
	 'signature.c',
	 'stream.c',
	 'transform.c',
	 'resize.c',
         'widget.c');

$tmpname_pre_format = "/tmp/fmtdocs_pre.$$";
$tmpname_pod = "/tmp/fmtdocs_pod.$$";
$tmpname_html = "/tmp/fmtdocs_html.$$";

#@srcs = ('draw.c');

#
# What is for source files
#
%whatis =
(
 'animate',	'Interactively animate an image sequence',
 'annotate',	'Annotate an image with text',
 'attribute',	'Access key,value image attributes',
 'blob',	'Read or write formatted images in memory (BLOBs)',
 'color',	'Color related functions',
 'constitute',	'Read or write an image',
 'composite',	'Merge image pixels using a specified algorithm',
 'decorate',	'Add decorative frames and borders',
 'deprecate',	'Methods which should no longer be used',
 'display',	'Interactively display and edit an image',
 'render',	'Low-level methods to draw on an image',
 'draw',	'User-friendly methods to draw on an image',
 'effect',	'Image effects methods',
 'fx',		'Image special effects methods',
 'enhance',	'Methods to enhance or adjust an image',
 'error',	'Error reporting methods',
 'image',	'Miscellaneous image methods',
 'list',	'Image list support',
 'cache',	'Low-level access to image pixels',
 'cache_view',	'Low-level access to image pixels with multiple views',
 'magick',	'Image format support interfaces',
 'memory',	'Memory allocation/deallocation functions',
 'monitor',	'Progress monitor support',
 'montage',	'Create a thumbnail image mosaic',
 'paint',	'Methods to fill image pixel regions',
 'profile',     'Manipulate embedded profiles',
 'quantize',	'Reduce the number of colors in an image',
 'registry',	'In-memory image registration interface',
 'resource',	'Set resource consumption limits (e.g. memory)',
 'segment',	'Segment and image with thresholding using the fuzzy c-means method',
 'shear',	'Rotate image, shear image, or apply a 2D affine transformation',
 'signature',	'Add a digital signature to the image',
 'stream',	'Low-level image pixel FIFO (tap into encode/decode pixel stream)',
 'transform',	'Crop, flip, flop, roll, coalesce, etc.',
 'resize',	'Resize an image',
 'widget',	'X11 Widgets'
);

#
# Key words to replace with HTML links
#
my %keywords =
  (
   AffineMatrix		=> 'types.html#AffineMatrix',
   BlobInfo		=> 'types.html#BlobInfo',
   Cache		=> 'types.html#Cache',
   ChannelType		=> 'types.html#ChannelType',
   ChromaticityInfo	=> 'types.html#ChromaticityInfo',
   ClassType		=> 'types.html#ClassType',
   ClipPathUnits	=> 'types.html#ClipPathUnits',
   ColorPacket		=> 'types.html#ColorPacket',
   ColorspaceType	=> 'types.html#ColorspaceType',
   ComplianceType	=> 'types.html#ComplianceType',
   CompositeOperator	=> 'types.html#CompositeOperator',
   CompressionType	=> 'types.html#CompressionType',
   DecorationType	=> 'types.html#DecorationType',
   DrawContext		=> 'types.html#DrawContext',
   DrawInfo		=> 'types.html#DrawInfo',
   ErrorHandler		=> 'types.html#ErrorHandler',
   ExceptionInfo	=> 'types.html#ExceptionInfo',
   ExceptionType	=> 'types.html#ExceptionType',
   FillRule		=> 'types.html#FillRule',
   FilterTypes		=> 'types.html#FilterTypes',
   FrameInfo		=> 'types.html#FrameInfo',
   GravityType		=> 'types.html#GravityType',
   Image		=> 'types.html#Image',
   ImageInfo		=> 'types.html#ImageInfo',
   ImageType		=> 'types.html#ImageType',
   InterlaceType	=> 'types.html#InterlaceType',
   LayerType		=> 'types.html#LayerType',
   MagickInfo		=> 'types.html#MagickInfo',
   MonitorHandler	=> 'types.html#MonitorHandler',
   MontageInfo		=> 'types.html#MontageInfo',
   NoiseType		=> 'types.html#NoiseType',
   PaintMethod		=> 'types.html#PaintMethod',
   PixelPacket		=> 'types.html#PixelPacket',
   PointInfo		=> 'types.html#PointInfo',
   ProfileInfo		=> 'types.html#ProfileInfo',
   QuantizeInfo		=> 'types.html#QuantizeInfo',
   Quantum		=> 'types.html#Quantum',
   QuantumType		=> 'types.html#QuantumType',
   RectangleInfo	=> 'types.html#RectangleInfo',
   RegistryType		=> 'types.html#RegistryType',
   RenderingIntent	=> 'types.html#RenderingIntent',
   ResolutionType	=> 'types.html#ResolutionType',
   ResourceType		=> 'types.html#ResourceType',
   SegmentInfo		=> 'types.html#SegmentInfo',
   SignatureInfo	=> 'types.html#SignatureInfo',
   StorageType		=> 'types.html#StorageType',
   StreamHandler	=> 'types.html#StreamHandler',
   StretchType		=> 'types.html#StretchType',
   StyleType		=> 'types.html#StyleType',
   TypeMetric		=> 'types.html#TypeMetric',
   ViewInfo		=> 'types.html#ViewInfo',
   VirtualPixelMethod	=> 'types.html#VirtualPixelMethod',
   XResourceInfo	=> 'types.html#XResourceInfo',
);


foreach $src (@srcs) {

  my($out,$command);

  # Compute POD name
  ($base = $src) =~ s/\.[^\.]*$//g;

  $out = "${base}.${opt_format}";
  if ("${opt_outdir}" ne "") {
    $out = "${opt_outdir}/${base}.${opt_format}";
  }

  if ("${opt_srcdir}" ne "") {
    $src = "${opt_srcdir}/${src}";
  }

  $command='pod2html -netscape';
  if ( $opt_format eq 'html' ) {
    $command='pod2html -netscape';
  } elsif ( $opt_format eq 'latex' ) {
    $command='pod2latex';
  } elsif ( $opt_format eq 'man' ) {
    $command='pod2man';
  } elsif ( $opt_format eq 'text' ) {
    $command='pod2text';
  } elsif ( $opt_format eq 'pod' ) {
    $command='cat';
  }

  print( "Processing $src -> $out\n" );

  pre_format($src, $tmpname_pre_format);		# Make easily parsed
  format_to_pod($tmpname_pre_format, $tmpname_pod);	# Format to pod.

  if ( $opt_format eq 'html' ) {
    system("$command $tmpname_pod > \"$tmpname_html\"");
    reformat_html($tmpname_html,$out);
  } else {
    system("$command $tmpname_pod > \"$out\"");
  }
  unlink($tmpname_pre_format);
  unlink($tmpname_pod);
  unlink($tmpname_html);
}

#unlink($tmpname_pre_format);
exit(0);

#
# Reformat pod2html-generated HTML into nicer form.
#
sub reformat_html {
  my($infile, $outfile) = @_;

  open( IN, "<$infile" ) || die("Failed to open \"$infile\" for read\n" );
  open( OUT, ">$outfile" ) || die("Failed to open \"$outfile\" for write\n" );

 INPUT:
  while(<IN>) {
    s|<HTML>|<\!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"<HTML>|;
    s|<HTML>| "http://www.w3.org/TR/html4/loose.dtd"><HTML>|;
    s|<HEAD>|<HEAD>
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
<STYLE>
<!--
\@page { size: 8.5in 11in }
TD P { color: #000000; font-family: "Verdana", "Arial", "Helvetica", sans-serif; font-size: 12pt }
P { color: #000000; font-family: "Verdana", "Arial", "Helvetica", sans-serif; font-size: 12pt }
H2 { color: #000000 }
A:link { color: #0085c0 }
A:visited { color: #800080 }
-->
</STYLE>
|;
    s|<BODY>|<BODY LANG="en-US" TEXT="#000000" LINK="#0085c0" VLINK="#800080" BGCOLOR="#ffffff">|;
    s|<FONT SIZE=-1>||g;
    s|</FONT>||g;

    s|<H2>|<H3>|g;
    s|</H2>|</H3>|g;

    s|<H1>|<H2>|g;
    s|</H1>|</H2>|g;

    s|<DT>|<DD><P></P><DT>|g;
    s|<DL>|<DL><DT><DD><DL>|g;
    s|</DL>|</DL></DL>|g;
    s|<dd>|<DD>|g;
    s|<p>|<P>|g;
    s|</p>|</P>|g;
    s|</LI>||g;
    s|>o |>|g;
    s|unsignedint|unsigned int|g;
    print( OUT $_ );
  }
  close( TMP );
  close( IN );
}

#
# Pre-process file into intermediate form
#
# Initializes globals:
#
#  @functions	- Function names
#  %synopsis	- Function synopsis
#
sub pre_format {
  my($infile, $tmpfile) = @_;

  my $inpara = 0;	# Set to 1 if in paragraph
  my $inlist = 0;	# Set to 1 if in list-item paragraph

  # Open C source file
  open( IN, "<$infile" ) || die("Failed to open \"$infile\" for read\n" );

  # Open TMP file
  open( TMP, ">$tmpfile" ) || die("Failed to open \"$tmpfile\" for write\n" );

  undef @functions;
  undef %synopsis;

  # Skip past first form feed
  while(<IN>) {
    last if m/\014/;
  }

LINE:
  while(<IN>) {
    if (m/^\+/) {
      while(<IN>) {
	last unless m/^%/;
      }
      next;
    }
    next unless m/^%/ ;
    chop;

    # Extract and save function title
    if (m/^%\s+((\w )+)\s+%/) {
      ($ftitle = $1) =~ s/ //g;
      push(@functions, $ftitle);
      print( TMP "===$ftitle\n" );
      next;
    }

    # Zap text we don't want
    next if ( m/^%.+%/ );	# "%*%
    s/^%\s{0,2}//;

    # Extract and save synopsis info
    if (m /\(\)/ ) {
      # nothing
      ;
    }
    elsif ( m/${ftitle}\(.*\)$/ ) {
      s/,/ , /g;
      s/\(/ ( /g;
      s/\)/ ) /g;
      s/\*/ * /g;
      s/\s+/ /g;

      s/\(\s+\*/(*/g;
      s/ ,/,/g;
      s/ \(/(/g;
      s/\) /)/g;
      s/ \* / */g;

      s/^\s*//;
      $synopsis{$ftitle} = $_ . ';'; # Append semi-colon, prototype style
      print ( TMP " " . $synopsis{$ftitle} . "\n" );
      next LINE;
     }
     elsif ( m/${ftitle}\(.*/ ) {
      $synopsis{$ftitle} = $_;
      do {
	$_ = <IN>;
	chop;
	# Zap text we don't want
	next if m/^%.+%/;	# "%*%
	s/^%\s{0,2}//;
	$synopsis{$ftitle} .= $_;
      } until m/^\s*$/;
      $_ = $synopsis{$ftitle};

      s/,/ , /g;
      s/\(/ ( /g;
      s/\)/ ) /g;
      s/\*/ * /g;
      s/\s+/ /g;

      s/\(\s+\*/(*/g;
      s/ ,/,/g;
      s/ \(/(/g;
      s/\) /)/g;
      s/ \* / */g;

      s/^\s*//;
      $synopsis{$ftitle} = $_ . ';'; # Append semi-colon, prototype style
      print ( TMP " " . $synopsis{$ftitle} . "\n" );
      next LINE;
    }

  # Keep track of paragraphing
  if( ! m/^$/ ) {
    if ( $inpara == 0 ) {
      $inpara = 1;	# Start of paragraph
      $para = "$_";	# Start paragraph string
    } else {
      # Inside paragraph
      $para .= " $_";	# Add line to paragraph
    }
  }
  # Keep track of list items so they can
  # be wrapped as a paragraph
  if( m/^\s+(o[^:]+:|o|[0-9]\.)\s(.*)/ ) {
    $inlist = 1;
  }

  if ( $inpara == 1 ) {
    if( $para =~ m/^\s+\S+/ && ! $inlist ) {
      # Lines that start with a space shouldn't be munged
      $inpara = 0;	# End of paragraph
      $inlist = 0;
      $para .= "";	# Terminate paragraph
      print( TMP "$para\n" );
    }
    elsif( m/^$/ ) {
      # End of paragraph
      $inpara = 0;	# End of paragraph
      $inlist = 0;
      $para .= "";	# Terminate paragraph
      $para =~ s/^\s+//g;		# Eliminate any leading space
      $para =~ s/\s+/ /g;		# Canonicalize whitespace
      $para =~ s/ $//;		# Trim final space
      $para =~ s/([a-zA-Z0-9][.!?][)'"]*) /$1  /g; #' Fix sentance ends
		  print( TMP "\n$para\n\n" );
		}
    }
  }

  close( TMP );
  close( IN );
}

#
# Second pass
# Process into formatted form
#
sub format_to_pod {
    my($infile, $outfile) = @_;

    my $func;

    my $inlist = 0;		# Set to one if in indented list

    # Open input file
    open( IN, "<$infile" ) || die("Failed to open \"$infile\" for read\n" );

    # Open output file
    open( OUT, ">$outfile" ) || die("Failed to open \"$outfile\" for write\n" );

    # Name field
    print( OUT head1("NAME") );
    if (!defined($whatis{$base})) {
      print("Whatis definition missing for \"$base\"!\n");
      print( OUT "${base} - Unknown\n\n" );
    } else {
      print( OUT "${base} - $whatis{$base}\n\n" );
    }

    # Synopsis field (function signatures)
    print( OUT head1("SYNOPSIS") );
    foreach $func (sort( @functions )) {
      if (defined $synopsis{$func} ) {
	$_ = $synopsis{$func};
	s/$func/ B<$func>/;
	s/^\s*//;
	my $synopsis = $_;
	print( OUT $synopsis, "\n\n" );
      }
    }

    # Description field
    print( OUT head1("FUNCTION DESCRIPTIONS") );

    while(<IN>){
	chop;
	next if m/^$/;

	# Match list element
	if( m/^(o[^:]+:|o|[0-9]\.?)\s(.*)/ ) {
	    my $bullet = $1;
	    my $bullet_text = $2;

	    print( OUT startlist() ) unless $inlist;
	    $inlist = 1;
	    print( OUT item($bullet), "$bullet_text\n\n" );
	    next;
	} else {
	    print( OUT endlist() ) if $inlist;
	    $inlist = 0;
	}

	# Match synopsis item
	if( defined $func && m/$func\s*\(.*\)/ ) {
	  # Split all words with spaces to aid with tokenization
	  s/,/ , /g;
	  s/\(/ ( /g;
	  s/\)/ ) /g;
	  s/\*/ * /g;

	  my $html = '';

	  # Replace tokens matching keywords with HTML links.
TOKEN:	  foreach $token ( split(' ', $_ ) ) {
	    foreach $keyword ( %keywords ) {
	      if ( $token eq $keyword ) {
		$html .= linked( $keyword, $keywords{$keyword} );
		$html .= " ";
		next TOKEN;
	      }
	    }
	    $html .= "$token ";
	  }
	  $_ = $html;
	  # Remove excess spaces
	  s/\s+/ /g;
	  s/ ,/,/g;
	  s/\* /*/g;
	  s/\)\s*\;/);/;
	  s/^\s*//;
          s/ \( *\)/\(\)/;

          # This is very poor because text is output specifically
          # for HTML so the text isn't output at all for other target
          # formats.
	  print( OUT html("<blockquote>$_</blockquote>") );
	    next;
	}

	# Match function title
	if( m/===([a-zA-Z0-9]+)/ ) {
	    $func = $1;
	    print( OUT head2($func) );
	    next;
	}

        print( OUT "\n") if /^[^ ]/;
	print( OUT "$_\n") ;
        print( OUT "\n") if /^[^ ]/;
    }

    close( OUT );
    close( IN );
}

#
# Return level 1 heading
# Similar to: <H1>heading</H1>
#
sub head1 {
    my($heading) = @_;
    return( "=head1 $heading\n\n" );
}

#
# Return level 2 heading
# Similar to: <H2>heading</H2>
#
sub head2 {
    my($heading) = @_;
    return( "=head2 $heading\n\n" );
}


#
# Return item
# Simlar to: <I>
#
sub item {
    my($item) = @_;
    return( "=item $item\n\n" );
}


#
# Start list
# Similar to: <UL>
#
sub startlist {
    return( "=over 4\n\n" )
}

#
# End list
# Similar to: </UL>
#
sub endlist {
    return( "=back\n\n" );
}

#
# Preformatted text
# Similar to <PRE></PRE>
#
sub formated {
    my($text) = @_;
    return( " $text\n\n" );
}

#
# Raw HTML paragraph
#
sub html {
  my($html) = @_;
  return return( "=for html $html\n\n" );
}

#
# HTML Link
# Similar to: <A HREF="url">description</A>
#
sub linked {
  local($description, $url) = @_;
  return( "<A HREF=\"" . $url . "\">" . $description . "</A>" );
}
