=head1 NAME

UML::Sequence::Svg - converts xml sequence files to svg

=head1 SYNOPSIS

    use UML::Sequence::Svg;

    seq2svg @ARGV;

=head1 DESCRIPTION

This module supports the seq2svg.pl script like Pod::Html supports pod2html.
The array passed to seq2svg.pl should have the following form:

 ([ I<options-pairs>, ] [input_file_name])

where I<options-pairs> are any of

=over 4

=item C<-a color>

specifies a color to be used to fill the activation boxes. Must be in a form
acceptable to SVG.

=item C<-c color>

specifies a color to be used to fill the class boxes. Must be in a form
acceptable to SVG.

=item C<-e>

specifies that embedded annotations are to be applied
to any rasterized version of the SVG image. Only valid when the C<-m> or C<-M>
option is also specified. When an arrow label has associated annotations,
the labels with be suffixed with a superscript number linking to
a text section containing the annotation text.

=item C<-g arrow-gap>

If C<arrow-gap> is an integer value, specifies number of pixels between arrows
(default is 40 pixels).
If C<arrow-gap> is a fractional value, specifies a scaling factor for the
default number of pixels between arrows.

=item C<-j>

specifies that Javascript'ed tooltip annotations are to be applied
to any rasterized version of the SVG image. Only valid when the C<-m> or C<-M>
option is also specified. When an arrow label has associated annotations,
the hyperlinks in the areamap for the label will include
C<onmouseover()> function calls containing the annotation text for use with the
Javascript tooltip package available at
L<http://www.walterzorn.com/tooltip/tooltip_e.htm>.

=item C<-m areamap-path>, C<-M areamap-path>

specifies the name of a file to receive HTML containing an image element,
areamap, and (optionally) either an ordered list of annotations (if L<-e> was
specified) or a script tag linking to the Javascript tooltip script (if L<-j>
was specified). to be applied to any rasterized version of the SVG image.
Only valid when either the L<-p> or L<-P> option is specified.
C<-M> specifies append mode for the output file.

=item C<-o output_file_name>

specifies the output file name.

=item C<-p classdocs-path>, C<-P classdocs-path>

