$VERSION
=
'0.76'
;
my
$remap
= {
node
=> {
'align'
=>
undef
,
'background'
=>
undef
,
'basename'
=>
undef
,
'bordercolor'
=> \
&_remap_color
,
'borderstyle'
=> \
&_graphviz_remap_border_style
,
'borderwidth'
=>
undef
,
'border'
=>
undef
,
'color'
=> \
&_remap_color
,
'fill'
=> \
&_remap_color
,
'label'
=> \
&_graphviz_remap_label
,
'pointstyle'
=>
undef
,
'pointshape'
=>
undef
,
'rotate'
=> \
&_graphviz_remap_node_rotate
,
'shape'
=> \
&_graphviz_remap_node_shape
,
'title'
=>
'tooltip'
,
'rows'
=>
undef
,
'columns'
=>
undef
,
},
edge
=> {
'align'
=>
undef
,
'arrowstyle'
=> \
&_graphviz_remap_arrow_style
,
'background'
=>
undef
,
'color'
=> \
&_graphviz_remap_edge_color
,
'end'
=> \
&_graphviz_remap_port
,
'headtitle'
=>
'headtooltip'
,
'headlink'
=>
'headURL'
,
'labelcolor'
=> \
&_graphviz_remap_label_color
,
'start'
=> \
&_graphviz_remap_port
,
'style'
=> \
&_graphviz_remap_edge_style
,
'tailtitle'
=>
'tailtooltip'
,
'taillink'
=>
'tailURL'
,
'title'
=>
'tooltip'
,
'minlen'
=> \
&_graphviz_remap_edge_minlen
,
},
graph
=> {
align
=> \
&_graphviz_remap_align
,
background
=>
undef
,
bordercolor
=> \
&_remap_color
,
borderstyle
=> \
&_graphviz_remap_border_style
,
borderwidth
=>
undef
,
color
=> \
&_remap_color
,
fill
=> \
&_remap_color
,
gid
=>
undef
,
label
=> \
&_graphviz_remap_label
,
labelpos
=>
'labelloc'
,
output
=>
undef
,
type
=>
undef
,
},
group
=> {
align
=> \
&_graphviz_remap_align
,
background
=>
undef
,
bordercolor
=> \
&_remap_color
,
borderstyle
=> \
&_graphviz_remap_border_style
,
borderwidth
=>
undef
,
color
=> \
&_remap_color
,
fill
=> \
&_remap_color
,
labelpos
=>
'labelloc'
,
rank
=>
undef
,
title
=>
'tooltip'
,
},
all
=> {
arrowshape
=>
undef
,
autolink
=>
undef
,
autotitle
=>
undef
,
autolabel
=>
undef
,
class
=>
undef
,
colorscheme
=>
undef
,
flow
=>
undef
,
fontsize
=> \
&_graphviz_remap_fontsize
,
font
=> \
&_graphviz_remap_font
,
format
=>
undef
,
group
=>
undef
,
link
=> \
&_graphviz_remap_link
,
linkbase
=>
undef
,
textstyle
=>
undef
,
textwrap
=>
undef
,
},
always
=> {
node
=> [
qw/borderstyle label link rotate color fill/
],
'node.anon'
=> [
qw/bordercolor borderstyle label link rotate color/
],
edge
=> [
qw/labelcolor label link color/
],
graph
=> [
qw/labelpos borderstyle label link color/
],
},
x
=> \
&_remap_custom_dot_attributes
,
};
sub
_remap_custom_dot_attributes
{
my
(
$self
,
$name
,
$value
) =
@_
;
return
(
undef
,
undef
)
unless
$name
=~ /^x-dot-/;
$name
=~ s/^x-dot-//;
(
$name
,
$value
);
}
my
$color_remap
= {
bordercolor
=>
'color'
,
color
=>
'fontcolor'
,
fill
=>
'fillcolor'
,
};
sub
_remap_color
{
my
(
$self
,
$name
,
$color
,
$object
) =
@_
;
return
(
undef
,
undef
)
unless
defined
$color
;
if
(!
ref
(
$object
) &&
$object
eq
'graph'
)
{
$name
=
'bgcolor'
if
$name
eq
'fill'
;
}
$name
=
$color_remap
->{
$name
} ||
$name
;
$color
=
$self
->_color_as_hex_or_hsv(
$object
,
$color
);
(
$name
,
$color
);
}
sub
_color_as_hex_or_hsv
{
my
(
$graph
,
$self
,
$color
) =
@_
;
if
(
$color
!~ /^
{
if
(
$color
=~ /^hsv\(([0-9\.]+),([0-9\.]+),([0-9\.]+)\)/)
{
$color
=
"$1 $2 $3"
;
}
else
{
my
$cs
=
ref
(
$self
) ?
$self
->attribute(
'colorscheme'
) :
$graph
->attribute(
$self
,
'colorscheme'
);
$color
=
$graph
->color_as_hex(
$color
,
$cs
);
}
}
$color
;
}
sub
_graphviz_remap_align
{
my
(
$self
,
$name
,
$style
) =
@_
;
my
$s
=
lc
(
substr
(
$style
,0,1));
(
'labeljust'
,
$s
);
}
sub
_graphviz_remap_edge_minlen
{
my
(
$self
,
$name
,
$len
) =
@_
;
$len
=
int
((
$len
+ 1) / 2);
(
$name
,
$len
);
}
sub
_graphviz_remap_edge_color
{
my
(
$self
,
$name
,
$color
,
$object
) =
@_
;
my
$style
=
ref
(
$object
) ?
$object
->attribute(
'style'
) :
$self
->attribute(
'edge'
,
'style'
);
if
(!
defined
$color
)
{
$color
=
ref
(
$object
) ?
$object
->attribute(
'color'
) :
$self
->attribute(
'edge'
,
'color'
);
}
$color
=
'#000000'
unless
defined
$color
;
$color
=
$self
->_color_as_hex_or_hsv(
$object
,
$color
);
$color
=
$color
.
':'
.
$color
if
$style
=~ /^double/;
(
$name
,
$color
);
}
sub
_graphviz_remap_edge_style
{
my
(
$self
,
$name
,
$style
) =
@_
;
$style
=
'solid'
unless
defined
$style
;
$style
=
'dotted'
if
$style
=~ /^dot-/;
$style
=
'dotted'
if
$style
=~ /^wave/;
$style
=
'solid'
if
$style
eq
'double'
;
$style
=
'dashed'
if
$style
=~ /^double-dash/;
$style
=
'invis'
if
$style
eq
'invisible'
;
$style
=
'setlinewidth(2), dashed'
if
$style
=~ /^bold-dash/;
$style
=
'setlinewidth(5)'
if
$style
=~ /^broad/;
$style
=
'setlinewidth(11)'
if
$style
=~ /^wide/;
return
(
undef
,
undef
)
if
$style
eq
'solid'
;
(
$name
,
$style
);
}
sub
_graphviz_remap_node_rotate
{
my
(
$graph
,
$name
,
$angle
,
$self
) =
@_
;
return
(
undef
,
undef
)
unless
ref
(
$self
) &&
defined
$angle
;
return
(
undef
,
undef
)
if
$angle
== 0;
$angle
= 360 -
$angle
;
(
'orientation'
,
$angle
);
}
sub
_graphviz_remap_port
{
my
(
$graph
,
$name
,
$side
,
$self
) =
@_
;
return
(
undef
,
undef
)
unless
ref
(
$self
) &&
defined
$side
;
return
(
undef
,
undef
)
if
$side
=~ /,/;
$side
=
$graph
->_flow_as_side(
$self
->flow(),
$side
);
$side
=
substr
(
$side
,0,1);
my
$n
=
'tailport'
;
$n
=
'headport'
if
$name
eq
'end'
;
(
$n
,
$side
);
}
sub
_graphviz_remap_font
{
my
(
$self
,
$name
,
$style
) =
@_
;
(
'fontname'
,
$style
);
}
sub
_graphviz_remap_fontsize
{
my
(
$self
,
$name
,
$style
) =
@_
;
my
$fs
=
'11'
;
if
(
$style
=~ /^([\d\.]+)em\z/)
{
$fs
= $1 * 11;
}
elsif
(
$style
=~ /^([\d\.]+)%\z/)
{
$fs
= ($1 / 100) * 11;
}
elsif
(
$style
=~ /^([\d\.]+)px\z/)
{
$fs
= $1;
}
else
{
$self
->_croak(
"Illegal font-size '$style'"
);
}
(
'fontsize'
,
$fs
);
}
sub
_graphviz_remap_border_style
{
my
(
$self
,
$name
,
$style
,
$node
) =
@_
;
my
$shape
=
''
;
$shape
= (
$node
->attribute(
'shape'
) ||
''
)
if
ref
(
$node
);
return
(
undef
,
undef
)
if
$shape
=~ /^(none|invisible|img|point)\z/;
$style
=
$node
->attribute(
'borderstyle'
)
unless
defined
$style
;
$style
=
''
unless
defined
$style
;
$style
=
'dotted'
if
$style
=~ /^dot-/;
$style
=
'dashed'
if
$style
=~ /^double-/;
$style
=
'dotted'
if
$style
=~ /^wave/;
$style
=
'solid'
if
$style
eq
'double'
;
$style
=
'setlinewidth(2)'
if
$style
=~ /^bold/;
$style
=
'setlinewidth(5)'
if
$style
=~ /^broad/;
$style
=
'setlinewidth(11)'
if
$style
=~ /^wide/;
my
$w
= 0;
$w
=
$node
->attribute(
'borderwidth'
)
if
(
ref
(
$node
) &&
$style
ne
'none'
);
$style
=
'none'
if
$w
== 0;
my
@rc
;
if
(
$style
eq
'none'
)
{
my
$fill
=
'white'
;
$fill
=
$node
->color_attribute(
'fill'
)
if
ref
(
$node
);
$style
=
'filled'
;
@rc
= (
'color'
,
$fill
);
}
return
(
undef
,
undef
)
if
$style
=~ /^(|solid)\z/ &&
$shape
ne
'rounded'
;
$style
=
'filled'
if
$style
eq
'solid'
;
$style
=
'filled,'
.
$style
unless
$style
eq
'filled'
;
$style
=
'rounded,'
.
$style
if
$shape
eq
'rounded'
&&
$style
ne
'none'
;
$style
=~ s/,\z//;
push
@rc
,
'style'
,
$style
;
@rc
;
}
sub
_graphviz_remap_link
{
my
(
$self
,
$name
,
$l
,
$object
) =
@_
;
return
(
undef
,
undef
)
unless
ref
(
$object
);
$l
=
$object
->
link
()
unless
defined
$l
;
(
'URL'
,
$l
);
}
sub
_graphviz_remap_label_color
{
my
(
$graph
,
$name
,
$color
,
$self
) =
@_
;
return
(
undef
,
undef
)
unless
ref
(
$self
);
return
(
undef
,
$color
)
if
(
$self
->label()||
''
) eq
''
;
$color
=
$self
->raw_attribute(
'labelcolor'
)
unless
defined
$color
;
$color
=
$self
->attribute(
'color'
)
unless
defined
$color
;
$color
=
$graph
->_color_as_hex_or_hsv(
$self
,
$color
);
(
'fontcolor'
,
$color
);
}
sub
_graphviz_remap_node_shape
{
my
(
$self
,
$name
,
$style
,
$object
) =
@_
;
return
(
undef
,
undef
)
if
$style
=~ /^(img|rounded)\z/;
my
$s
=
$style
;
$s
=
'plaintext'
if
$style
=~ /^(invisible|none|point)\z/;
if
(
ref
(
$object
))
{
my
$border
=
$object
->attribute(
'borderstyle'
);
$s
=
'plaintext'
if
$border
eq
'none'
;
}
(
$name
,
$s
);
}
sub
_graphviz_remap_arrow_style
{
my
(
$self
,
$name
,
$style
) =
@_
;
my
$s
=
'normal'
;
$s
=
$style
if
$style
=~ /^(none|
open
)\z/;
$s
=
'empty'
if
$style
eq
'closed'
;
my
$n
=
'arrowhead'
;
$n
=
'arrowtail'
if
$self
->{_flip_edges};
(
$n
,
$s
);
}
sub
_graphviz_remap_label
{
my
(
$self
,
$name
,
$label
,
$node
) =
@_
;
my
$s
=
$label
;
$s
=
$node
->label()
if
ref
(
$node
);
if
(
ref
(
$node
))
{
my
$align
=
$node
->attribute(
'align'
);
my
$next_line
=
'\n'
;
$next_line
=
'\l'
,
$s
.=
'\l'
if
$align
eq
'left'
;
$next_line
=
'\r'
,
$s
.=
'\r'
if
$align
eq
'right'
;
$s
=~ s/(^|[^\\])\\n/$1
$next_line
/g;
}
$s
=~ s/(^|[^\\])\\c/$1\\n/g;
my
$shape
=
'rect'
;
$shape
= (
$node
->attribute(
'shape'
) ||
''
)
if
ref
(
$node
);
if
(
$shape
eq
'img'
)
{
my
$s
=
'<<TABLE BORDER="0"><TR><TD><IMG SRC="##url##" /></TD></TR></TABLE>>'
;
my
$url
=
$node
->label();
$url
=~ s/\s/\+/g;
$url
=~ s/'/%27/g;
$s
=~ s/
}
(
$name
,
$s
);
}
sub
_att_as_graphviz
{
my
(
$self
,
$out
) =
@_
;
my
$att
=
''
;
for
my
$atr
(
sort
keys
%$out
)
{
my
$v
=
$out
->{
$atr
};
$v
=~ s/\n/\\n/g;
$v
=
'"'
.
$v
.
'"'
if
$v
!~ /^[a-z0-9A-Z]+\z/;
my
$name
=
$atr
;
$name
=~ s/^x-dot-//;
$name
=
'K'
if
$name
eq
'k'
;
$att
.=
" $name=$v,\n"
;
}
$att
=~ s/,\n\z/ /;
if
(
$att
ne
''
)
{
if
(
$att
!~ /\n.*\n/ &&
length
(
$att
) < 40)
{
$att
=~ s/\n/ /;
$att
=~ s/( )+/ /g;
}
else
{
$att
=~ s/\n/\n /g;
$att
=
"\n $att"
;
}
}
$att
;
}
sub
_generate_group_edge
{
my
(
$self
,
$e
,
$indent
) =
@_
;
my
$edge_att
=
$e
->attributes_as_graphviz();
my
$a
=
''
;
my
$b
=
''
;
my
$from
=
$e
->{from};
my
$to
=
$e
->{to};
(
$from
,
$to
) = (
$to
,
$from
)
if
$self
->{_flip_edges};
if
(
$from
->isa(
'Graph::Easy::Group'
))
{
my
(
$n
,
$v
) = first_kv(
$from
->{nodes});
$a
=
'ltail="cluster'
.
$from
->{id}.
'"'
;
$from
=
$v
;
}
if
(
$to
->isa(
'Graph::Easy::Group'
))
{
my
(
$n
,
$v
) = first_kv(
$to
->{nodes});
$b
=
'lhead="cluster'
.
$to
->{id}.
'"'
;
$to
=
$v
;
}
my
$other
=
$to
->_graphviz_point();
my
$first
=
$from
->_graphviz_point();
$e
->{_p} =
undef
;
my
$att
=
$a
;
$att
.=
', '
.
$b
if
$b
ne
''
;
$att
=~ s/^,//;
if
(
$att
ne
''
)
{
if
(
$edge_att
eq
''
)
{
$edge_att
=
" [ $att ]"
;
}
else
{
$edge_att
=~ s/ \]/,
$att
\]/;
}
}
"$indent$first $self->{edge_type} $other$edge_att\n"
;
}
sub
_insert_edge_attribute
{
my
(
$self
,
$att
,
$new_att
) =
@_
;
return
'[ $new_att ]'
if
$att
eq
''
;
my
$att_name
=
$new_att
;
$att_name
=~ s/=.*//;
$att
=~ s/
$att_name
=(
"[^"
]+"|[^\s]+)//;
$att
=~ s/\s?\]/,
$new_att
]/;
$att
;
}
sub
_suppress_edge_attribute
{
my
(
$self
,
$att
,
$sup_att
) =
@_
;
$att
=~ s/
$sup_att
=(
"(\\"
|[^
"])*"
|[^\s\n,;]+)[,;]?//;
$att
;
}
sub
_generate_edge
{
my
(
$self
,
$e
,
$indent
) =
@_
;
return
''
if
$e
->{from}->isa(
'Graph::Easy::Group'
) ||
$e
->{to}->isa(
'Graph::Easy::Group'
);
my
$invis
=
$self
->{_graphviz_invis};
my
$inv
=
' [ label="",shape=none,style=filled,height=0,width=0,fillcolor="'
;
my
$other
=
$e
->{to}->_graphviz_point();
my
$first
=
$e
->{from}->_graphviz_point();
my
$edge_att
=
$e
->attributes_as_graphviz();
my
$txt
=
''
;
my
$modify_edge
= 0;
my
$suppress_start
= (!
$self
->{_flip_edges} ?
'arrowtail=none'
:
'arrowhead=none'
);
my
$suppress_end
= (
$self
->{_flip_edges} ?
'arrowtail=none'
:
'arrowhead=none'
);
my
$suppress
;
if
(
$e
->has_ports())
{
my
@edges
= ();
my
(
$side
,
@port
) =
$e
->port(
'start'
);
@edges
=
$e
->{from}->edges_at_port(
'start'
,
$side
,
@port
)
if
defined
$side
&&
@port
> 0;
if
(
@edges
> 1)
{
my
$sp
=
$e
->port(
'start'
);
my
$key
=
"$e->{from}->{name},start,$sp"
;
my
$invis_id
=
$invis
->{
$key
};
$suppress
=
$suppress_start
;
if
(!
defined
$invis_id
)
{
$self
->{_graphviz_invis_id}++
while
(
defined
$self
->node(
$self
->{_graphviz_invis_id}));
$invis_id
=
$self
->{_graphviz_invis_id}++;
my
$e_color
=
$e
->color_attribute(
'color'
);
$txt
.=
$indent
.
"$invis_id$inv$e_color\" ]\n"
;
my
$e_att
=
$self
->_insert_edge_attribute(
$edge_att
,
$suppress_end
);
$e_att
=
$self
->_suppress_edge_attribute(
$e_att
,
'label'
);
my
$before
=
''
;
my
$after
=
''
;
my
$i
=
$indent
;
if
(
$e
->{group})
{
$before
=
$indent
.
'subgraph "cluster'
.
$e
->{group}->{id} . "\
" {\n"
;
$after
=
$indent
.
"}\n"
;
$i
=
$indent
.
$indent
;
}
if
(
$self
->{_flip_edges})
{
$txt
.=
$before
.
$i
.
"$invis_id $self->{_edge_type} $first$e_att\n"
.
$after
;
}
else
{
$txt
.=
$before
.
$i
.
"$first $self->{_edge_type} $invis_id$e_att\n"
.
$after
;
}
$invis
->{
$key
} =
$invis_id
;
}
$first
=
$invis_id
;
$modify_edge
++;
}
(
$side
,
@port
) =
$e
->port(
'end'
);
@edges
= ();
@edges
=
$e
->{to}->edges_at_port(
'end'
,
$side
,
@port
)
if
defined
$side
&&
@port
> 0;
if
(
@edges
> 1)
{
my
$ep
=
$e
->port(
'end'
);
my
$key
=
"$e->{to}->{name},end,$ep"
;
my
$invis_id
=
$invis
->{
$key
};
$suppress
=
$suppress_end
;
if
(!
defined
$invis_id
)
{
$self
->{_graphviz_invis_id}++
while
(
defined
$self
->node(
$self
->{_graphviz_invis_id}));
$invis_id
=
$self
->{_graphviz_invis_id}++;
my
$e_att
=
$self
->_insert_edge_attribute(
$edge_att
,
$suppress_start
);
my
$e_color
=
$e
->color_attribute(
'color'
);
$txt
.=
$indent
.
"$invis_id$inv$e_color\" ]\n"
;
my
$before
=
''
;
my
$after
=
''
;
my
$i
=
$indent
;
if
(
$e
->{group})
{
$before
=
$indent
.
'subgraph "cluster'
.
$e
->{group}->{id} . "\
" {\n"
;
$after
=
$indent
.
"}\n"
;
$i
=
$indent
.
$indent
;
}
if
(
$self
->{_flip_edges})
{
$txt
.=
$before
.
$i
.
"$other $self->{_edge_type} $invis_id$e_att\n"
.
$after
;
}
else
{
$txt
.=
$before
.
$i
.
"$invis_id $self->{_edge_type} $other$e_att\n"
.
$after
;
}
$invis
->{
$key
} =
$invis_id
;
}
$other
=
$invis_id
;
$modify_edge
++;
}
}
(
$other
,
$first
) = (
$first
,
$other
)
if
$self
->{_flip_edges};
$e
->{_p} =
undef
;
$edge_att
=
$self
->_insert_edge_attribute(
$edge_att
,
$suppress
)
if
$modify_edge
;
$txt
.
"$indent$first $self->{_edge_type} $other$edge_att\n"
;
}
sub
_order_group
{
my
(
$self
,
$group
) =
@_
;
$group
->{_order}++;
for
my
$sg
(ord_values(
$group
->{groups}))
{
$self
->_order_group(
$sg
);
}
}
sub
_as_graphviz_group
{
my
(
$self
,
$group
) =
@_
;
my
$txt
=
''
;
my
$name
=
$group
->{name};
$name
=~ s/([\[\]\(\)\{\}\
return
if
$group
->{_p};
my
$indent
=
' '
x (
$group
->{_order});
$txt
.=
$indent
.
"subgraph \"cluster$group->{id}\" {\n${indent}label=\"$name\";\n"
;
for
my
$sg
(ord_values (
$group
->{groups} ))
{
$txt
.=
$self
->_as_graphviz_group(
$sg
,
$indent
);
$sg
->{_p} = 1;
}
my
$copy
= {};
my
$attribs
=
$group
->get_attributes();
for
my
$key
(
sort
keys
%$attribs
)
{
$copy
->{
$key
} =
$attribs
->{
$key
};
}
$copy
->{
'borderstyle'
} =
'solid'
unless
defined
$copy
->{
'borderstyle'
};
my
$out
=
$self
->_remap_attributes(
$group
->class(),
$copy
,
$remap
,
'noquote'
);
$out
->{fillcolor} =
'#a0d0ff'
unless
defined
$out
->{fillcolor};
$out
->{labeljust} =
'l'
unless
defined
$out
->{labeljust};
my
$att
=
''
;
for
my
$atr
(
reverse
sort
keys
%$out
)
{
my
$v
=
$out
->{
$atr
};
$v
=
'"'
.
$v
.
'"'
if
$v
!~ /^[a-z0-9A-Z]+\z/;
my
$name
=
$atr
;
$name
=~ s/^x-dot-//;
$name
=
'K'
if
$name
eq
'k'
;
$att
.=
$indent
.
"$name=$v;\n"
;
}
$txt
.=
$att
.
"\n"
if
$att
ne
''
;
for
my
$n
(
$group
->sorted_nodes())
{
next
if
$n
->{origin};
my
$att
=
$n
->attributes_as_graphviz();
$n
->{_p} =
undef
;
$txt
.=
$indent
.
$n
->as_graphviz_txt() .
$att
.
"\n"
;
}
for
my
$e
(ord_values
$group
->{edges})
{
next
if
exists
$e
->{_p};
$txt
.=
$self
->_generate_edge(
$e
,
$indent
);
}
$txt
.=
$indent
.
"}\n"
;
return
$txt
;
}
sub
_as_graphviz
{
my
(
$self
) =
@_
;
my
$name
=
"GRAPH_"
. (
$self
->{gid} ||
'0'
);
my
$type
=
$self
->attribute(
'type'
);
$type
=
$type
eq
'directed'
?
'digraph'
:
'graph'
;
$self
->{_edge_type} =
$type
eq
'digraph'
?
'->'
:
'--'
;
my
$txt
=
"$type $name {\n\n"
.
" // Generated by Graph::Easy $Graph::Easy::VERSION"
.
" at "
.
scalar
localtime
() .
"\n\n"
;
my
$flow
=
$self
->attribute(
'graph'
,
'flow'
);
$flow
=
'east'
unless
defined
$flow
;
$flow
= Graph::Easy->_direction_as_number(
$flow
);
$self
->{_flip_edges} = 0;
$self
->{_flip_edges} = 1
if
$flow
== 270 ||
$flow
== 0;
my
$groups
=
$self
->groups();
$self
->{_graphviz_invis} = {};
$self
->{_graphviz_invis_id} =
'joint0'
;
my
$atts
=
$self
->{att};
for
my
$class
(
qw/edge graph node/
)
{
next
if
$class
=~ /\./;
my
$out
=
$self
->_remap_attributes(
$class
,
$atts
->{
$class
},
$remap
,
'noquote'
);
if
(
$class
eq
'node'
)
{
$out
->{shape} =
'box'
unless
$out
->{shape};
$out
->{style} =
'filled'
unless
$out
->{style};
$out
->{fontsize} =
'11'
unless
$out
->{fontsize};
$out
->{fillcolor} =
'white'
unless
$out
->{fillcolor};
}
elsif
(
$class
eq
'graph'
)
{
$out
->{rankdir} =
'LR'
if
$flow
== 90 ||
$flow
== 270;
$out
->{labelloc} =
'top'
if
defined
$out
->{label} && !
defined
$out
->{labelloc};
$out
->{style} =
'filled'
if
$groups
> 0;
}
elsif
(
$class
eq
'edge'
)
{
$out
->{dir} =
'back'
if
$flow
== 270 ||
$flow
== 0;
my
(
$name
,
$style
) =
$self
->_graphviz_remap_arrow_style(
''
,
$self
->attribute(
'edge'
,
'arrowstyle'
) );
$out
->{
$name
} =
$style
;
}
my
$att
=
$self
->_att_as_graphviz(
$out
);
$txt
.=
" $class [$att];\n"
if
$att
ne
''
;
}
$txt
.=
"\n"
if
$txt
ne
''
;
$self
->_edges_into_groups()
if
$groups
> 0;
for
my
$group
(ord_values
$self
->{groups})
{
$self
->_order_group(
$group
);
}
for
my
$group
(
sort
{
$a
->{_order} cmp
$b
->{_order} }
values
%{
$self
->{groups}})
{
$txt
.=
$self
->_as_graphviz_group(
$group
) ||
''
;
}
my
$root
=
$self
->attribute(
'root'
);
$root
=
''
unless
defined
$root
;
my
$count
= 0;
for
my
$n
(
sort
{
$a
->{name} cmp
$b
->{name} }
values
%{
$self
->{nodes}})
{
next
if
exists
$n
->{_p};
next
if
$n
->{origin};
my
$att
=
$n
->attributes_as_graphviz(
$root
);
if
(
$att
ne
''
)
{
$n
->{_p} =
undef
;
$count
++;
$txt
.=
" "
.
$n
->as_graphviz_txt() .
$att
.
"\n"
;
}
}
$txt
.=
"\n"
if
$count
> 0;
my
@nodes
=
$self
->sorted_nodes();
foreach
my
$n
(
@nodes
)
{
my
@out
=
$n
->successors();
my
$first
=
$n
->as_graphviz_txt();
if
((
@out
== 0) && ( (
scalar
$n
->predecessors() || 0) == 0))
{
$txt
.=
" "
.
$first
.
"\n"
unless
exists
$n
->{_p} ||
$n
->{origin};
}
foreach
my
$other
(
reverse
@out
)
{
my
@edges
=
$n
->edges_to(
$other
);
foreach
my
$e
(
@edges
)
{
next
if
exists
$e
->{_p};
$txt
.=
$self
->_generate_edge(
$e
,
' '
);
}
}
}
foreach
my
$e
(ord_values
$self
->{edges})
{
$txt
.=
$self
->_generate_group_edge(
$e
,
' '
)
if
$e
->{from}->isa(
'Graph::Easy::Group'
) ||
$e
->{to}->isa(
'Graph::Easy::Group'
);
}
for
my
$n
( ord_values(
$self
->{nodes}), ord_values(
$self
->{edges} ))
{
delete
$n
->{_p};
}
delete
$self
->{_graphviz_invis};
delete
$self
->{_flip_edges};
delete
$self
->{_edge_type};
$txt
.
"\n}\n"
;
}
sub
attributes_as_graphviz
{
my
(
$self
,
$root
) =
@_
;
$root
=
''
unless
defined
$root
;
my
$att
=
''
;
my
$class
=
$self
->class();
return
''
unless
ref
$self
->{graph};
my
$g
=
$self
->{graph};
my
$a
=
$self
->raw_attributes();
my
$attr
=
$self
->{att};
my
$base_class
=
$class
;
$base_class
=~ s/\..*//;
my
$list
=
$remap
->{always}->{
$class
} ||
$remap
->{always}->{
$base_class
};
for
my
$name
(
@$list
)
{
if
(
ref
(
$remap
->{
$base_class
}->{
$name
}) ||
ref
(
$remap
->{all}->{
$name
}) )
{
$a
->{
$name
} =
$self
->raw_attribute(
$name
);
if
(!
defined
$a
->{
$name
})
{
my
$b_attr
=
$g
->get_attribute(
$base_class
,
$name
);
my
$c_attr
=
$g
->get_attribute(
$class
,
$name
);
if
(
defined
$b_attr
&&
defined
$c_attr
&&
$b_attr
ne
$c_attr
)
{
$a
->{
$name
} =
$c_attr
;
$a
->{
$name
} =
$b_attr
unless
defined
$a
->{
$name
};
}
}
}
else
{
$a
->{
$name
} =
$attr
->{
$name
};
$a
->{
$name
} =
$self
->attribute(
$name
)
unless
defined
$a
->{
$name
} &&
$a
->{
$name
} ne
'inherit'
;
}
}
$a
=
$g
->_remap_attributes(
$self
,
$a
,
$remap
,
'noquote'
);
delete
$a
->{label}
if
!
$self
->isa(
'Graph::Easy::Edge'
) &&
exists
$a
->{label} &&
$a
->{label} eq
$self
->{name};
if
(!
$self
->{origin} &&
$self
->{children} &&
keys
%{
$self
->{children}} > 0)
{
$a
->{label} =
$self
->_html_like_label();
$a
->{shape} =
'none'
;
}
if
(
$self
->{bidirectional})
{
delete
$a
->{dir};
my
(
$n
,
$s
) = Graph::Easy::_graphviz_remap_arrow_style(
$self
,
''
,
$self
->attribute(
'arrowstyle'
));
$a
->{arrowhead} =
$s
;
$a
->{arrowtail} =
$s
;
}
if
(
$self
->{undirected})
{
delete
$a
->{dir};
$a
->{arrowhead} =
'none'
;
$a
->{arrowtail} =
'none'
;
}
if
(!
$self
->isa_cell())
{
my
$style
=
$self
->attribute(
'borderstyle'
);
my
$w
=
$self
->attribute(
'borderwidth'
);
$a
->{peripheries} = 2
if
$style
=~ /^double/ &&
$w
> 0;
}
my
$shape
=
$a
->{shape} ||
'rect'
;
if
(
$class
=~ /node/ &&
$shape
eq
'plaintext'
)
{
my
$p
=
$self
->parent();
$a
->{fillcolor} =
$p
->attribute(
'fill'
);
$a
->{fillcolor} =
'white'
if
$a
->{fillcolor} eq
'inherit'
;
}
$shape
=
$self
->attribute(
'shape'
)
unless
$self
->isa_cell();
if
(
$shape
eq
'point'
)
{
my
$style
=
$self
->_point_style(
$self
->attribute(
'pointshape'
),
$self
->attribute(
'pointstyle'
) );
$a
->{label} =
$style
;
$a
->{width} = 0,
$a
->{height} = 0
if
$style
eq
''
;
}
if
(
$shape
eq
'invisible'
)
{
$a
->{label} =
' '
;
}
$a
->{rank} =
'0'
if
$root
ne
''
&&
$root
eq
$self
->{name};
for
my
$atr
(
sort
keys
%$a
)
{
my
$v
=
$a
->{
$atr
};
$v
=~ s/
"/\\"
/g;
if
(
$atr
eq
'label'
&&
$v
=~ /^<<TABLE/)
{
my
$va
=
$v
;
$va
=~ s/\\
"/"
/g;
$att
.=
"$atr=$va, "
;
next
;
}
$v
=
'"'
.
$v
.
'"'
if
$v
!~ /^[a-z0-9A-Z]+\z/
||
$atr
eq
'URL'
;
my
$name
=
$atr
;
$name
=~ s/^x-dot-//;
$name
=
'K'
if
$name
eq
'k'
;
$att
.=
"$name=$v, "
;
}
$att
=~ s/,\s$//;
$att
=
' [ '
.
$att
.
' ]'
if
$att
ne
''
;
$att
;
}
sub
_html_like_label
{
my
(
$self
) =
@_
;
my
$cells
= {};
my
$rc
=
$self
->_do_place(0,0, {
cells
=>
$cells
,
cache
=> {} } );
my
$label
=
'<<TABLE BORDER="0"><TR>'
;
my
$old_y
= 0;
my
$old_x
= 0;
my
@cells
= ();
for
my
$cell
(
sort
{
my
(
$ax
,
$ay
) =
split
/,/,
$a
;
my
(
$bx
,
$by
) =
split
/,/,
$b
;
$ay
<=>
$by
or
$ax
<=>
$bx
; }
keys
%$cells
)
{
my
(
$x
,
$y
) =
split
/,/,
$cell
;
if
(
$y
>
$old_y
)
{
$label
.=
'</TR><TR>'
;
$old_x
= 0;
}
my
$n
=
$cells
->{
$cell
};
my
$l
=
$n
->label();
$l
=~ s/\\n/<BR\/>/g;
my
$portname
=
$n
->{autosplit_portname};
$portname
=
$n
->label()
unless
defined
$portname
;
my
$name
=
$self
->{name};
$portname
=~ s/\
"/\\"
/g;
$name
=~ s/\
"/\\"
/g;
$n
->{_graphviz_portname} =
'"'
.
$name
.
'":"'
.
$portname
.
'"'
;
if
((
$x
-
$old_x
) > 0)
{
$label
.=
'<TD BORDER="0" COLSPAN="'
. (
$x
-
$old_x
) .
'"></TD>'
;
}
$label
.=
'<TD BORDER="1" PORT="'
.
$portname
.
'">'
.
$l
.
'</TD>'
;
$old_y
=
$y
+
$n
->{cy};
$old_x
=
$x
+
$n
->{cx};
}
$label
.
'</TR></TABLE>>'
;
}
sub
_graphviz_point
{
my
(
$n
) =
@_
;
return
$n
->{_graphviz_portname}
if
exists
$n
->{_graphviz_portname};
$n
->as_graphviz_txt();
}
sub
as_graphviz_txt
{
my
$self
=
shift
;
my
$name
=
$self
->{name};
$name
=~ s/([\[\]\(\)\{\}"])/\\$1/g;
$name
=
'"'
.
$name
.
'"'
if
$name
!~ /^([a-zA-Z_]+|\d+)\z/ ||
$name
=~ /^(subgraph|graph|node|edge|strict)\z/i;
$name
;
}
1;