specifies a base path to classdocs generated by psichedoc. C<-p> causes
hyperlinks to the documents for individual classes and/or methods to be
embedded in the SVG file for both the class labels and method labels, excluding
method labels w/ embedded whitespace. Additionally, the specified path is used
with any generated HTML imagemap. C<-P> is the same, except it does B<not>
embed hyperlinks in the SVG (due to Batik's current inability to handle
rasterization of SVG's with embeded hyperlinks). In other words, use C<-P>
to specify a path for HTML imagemaps when the SVG output will be further
processed by Batik.

=item C<-w box-width>

specifies width of class box in pixels; default is 125. Used to compute
class header boxes and areamap coordinates.

=item C<-x char-width>

specifies width of characters in pixels; default is 6. Used to compute
class header boxes and areamap coordinates.

=item C<-y char-height>

specifies height of characters in pixels; default is 14. Used to compute
class header boxes and areamap coordinates.

=back

By default input is from standard in and output is to standard out.

=cut

package UML::Sequence::Svg;

use Exporter;
@ISA    = qw(Exporter);
@EXPORT = qw(seq2svg);

use XML::DOM;
use Getopt::Std;

use strict;
use warnings;

our $VERSION = '0.02';

# Constant declarations.
my $CLASS_TEXT_Y      =  40;
my $CLASS_BOX_Y       =  25;
my $CLASS_BOX_HEIGHT  =  20;
my $CLASS_BOX_WIDTH   = 125;
my $CLASS_SPACING     =   3;

my $LEFT_EDGE         =  30;

my $ACTIVATION_WIDTH  =  15;
my $ACTIVATION_OFFSET =  10;

my $FIRST_ARROW       =  55;
my $ARROW_SPACING     =  40;

# Global variable:
my $output_file = "-";
my $classcolor = 'white';
my $actcolor = 'white';
my $docpath;            # path to assoc. classdocs
my $hyperlink = 0;      # true => embed hyperlinks
my $mappath;            # path to write areamap file
my $mapname;            # imagemap name derived from SVG output filename
my $charwidth = 10;     # width of characters
my $charht = 10;        # height of characters
my $annot;              # annotations behavior:
                        # 'e' : embed in specified file;
                        # 'j' : apply javascript tooltips in specified file
                        # NOTE: specified file may be same as image map file

sub seq2svg {
    local (@ARGV) = @_;
    my $opts = parse_command_line();

    $classcolor = $opts->{c} || 'white';
    $actcolor = $opts->{a} || 'white';
    $docpath = $opts->{p} || $opts->{P};
    $hyperlink = defined($opts->{p});
    $mappath = $opts->{m} || $opts->{M};
    $charwidth = $opts->{x} || 6;
    $charht = $opts->{y} || 14;
    $CLASS_BOX_WIDTH = $opts->{w} || 125;
    $annot = $opts->{e} ? 'e' : $opts->{j} ? 'j' : undef;

    die "Annotation requested without output path."
        if ($annot && (! $mappath));

    $ARROW_SPACING = (index($opts->{g}, '.') >= 0) ?
        int($ARROW_SPACING * $opts->{g}) : $opts->{g}
        if $opts->{g};

    $docpath .= '/'
        if ($docpath && (substr($docpath, -1, 1) ne '/'));

    my $input_file = shift @ARGV;

    if (defined $input_file) {
        open INPUT, "$input_file"
            or die "Couldn't open $input_file for input: $!\n";
    }
    else {
        *INPUT = *STDIN;
    }
#
#    DAA Add HTML image map rendering
#
    my $mapfd;
    if (defined($mappath)) {
        $mappath = $opts->{M} ? ">>$mappath" : ">$mappath";
        die "Cannot open image map file $mappath: $!"
            unless open($mapfd, $mappath);
#
#    define a mapname from output file name (if any)
#
        $mapname = ($output_file=~/(\w+)\.\w+$/) ? $1 : 'mapname';
        print $mapfd
"<html>
<body>
<img src='$mapname\.png' usemap='#$mapname'>
<MAP NAME='$mapname'>
";
    }

    my $parser       = XML::DOM::Parser->new();
    my $doc          = $parser->parse(*INPUT);

    my $sequence     = $doc->getDocumentElement();
    my $title        = $sequence->getAttribute("title");

    my $classes      = $doc->getElementsByTagName("class");
#
#    DAA added to track previous class for drawing async arrows
#
    my %priors       = ();
    my $class_output = draw_classes($classes, $mapfd, \%priors);
    my $class_hash   = build_class_name_hash($classes);
    my $arrow_output = draw_arrows($doc, $class_hash, $mapfd, \%priors);
    my $class_count  = scalar (keys %$class_hash);
    my $arrow_count  = count_arrows($doc);
    my $width        =
        ($class_count  + 1) * ($CLASS_BOX_WIDTH + $CLASS_SPACING) + 40;
    my $height       = 2.5 * $CLASS_TEXT_Y + $arrow_count * ($ARROW_SPACING);

    open SVGOUT, ">$output_file";

    print SVGOUT <<EOJ;
<?xml version="1.0"?>
  <svg xmlns="http://www.w3.org/2000/svg" height="$height" width="$width">
    <defs>
      <style type="text/css">
              rect, line, path { stroke-width: 2; stroke: black }
              text { font-weight: bold }
      <marker orient="auto" refY="2.5" refX="4" markerHeight="7" markerWidth="6" id="mArrow">
        <path style="fill: black; stroke: none" d="M 0 0 6 3 0 7"/>
      </marker>
      <marker orient="auto" refY="2.5" refX="4" markerHeight="7" markerWidth="6" id="mRtHalfArrow">
        <path style="fill: black; stroke: none" d="M 0 7 6 2 0 2"/>
      </marker>

      <marker orient="0 deg" refY="2.5" refX="4" markerHeight="7" markerWidth="6" id="mLtHalfArrow">
        <path style="fill: black; stroke: none" d="M 0 2 6 2 6 7"/>
      </marker>
      </style>
    </defs>
EOJ

    if ($title) {
        print SVGOUT <<EOJ;
    <text x="5" y="15">
        $title
    </text>
EOJ
    }

    print SVGOUT <<EOJ;
$class_output$arrow_output</svg>
EOJ
#
#    DAA terminate areamap
#
    if (defined($mapfd)) {
        print $mapfd '
<script language="JavaScript" type="text/javascript" src="../wz_tooltip.js"></script>

</body>
</html>
'
            if $opts->{j};
        close $mapfd;
    }

}

sub draw_classes {
  my $classes = shift;
  my $mapfd = shift;
  my $priors = shift;
  my $retval;

  my $x        = $LEFT_EDGE;
  my $box_left = $LEFT_EDGE - 8;
  my $y        = $CLASS_TEXT_Y;
  my $max_extent;
  my $boxht;
  my $boxtext;

  my $prior = '_EXTERNAL';
  for (my $i = 0; $i < $classes->getLength(); $i++) {
    my $class      = $classes->item($i);
    my $life_x     = int($x + $CLASS_BOX_WIDTH / 2);
    my $class_name = $class  ->getAttribute("name");

    if ($class_name eq '_EXTERNAL') {
        $retval = '';
    }
    else {

    $priors->{$class_name} = $prior;
    $prior = $class_name;

    ($boxht, $boxtext) = _wrapText($class_name, $x);
#
#    DAA add hyperlink to psichedocs
#
    my $class_path = ($docpath && ($class_name!~/\s/)) ?
        $docpath . join('/', split(/::/, $class_name)) . '.html' : undef;

    my $born       = $class  ->getAttribute("born")
                     * $ARROW_SPACING + $FIRST_ARROW;
    my $extends_to = ($class ->getAttribute("extends-to") + 1)
                     * $ARROW_SPACING + $FIRST_ARROW;

    if (not defined $max_extent) { $max_extent = $extends_to; }
#
#    DAA rearranged to place text on top of rectangle
#    for fill purposes
#
    $retval .= "  <rect style='fill: $classcolor' height='$boxht' "
            . "width='$CLASS_BOX_WIDTH' y='$CLASS_BOX_Y' x='$box_left' />\n";

    $retval .= "<a xlink:href='$class_path'>"
        if $hyperlink;

    $retval .= "<text y='$CLASS_TEXT_Y' x='$x'>$boxtext</text>\n";

    $retval .= "</a>"
        if $hyperlink;
#
#    DAA support areamaps
#
    print $mapfd "<AREA TITLE='$class_name'",
        ($class_path ? " HREF='$class_path'" : ''),
        " SHAPE=RECT COORDS='$box_left,$CLASS_BOX_Y,",
        $box_left + $CLASS_BOX_WIDTH, ',', $CLASS_BOX_Y + $boxht, "'>\n"
        if $mapfd;

    $retval .= "  <line style='stroke-dasharray: 4,4; ' fill='none' "
            .  "stroke='black' x1='$life_x' y1='$born' x2='$life_x' "
            .  "y2='$max_extent' />\n";

    my $activation_x = int($box_left + $CLASS_BOX_WIDTH / 2);
    my @activations  = $class->getElementsByTagName("activation");
    foreach my $activation (@activations) {
      my $born       = $activation->getAttribute("born");
      my $extends_to = $activation->getAttribute("extends-to");
      my $offset     = $activation->getAttribute("offset");
      my $top        = $FIRST_ARROW  + $born       * $ARROW_SPACING;
      my $height     = ($extends_to  - $born + .5) * $ARROW_SPACING;
      my $left       = $activation_x + $offset     * $ACTIVATION_OFFSET;
      $retval .= "    <rect style='fill: $actcolor' height='$height' "
          . "width='$ACTIVATION_WIDTH' y='$top' x='$left'/>\n";
    }
    }
    $x        += $CLASS_BOX_WIDTH + $CLASS_SPACING;
    $box_left += $CLASS_BOX_WIDTH + $CLASS_SPACING;
    $retval .= "\n";
  }
  return $retval;
}

sub count_arrows {
  my $doc    = shift;
  my $arrows = $doc->getElementsByTagName("arrow");
  return $arrows->getLength();
}

sub draw_arrows {
  my $doc        = shift;
  my $class_hash = shift;
  my $mapfd      = shift;
  my $priors = shift;
  my $retval;

  my $arrows     = $doc->getElementsByTagName("arrow");
  my $annotnum = 1;
  my $annotspan = "\n<ol>\n" if ($annot && ($annot eq 'e'));

  for (my $i = 0; $i < $arrows->getLength(); $i++) {
    my $arrow = $arrows->item($i);
    my $from         = $arrow->getAttribute("from"       );
    my $to           = $arrow->getAttribute("to"         );
#
#    DAA 12/24/2005
#    use type attribute to specify returnvalue or external
#    which changes the line style to dashed or folded, respectively
#    also note that $from for external events originate at the far left
#
    my $type         = $arrow->getAttribute("type"       );
    my $label        = $arrow->getAttribute("label"      );
    my $from_offset  = $arrow->getAttribute("from-offset");
    my $to_offset    = $arrow->getAttribute("to-offset"  );
    my $annots       = $arrow->getElementsByTagName('annotation');
    $annots          = $annots->item(0) if $annots;
    my $y            = $FIRST_ARROW + ($i + 1) * $ARROW_SPACING;

    my $from_number  = ($from eq '_EXTERNAL') ?
        $class_hash->{$priors->{$to}} : $class_hash->{$from};
    my $to_number    = $class_hash->{$to};
    $label =~ s/</&lt;/g;
    $label =~ s/>/&gt;/g;
    my $class_path;
    my $labmethod = $hyperlink ? \&drawHyperLabel : \&drawLabel;
#
#    DAA add hyperlink to psichedocs
#
    if ($docpath && ($to!~/\s/)) {
        my $doclabel = $label;
        $doclabel=~s/[\*!]//g;
        $doclabel=~s/^\s*\[[^\]]*\]\s*//;
        $doclabel=~s/^\s+//;
        $doclabel=~s/\s+$//;
        $class_path = $docpath . join('/', split(/::/, $to)) .
            ".html#$doclabel"
            unless ($doclabel=~/\s/);
    }

    if    ($from_number < $to_number) {  # arrow from left to right
      my $x1 = $from_number * ($CLASS_BOX_WIDTH + $CLASS_SPACING) + $LEFT_EDGE
               + ($CLASS_BOX_WIDTH + $ACTIVATION_WIDTH)/2
               + $from_offset * $ACTIVATION_OFFSET;
      $x1 += 20 if ($from eq '_EXTERNAL');
      my $x2 = $to_number   * ($CLASS_BOX_WIDTH + $CLASS_SPACING) + $LEFT_EDGE
               + ($CLASS_BOX_WIDTH - $ACTIVATION_WIDTH)/2;
#
#    DAA cuddle the label to the arrowhead
#
      my $xlab = $x2 - $CLASS_SPACING - 6 -
          ($label ? ($charwidth * length($label)) : $charwidth);
      my $ylab = $y  - 6;
      my $xend = $label ? $xlab + ($charwidth * length($label)) : undef;
      my $yend = $ylab + $charht;
#
#    DAA changed to support call vs. return vs. async activations
#
      $retval .= "<line x1='$x1' y1='$y' x2='$x2' y2='$y' style='" .
              (($type eq 'return') ? "stroke-dasharray: 4,4; " : ' ') .
              'marker-end: url(#' .
              (($type eq 'async') ? "mRtHalfArrow);' />\n" : "mArrow);' />\n");
#
#    DAA need to add hrefs for methods, but we'll need to track the "to" class
#    name, and ignore names w/ whitespace
#
        $retval .= $labmethod->($mapfd, $xlab, $ylab, $xend, $yend,
            $type, $label, $class_path, $mapname, $i,
            \$annotnum, $annots, \$annotspan)
            if defined($label);
    }
    elsif ($from_number > $to_number) {  # arrow from right to left
      my $x1 = $from_number * ($CLASS_BOX_WIDTH + $CLASS_SPACING) + $LEFT_EDGE
               + ($CLASS_BOX_WIDTH - $ACTIVATION_WIDTH)/2;
      my $x2 = $to_number   * ($CLASS_BOX_WIDTH + $CLASS_SPACING) + $LEFT_EDGE
               + ($CLASS_BOX_WIDTH + $ACTIVATION_WIDTH)/2
               + $to_offset * $ACTIVATION_OFFSET;
#
#    DAA changed to support call vs. return vs. async activations
#    (note async activations always go from left to right, so we won't get
#    any here...)
#
      $retval .= "<line x1='$x1' y1='$y' x2='$x2' y2='$y' style='" .
              (($type eq 'return') ? "stroke-dasharray: 4,4; " : ' ') .
              "marker-end: url(#mArrow);' />\n";

      my $xlab = $x2 + $CLASS_SPACING + 6;
      my $ylab = $y  - 6;
      my $xend = $label ? $xlab + ($charwidth * length($label)) : undef;
      my $yend = $ylab + $charht;
#
#    DAA need to add hrefs for methods, but we'll need to track the "to" class
#    name, and ignore names w/ whitespace
#
        $retval .= $labmethod->($mapfd, $xlab, $ylab, $xend, $yend,
            $type, $label, $class_path, $mapname, $i,
            \$annotnum, $annots, \$annotspan)
            if defined($label);
    }
    else {               # arrow from and to same class
      my $x1 = $from_number * ($CLASS_BOX_WIDTH + $CLASS_SPACING) + $LEFT_EDGE
               + ($CLASS_BOX_WIDTH + $ACTIVATION_WIDTH)/2
               + $from_offset * $ACTIVATION_OFFSET;
      my $x2 = $to_number   * ($CLASS_BOX_WIDTH + $CLASS_SPACING) + $LEFT_EDGE
               + ($CLASS_BOX_WIDTH + $ACTIVATION_WIDTH)/2
               + $to_offset * $ACTIVATION_OFFSET;
      $y -= 10;
      my $y2 = $y + 20;
      my $x1padded = $x1 + $ACTIVATION_OFFSET + 15;
      $retval .= "<line x1='$x1' y1='$y' x2='$x1padded' y2='$y' />\n"
              .  "<line x1='$x1padded' y1='$y' x2='$x1padded' y2='$y2' />\n"
              .  "<line x1='$x1padded' y1='$y2' x2='$x2' y2='$y2' "
              .  "style='marker-end: url(#mArrow);' />\n";
      my $xlab = $x1padded + $CLASS_SPACING;
      my $ylab = ($y + $y2) / 2;
      my $xend = $label ? $xlab + ($charwidth * length($label)) : undef;
      my $yend = $ylab + $charht;
#
#    DAA need to add hrefs for methods, but we'll need to track the "to" class
#    name, and ignore names w/ whitespace
#
        $retval .= $labmethod->($mapfd, $xlab, $ylab, $xend, $yend,
            $type, $label, $class_path, $mapname, $i,
            \$annotnum, $annots, \$annotspan)
            if defined($label);

    }
  }
#
#    DAA save annotations if any
#
    if ($mapfd) {
        print $mapfd "</MAP>\n\n";
        print $mapfd "$annotspan\n</ol>\n"
              if $annotspan && ($annotspan ne '<ol>');
    }

  return $retval;
}

sub build_class_name_hash {
  my $class_nodes = shift;
  my %classes;  # keyed by class name store left to right position

  for (my $i = 0; $i < $class_nodes->getLength(); $i++) {
    my $class = $class_nodes->item($i);
    my $class_name = $class->getAttribute("name");
    $classes{$class_name} = $i;
  }
  return \%classes;
}
#
#    DAA to wrap long class names in the header boxes
#
sub _wrapText {
    my ($text, $xpos) = @_;

    return ($CLASS_BOX_HEIGHT, $text)
        if ((length($text) * $charwidth) < ($CLASS_BOX_WIDTH - 10));
#
#    split on whitespace, dot/colon/underscore, or
#    lowercaseUppercase
#
    my $maxChars = int(($CLASS_BOX_WIDTH - 10)/$charwidth);
    my @lines = ();
    my @pieces = ();
    while ($text=~s/([^:\.\s_]+)((:+)|\.|_|\s+)?//) {
        my ($t, $p) = ($1, $2);
        push @pieces, $t;
#
#    if the text is still too long, look for lowerUpper
#
        if (length($pieces[-1]) <= $maxChars) {
            push(@pieces, $p) if $p;
            next;
        }

        $t = pop @pieces;
        push @pieces, $1
            while ($t=~s/^(.*?[a-z])([A-Z].*)$/$2/);
        push @pieces, $t if ($t ne '');

        push(@pieces, $p) if $p;
    }
#
#    now reassemble to minimize box height
#
    $lines[0] = shift @pieces;
    foreach (@pieces) {
        $lines[-1] .= $_,
        next
            if (length($lines[-1]) <= $maxChars) &&
                ((length($lines[-1]) + length($_)) <= $maxChars);
        push @lines, $_ unless /^\s+$/;
    }

    my $otext = "<tspan x='$xpos'>" .
        join("</tspan>\n<tspan x='$xpos' dy='1em'>", @lines) . '</tspan>';
    return (($charht + 2) * scalar @lines, $otext);

}

sub parse_command_line {
    my %opts;
    getopts('a:c:eg:jm:M:o:p:P:w:x:y:', \%opts);
    $output_file = $opts{o} if defined $opts{o};
    $classcolor = $opts{c} || 'white';
    $actcolor = $opts{a} || 'white';
    $docpath = $opts{p} || $opts{P};
    $annot = $opts{e} || $opts{j};
    return \%opts;
}

sub _createLabelMap {
    my ($x1, $y1, $x2, $y2, $title, $name, $path, $annot_text,
        $annot_name, $annot_span) = @_;
#
#    NOTE: Batik seems to render the text coords about 10px lower than SVG,
#    so we'll cheat here...
#
    $y1 -= 10, $y2 -= 10;

    my $maptext =
"<AREA NAME='$name' TITLE='$title' SHAPE=RECT COORDS='$x1,$y1,$x2,$y2' " .
        ($path ? "HREF='$path' " : '');

    if ($annot_text) {
        if ($annot && ($annot eq 'j')) {
#
#    Scripted annotation
#
            $annot_text =~s/'/\\'/g;    # escape single quotes
            $title =~s/'/\\'/g;         # escape single quotes
#
#    NOTE: this must all be on 1 line!!!
#
            $maptext .="
onmouseover=\"this.T_STATIC=true;this.T_FONTCOLOR='black';this.T_FONTSIZE='12px';this.T_BGCOLOR='#e0e0e0';this.T_OPACITY=90;this.T_SHADOWWIDTH=8;return escape('$annot_text')\" >
";
        }
        else {     # annot eq 'e'
#
#    embedded annotation, add anchor and annotation text
#    NOTE: the offsets used here are heuristic, need a bbox jscript
#
            my ($x3, $y3, $y4) = ($x2+4, $y1 + (($y2 - $y1)>>1), $y1 - 4);

            $maptext .= ">
<AREA SHAPE=RECT COORDS='$x2,$y4,$x3,$y3' HREF='#$annot_name'>
";

            $annot_text=~s/</&lt;/g;
            $annot_text=~s/>/&gt;/g;
            $$annot_span .= "
<a name='$annot_name'></a>
<li>$annot_text
<p>
";
        }
    }
    else {
        $maptext .= ">\n";
    }

    return $maptext;
}

sub drawLabel {
    my ($mapfd, $xlab, $ylab, $xend, $yend, $type, $label, $path,
        $mapname, $i, $annotnum, $annots, $annotspan) = @_;

    my $retval = "<text x='$xlab' y='$ylab' " .
#
#    use italics for asyncs
#
        (($type eq 'async') ?
            "style='font-style: italic;'><tspan>" :
            '><tspan>') . $label;
#
#    if annotated, add superscript if embedded
#
    $retval .= "<tspan baseline-shift=\"super\">$$annotnum</tspan>",
    $$annotnum++
        if ($annot && ($annot eq 'e') && $annots);

    $retval .= "</tspan></text>\n";
#
#    DAA support areamaps
#
    print $mapfd _createLabelMap(
        $xlab, $ylab, $xend, $yend,             # location
        $label, "$mapname\_$i", (($type eq 'call') ? $path : undef),
        ($annots ? $annots->getAttribute('text') : undef),
            "$mapname\_annot_$i", $annotspan)    # annotation
        if $mapfd;

    return $retval;
}

sub drawHyperLabel {
    my ($mapfd, $xlab, $ylab, $xend, $yend, $type, $label, $path,
        $mapname, $i, $annotnum, $annots, $annotspan) = @_;

    my $retval = "<text x='$xlab' y='$ylab' " .
#
#    use italics for asyncs
#
        (($type eq 'async') ?
            "style='font-style: italic;'><tspan>" : '><tspan>') .
            ($path ? "<a xlink:href='$path'>$label</a>" : $label);
#
#    if annotated, add superscript if embedded
#
    $retval .= "<tspan baseline-shift=\"super\">
    <a xlink:href='#$mapname\_annot_$i'>$$annotnum</a>
</tspan>",
    $$annotnum++
        if ($annot && ($annot eq 'e') && $annots);

    $retval .= "</tspan></text>\n";
#
#    DAA support areamaps
#
    print $mapfd _createLabelMap(
        $xlab, $ylab, $xend, $yend,             # location
        $label, "$mapname\_$i", (($type eq 'call') ? $path : undef),
        ($annots ? $annots->getAttribute('text') : undef),
            "$mapname\_annot_$i", $annotspan)    # annotation
        if $mapfd;

    return $retval;
}

1;

=head1 SEE ALSO

L<UML::Sequence::Raster>

=head1 AUTHOR

Phil Crow, <philcrow2000@yahoo.com>

Version 0.02 Updates by Dean Arnold, <darnold@presicient.com>

=head1 COPYRIGHT

Copyright 2003-2006, Philip Crow, all rights reserved.  You may modify and/or
redistribute this code in the same manner as Perl itself.

=cut

