File Coverage

blib/lib/PostScript/Graph/Key.pm
Criterion Covered Total %
statement 125 155 80.6
branch 49 86 56.9
condition 5 7 71.4
subroutine 11 15 73.3
pod 6 9 66.6
total 196 272 72.0


line stmt bran cond sub pod time code
1             package PostScript::Graph::Key;
2             our $VERSION = 1.03;
3 13     13   1042 use strict;
  13         30  
  13         672  
4 13     13   79 use warnings;
  13         29  
  13         439  
5 13     13   71 use Exporter;
  13         27  
  13         549  
6 13     13   74 use Carp;
  13         28  
  13         1503  
7 13     13   71 use PostScript::File 1.00 qw(str);
  13         266  
  13         685  
8 13     13   11023 use PostScript::Graph::Paper 1.00;
  13         473  
  13         27970  
9              
10             our @ISA = qw(Exporter);
11              
12             =head1 NAME
13              
14             PostScript::Graph::Key - a Key area for PostScript::Graph::Paper
15              
16             =head1 SYNOPSIS
17              
18             use PostScript::File;
19             use PostScript::Graph::Key;
20             use PostScript::Graph::Paper;
21            
22             =head2 Typical
23              
24             A Key panel is drawn to the right of its associated graph. The area needed for the Key panel must be calculated
25             before the graph Paper can be drawn in the remaining space.
26              
27             my $ps = new PostScript::File;
28             my @bbox = $psf->get_page_bounding_box();
29              
30             # Calculate variables from the graph data
31             # planned layout and available space ...
32            
33             my $gk = new PostScript::Graph::Key(
34             num_items => ... ,
35             max_height => ... ,
36             text_width => ... ,
37             icon_width => ... ,
38             icon_height => ... ,
39             );
40              
41             # Need to pass this to GraphPaper
42             my $width = $pgk->width();
43              
44             my $gp = new PostScript::Graph::Paper(
45             file => $ps,
46             chart => {
47             key_width => $width,
48             },
49             );
50              
51             # Now we can link the two
52             $gk->build_key( $gp );
53              
54             foreach $line (@lines) {
55             ... draw line on graph_paper ...
56              
57             $gk->add_key_item( $title, <
58             ... postscript code
59             ... drawing icon
60             END
61             }
62              
63             =head2 All options
64              
65             my $gp = new PostScript::Graph::Key(
66             file => $ps_file,
67             graph_paper => $ps_gpaper,
68             max_height => 500,
69             num_items => 5,
70             background => 0.9,
71             outline_color => [0.5, 0.5, 0.2],
72             outline_wdith => 0.8,
73             spacing => 4,
74             horz_spacing => 4,
75             vert_spacing => 6,
76             icon_height => 12,
77             icon_width => 40,
78             text_width => 72,
79             text_font => {
80             size => 10,
81             color => 0,
82             font => 'Courier',
83             },
84             title => 'My Key',
85             title_font => {
86             color => [1, 0, 0],
87             font => 'Times-Bold',
88             size => 14,
89             },
90             };
91              
92             =head1 DESCRIPTION
93              
94             This module is designed as a supporting part of the PostScript::Graph suite. For top level modules that output
95             something useful, see
96              
97             PostScript::Graph::Bar
98             PostScript::Graph::Stock
99             PostScript::Graph::XY
100              
101             A companion object to PostScript::Graph::Paper, this is used by any module that requies a Key for a graph. The
102             size and shape is automatically adjusted to accomodate the number of items in the space available, adding more
103             columns if there is not enough vertical space. The textual description now wraps onto multiple lines if needed.
104              
105             The opportunity is provided to draw an icon with each key item using the same code and style as is used on the
106             graph. This is accomplished in distinct phases.
107              
108             =over 4
109              
110             =item *
111              
112             The total space available is typically obtained from PostScript::File, but must be known so that
113             PostScript::Graph::Key and PostScript::Graph::Paper can divide it between them.
114              
115             =item *
116              
117             All the graph lines must exist before the graph is drawn. PostScript::Graph::Paper needs to calculate the axes
118             and suggest the height available for the Key which will be placed to the right of the graph.
119              
120             =item *
121              
122             The PostScript::Graph::Key constructor calculates the outer box dimensions from the items required and the space
123             available. In particular the number of text rows per item must be worked out and how many columns will be needed.
124              
125             =item *
126              
127             The key width can then be passed to the PostScript::Graph::Paper constructor, which works out the space available
128             for the graph and usually draws the background grid.
129              
130             =item *
131              
132             B can then be called to draw the outer box and heading next to the grid.
133              
134             =item *
135              
136             For each graph line, the line can be drawn on the paper and its icons drawn in the key box at the same time, using
137             the same PostScript::Graph::Style settings.
138              
139             =back
140              
141              
142             =head1 CONSTRUCTOR
143              
144             =cut
145              
146             sub new {
147 13     13 1 1460 my $class = shift;
148 13         50 my $opt = {};
149 13 100 33     62 if (@_ == 1) {
    50          
150 12         29 $opt = $_[0];
151             } elsif (@_ == 2 and $_[0]+0 > 0) {
152 0         0 ($opt->{max_height}, $opt->{num_items}) = @_;
153             } else {
154 1         6 %$opt = @_;
155             }
156            
157 13         40 my $o = {};
158 13         46 bless( $o, $class );
159            
160 13 50       160 $o->{gp} = defined($opt->{graph_paper}) ? $opt->{graph_paper} : undef;
161 13 50       78 my $ps = defined($o->{gp}) ? $o->{gp}->file() : undef;
162 13 50       61 $o->{ps} = defined($opt->{file}) ? $opt->{file} : $ps;
163              
164 13 50       61 $o->{title} = defined($opt->{title}) ? $opt->{title} : "Key";
165 13 50       109 $o->{hcolor} = defined($opt->{title_font}{color}) ? $opt->{title_font}{color} : 0;
166 13 50       64 $o->{hfont} = defined($opt->{title_font}{font}) ? $opt->{title_font}{font} : 'Helvetica-Bold';
167 13 50       89 $o->{hsize} = defined($opt->{title_font}{size}) ? $opt->{title_font}{size} : 12;
168 13 50       97 $o->{tcolor} = defined($opt->{text_font}{color}) ? $opt->{text_font}{color} : 0;
169 13 50       83 $o->{tfont} = defined($opt->{text_font}{font}) ? $opt->{text_font}{font} : 'Helvetica';
170 13 50       66 $o->{tsize} = defined($opt->{text_font}{size}) ? $opt->{text_font}{size} : 10;
171 13 50       72 $o->{ratio} = defined($opt->{glyph_ratio}) ? $opt->{glyph_ratio} : 0.44;
172 13 100       80 $o->{twidth} = defined($opt->{text_width}) ? $opt->{text_width} : $o->{tsize} * 4;
173 13 100       74 $o->{fcolor} = defined($opt->{background}) ? $opt->{background} : 1;
174 13 50       61 $o->{ocolor} = defined($opt->{outline_color}) ? $opt->{outline_color} : 0;
175 13 50       77 $o->{owidth} = defined($opt->{outline_width}) ? $opt->{outline_width} : 0.75;
176            
177 13         61 $o->{items} = $opt->{item_labels}; # used by wrapped_items()
178 13         75 $o->{wrapchrs} = $o->{twidth}/($o->{tsize}*$o->{ratio}); # used by wrapped_items()
179 13 50       70 if (defined $o->{items}) {
180 0         0 $o->{nlines} = $o->wrapped_items;
181             } else {
182 13 50       1027 croak "Option 'num_items' must be given\nStopped" unless $opt->{num_items};
183 13         94 $o->{nlines} = [ (1) x $opt->{num_items} ];
184             }
185            
186 13 100       72 $o->{spc} = defined($opt->{spacing}) ? $opt->{spacing} : 4;
187 13 50       63 $o->{vspc} = defined($opt->{vert_spacing}) ? $opt->{vert_spacing} : $o->{spc};
188 13 50       69 $o->{hspc} = defined($opt->{horz_spacing}) ? $opt->{horz_spacing} : $o->{spc} * 2;
189 13 100       85 $o->{dxicon} = defined($opt->{icon_width}) ? $opt->{icon_width} : $o->{tsize};
190 13 100       67 $o->{dyicon} = defined($opt->{icon_height}) ? $opt->{icon_height} : $o->{tsize};
191            
192 13         98 $o->{dx} = $o->{hspc} + $o->{dxicon} + $o->{hspc} + $o->{twidth} + $o->{hspc};
193 13 100       83 $o->{dyicon} = ($o->{dyicon} > $o->{tsize} ? $o->{dyicon} : $o->{tsize}); # dyicon always >= tsize
194 13         37 my $isize = $o->{dyicon} + 2 * $o->{vspc};
195              
196 13 50       58 croak "Option 'max_height' must be given\nStopped" unless defined $opt->{max_height};
197 13         34 $o->{height} = $opt->{max_height};
198 13         61 $o->{tmargin} = $o->{hsize} + 3 * $o->{vspc};
199 13         34 my $margins = $o->{tmargin} + 2 * $o->{vspc};
200 13         41 my $height = $o->{height} - $margins;
201 13 50       54 $height = 1 unless $height > 0;
202            
203             # distribute items amongst columns
204 13         24 my $sofar = 0;
205 13         23 my $column = 0;
206 13         20 my $max = 0;
207 13         25 my (@cols, @rowp, @rowh);
208 13         30 for (my $i = 0; $i <= $#{$o->{nlines}}; $i++) {
  238         594  
209 225         360 $rowp[$i] = $sofar;
210 225         502 my $itemh = 2* $o->{vspc} + $o->{nlines}[$i] * ($o->{tsize} + $o->{vspc});
211             #warn "$i nlines=",$o->{nlines}[$i],", itemh=$itemh, rowp=",$rowp[$i],"\n";
212 225         251 $sofar += $itemh;
213 225 100       1432 if ($sofar >= $height) {
214 6         10 $sofar -= $itemh;
215 6 50       694 $max = $sofar if $sofar > $max;
216 6         10 $column++;
217 6         10 $rowp[$i] = 0;
218 6         10 $sofar = $itemh;
219             }
220 225         314 $cols[$i] = $column;
221 225 100       380 if ($isize > $itemh) {
222 3         9 $rowh[$i] = $isize;
223 3         5 $sofar += ($isize - $itemh);
224             } else {
225 222         360 $rowh[$i] = $itemh;
226             }
227 225 100       640 $max = $sofar if $sofar > $max;
228             #warn "$i column=$column, diff=$diff ($isize - $itemh), sofar=$sofar, max=$max\n"
229             }
230            
231             #warn "max=$max, column=$column\n";
232 13         46 $o->{height} = $margins + $max;
233 13         54 $o->{start} = $o->{height} - $o->{tmargin} - 4 * $o->{vspc};
234 13         43 $o->{width} = $o->{hspc} + ($column+1) * $o->{dx};
235              
236             # for add_key_item()
237 13         38 $o->{cols} = \@cols; # the column number for each item
238 13         74 $o->{rowp} = \@rowp; # offset from top of column to top of item
239 13         36 $o->{current} = 0; # item to be shown
240            
241 13         92 return $o;
242             }
243              
244             =head2 new( [options] )
245              
246             C should be either a list of hash keys and values or a hash reference. Unlike other objects in this
247             series, a couple of these (C and C) are required. Another difference is that all options
248             occur within a flat space - there are no sub-groups. This is because it is expected that this module will only be
249             used as part of another Chart object, with these options passed as a group through that constructor.
250              
251             All values are in PostScript native units (1/72 inch).
252              
253             If C is an integer, the textual description will have one line per item. Where long descriptions are
254             needed, C can be given instead. It should refer to a list of the strings to be placed next to each
255             key icon. The constructor calculates the number of rows needed when this text is wrapped within C.
256             The text is only actually wrapped when B is called.
257              
258             =head3 background
259              
260             Background colour for the key rectangle. (Default: 1)
261              
262             =head3 file
263              
264             If C is not given, this probably should be. It is the PostScript::File object that holds the graph
265             being constructed. It is possible to specify this later, when calling B. (No default)
266              
267             =head3 glyph_ratio
268              
269             A kludge provided to fine-tune how well the text labels fit into the box. It is not possible to get the actual
270             width of proportional font strings from PostScript, so this gives the opportunity to guess an 'average width' for
271             particular fonts. (Default: 0.5)
272              
273             =head3 graph_paper
274              
275             If given, this should be a PostScript::Graph::Paper object. A GraphKey needs to know which GraphPaper
276             has allocated space for it. But the GraphPaper need the GraphKey to have already worked out how much space it
277             needs. The best solution is to create the GraphKey before the GraphPaper, then pass the latter to B.
278             (No default)
279              
280             =head3 horizontal_spacing
281              
282             The gaps between edges, icon and text. (Defaults to C)
283              
284             =head3 icon_height
285              
286             Vertical space for each icon. This will never be smaller than the height of the text. (Defaults to C)
287              
288             =head3 icon_width
289              
290             Amount of horizontal space to allow for the icon to the left of each label. (Defaults to C)
291              
292             =head3 item_labels
293              
294             As an alternative to specifying the number of items (which must be single lines of text), this takes an array ref
295             pointing to a list of all the key labels that will later be added. The same wrapping algorithm is applied to
296             both. The box dimensions are calculated from these, but the actual text printed should be passed seperately to
297             B.
298              
299             =head3 max_height
300              
301             The vertical space available for the key rectangle. GraphKey tries to fit as many items as possible within this
302             before adding another column. Required - there is no default.
303              
304             =head3 num_items
305              
306             The number of items that will be placed in the key. Required unless C is given.
307              
308             =head3 outline_color
309              
310             Colour of the box's outline. (Default: 0)
311              
312             =head3 outline_width
313              
314             Width of the box's outline. (Default: 0.75)
315              
316             =head3 spacing
317              
318             A larger value gives a less crowded feel, reduce it if you are short of space. Think printing leading. (Default: 4)
319              
320             =head3 text_color
321              
322             Colour of the text used in the body of the key. (Default: 0)
323              
324             =head3 text_font
325              
326             Font used for the key body text. (Default: "Helvetica")
327              
328             =head3 text_size
329              
330             Size of the font used for the key body text. (Default: 10)
331              
332             =head3 text_width
333              
334             Amount of room allowed for the text label on each key item. (Defaults to four times the font size)
335              
336             =head3 title
337              
338             The heading at the top of the key rectangle. (Default: "Key")
339              
340             =head3 title_color
341              
342             Colour of the key heading. (Default: 0)
343              
344             =head3 title_font
345              
346             The font used for the heading. (Default: "Helvetica-Bold")
347              
348             =head3 title_size
349              
350             Size of the font used for the heading. (Default: 12)
351              
352             =head3 vertical_spacing
353              
354             The gap between key items. (Defaults to C)
355              
356             =head1 OBJECT METHODS
357              
358             =cut
359              
360             sub width {
361 13     13 1 303 return shift()->{width};
362             }
363              
364             =head2 width()
365              
366             Return the width required for the key rectangle.
367              
368             =cut
369              
370             sub height {
371 0     0 1 0 return shift()->{height};
372             }
373              
374             =head2 height()
375              
376             Return the height required for the key rectangle.
377              
378             =cut
379              
380             sub build_key {
381 13     13 1 330 my ($o, $gp) = @_;
382 13 50       90 if (defined $gp) {
383 13         45 $o->{gp} = $gp;
384 13         97 $o->{ps} = $gp->file();
385             }
386 13 50       100 die "No PostScript::Graph::Paper object\nStopped" unless (ref($o->{gp}) eq "PostScript::Graph::Paper");
387              
388 13         97 my ($kx0, $ky0, $kx1, $ky1) = $o->{gp}->key_area();
389 13         70 my $offset = ($ky1 - $ky0 - $o->{height})/2;
390 13         40 $ky0 += $offset;
391 13         33 $ky1 = $ky0 + $o->{height};
392 13         87 my $textc = str($o->{tcolor});
393 13         131 my $headc = str($o->{hcolor});
394 13         111 my $outlinec = str($o->{ocolor});
395 13         108 my $fillc = str($o->{fcolor});
396            
397 13         581 $o->{ps}->add_to_page( <
398             graphkeydict begin
399             /kx0 $kx0 def
400             /ky0 $ky0 def
401             /kx1 $kx1 def
402             /ky1 $ky1 def
403             /kvspc $o->{vspc} def
404             /khspc $o->{hspc} def
405             /kdxicon $o->{dxicon} def
406             /kdyicon $o->{dyicon} def
407             /kdxtext $o->{twidth} def
408             /kdytext $o->{tsize} def
409             /kfont /$o->{tfont} def
410             /ksize $o->{tsize} def
411             /kcol $textc def
412             ($o->{title}) /$o->{hfont} $o->{hsize} $headc $o->{owidth} $outlinec $fillc keybox
413             end
414             END_CODE
415              
416 13         9575 PostScript::Graph::Key->ps_functions($o->{ps});
417             }
418              
419             =head2 build_key( [graph_paper] )
420              
421             This is where the position of the key area is fixed and the outline and heading are drawn. It must be called
422             before B.
423              
424             =cut
425              
426             sub add_key_item {
427 220     220 1 486 my ($o, $label, $code, $ps) = @_;
428             #warn "add_key_item($label)\n";
429 220 50       570 $label = "" unless (defined $label);
430 220 50       446 $code = "" unless (defined $code);
431 220 50       473 $o->{ps} = $ps if (defined $ps);
432 220 50       702 die "No PostScript::File object to write to\nStopped" unless (ref($o->{ps}) eq "PostScript::File");
433            
434 220 50       885 my @lines = $o->{items} ? $o->split_lines(ucfirst $label) : ucfirst($label);
435 220         564 my $tsize = $o->{tsize} + $o->{vspc};
436            
437 220         423 my $n = $o->{current};
438 220   100     868 my $col = $o->{cols}[$n] || 0;
439 220   100     685 my $row = $o->{rowp}[$n] || 0;
440 220         392 my $kdx = $col * $o->{dx};
441 220         412 my $kdy = $o->{start} - $row;
442             #
443             # TODO Centre the icon box within the text entry.
444             # $o->{rowh} holds the necessary height, but it needs putting into PostScript
445             #
446            
447             # The gstyledict kludge involving tppdy/tppdx is to ensure the point is shown centrally in the icon.
448             # This may not be what is always wanted, but was put in to accomodate arrows (without lines)
449 220         1692 $o->{ps}->add_to_page( <
450             graphkeydict begin
451             /kdx $kdx def
452             /kdy $kdy def
453             newpath
454             movetoicon
455             gstyledict begin
456             /tppdy ppdy def /ppdy 0 def
457             /tppdx ppdx def /ppdx 0 def
458             $code
459             stroke
460             /ppdy tppdy def
461             /ppdx tppdx def
462             end
463             movetotext
464             END_ITEM
465 220         249336 foreach my $line (@lines) {
466 220         514 $line =~ s/[(]/\\\(/g;
467 220         330 $line =~ s/[)]/\\\)/g;
468 220         1184 $o->{ps}->add_to_page("($line) show $tsize movedown\n");
469             }
470 220         14315 $o->{current}++;
471 220         764 $o->{ps}->add_to_page("end\n");
472             }
473              
474             =head2 add_key_item( label, code [, psfile] )
475              
476             =over 8
477              
478             =item label
479              
480             The text for this key item
481              
482             =item code
483              
484             Postscript code which draws the icon.
485              
486             =item psfile
487              
488             The PostScript::File object the code will be written to. If it was not given to B as either C or
489             C, it must be given here.
490              
491             =back
492              
493             A number of postscript variables are provided to help with the drawing. See for the full list.
494              
495             kix0 left edge of icon area
496             kiy0 bottom edge
497             kix1 right edge
498             kiy1 top edge
499              
500             Your code should use its own dictionary and can refer to dictionaries you have begun but not yet ended at this
501             point. For example, here is the code used by PostScript::Graph::XY. It calculates the mid point of a diagonal line
502             and draws it using the same functions used for the main chart. C etc. are provided by
503             PostScript::Graph::Style to change style settings, C just expects 'x y' and C expects an
504             array of coordinates followed by the greatest index allowed.
505              
506            
507             $graphkey->add_key_item( $line->{ytitle},
508             <
509             2 dict begin
510             /kpx kix0 kix1 add 2 div def
511             /kpy kiy0 kiy1 add 2 div def
512             point_outer
513             kpx kpy draw1point
514             [ kix0 kiy0 kix1 kiy1 ] 3
515             2 copy
516             line_outer drawxyline
517             line_inner drawxyline
518             point_inner
519             kpx kpy draw1point
520             end
521             END_KEY_ITEM
522              
523             =cut
524              
525             sub wrapped_items {
526 0     0 0 0 my $o = shift;
527 0 0       0 my @items = @_ ? @_ : @{$o->{items}};
  0         0  
528 0         0 my @split;
529 0         0 my $nchars = $o->{twidth}/($o->{tsize}*$o->{ratio});
530 0         0 foreach my $text (@items) {
531 0         0 push @split, scalar $o->split_lines($text, $nchars);
532             }
533 0         0 return \@split;
534             }
535             # Fit labels in $o->{items} array so they fit within $o->{twidth}
536             # Returning number of lines required in total.
537              
538              
539             sub split_lines {
540 0     0 0 0 my ($o, $text, $nchars) = @_;
541 0 0       0 $nchars = $o->{wrapchrs} unless defined $nchars;
542 0         0 my @split_text;
543 0         0 while ($text) {
544 0         0 my ($left, $right);
545 0 0       0 if (length $text <= $nchars) {
546 0         0 $left = $text;
547 0         0 $right = '';
548             } else {
549 0         0 $left = substr $text, 0, $nchars;
550 0         0 $right = substr $text, $nchars;
551 0         0 $left =~ s/\s+(\S*)$//;
552 0 0       0 $right = $1 . $right if $1;
553             }
554 0         0 push @split_text, $left;
555 0         0 $text = $right;
556             }
557 0         0 return @split_text;
558             }
559              
560             sub sum {
561 0     0 0 0 my $total = 0;
562 0         0 foreach my $item (@_) {
563 0         0 $total += $item;
564             }
565 0         0 return $total;
566             }
567              
568             sub ps_functions {
569 13     13 1 43 my ($class, $ps) = @_;
570 13         37 my $name = "GraphKey";
571 13 50       83 $ps->add_function( $name, <has_function($name));
572             /graphkeydict 20 dict def
573             graphkeydict begin
574             % _ title tfont tsize tcol boxw boxc fillc => _
575             /keybox {
576             gpaperdict begin
577             graphkeydict begin
578             newpath
579             kx0 ky0 moveto
580             kx1 ky0 lineto
581             kx1 ky1 lineto
582             kx0 ky1 lineto
583             closepath
584             gsave gpapercolor fill grestore
585             gpapercolor
586             setlinewidth
587             [ ] 0 setdash
588             stroke
589             /tcol exch def
590             /tsize exch def
591             /tfont exch def
592             tfont tsize tcol gpaperfont
593             kx0 kx1 add 2 div
594             ky1 tsize 1.2 mul sub
595             centered
596             end end
597             } bind def
598            
599             % _ => _
600             /movetoicon {
601             graphkeydict begin
602             /kix0 kx0 kdx add khspc add def
603             /kiy0 ky0 kdy add def
604             /kix1 kix0 kdxicon add def
605             /kiy1 kiy0 kdyicon add def
606             kix0 kiy0 moveto
607             end
608             } bind def
609            
610             % _ => _
611             /movetotext {
612             graphkeydict begin
613             gpaperdict begin
614             kfont ksize kcol gpaperfont
615             end
616             /ktx0 kx0 kdx add khspc add kdxicon add khspc add def
617             /kty0 ky0 kdy add def
618             /ktx1 ktx0 kdxtext add def
619             /kty1 kty0 kdyicon add def
620             ktx0 kty0 kty1 add 2 div ksize 2 div sub kvspc 2 div add moveto
621             end
622             } bind def
623              
624             % tsize => _
625             /movedown {
626             graphkeydict begin
627             /kty0 1 index kty0 exch sub def
628             /kty0 exch kty0 exch sub def
629             ktx0 kty0 kty1 add 2 div ksize 2 div sub kvspc 2 div add moveto
630             end
631             } bind def
632             end
633             END_FUNCTIONS
634             }
635              
636             =head3 ps_functions( ps )
637              
638             Normally, this is called in B, but is provided as a class function so the dictionary may be still
639             available even when a Key object is not required.
640              
641             =cut
642              
643             =head1 POSTSCRIPT CODE
644              
645             None of the postscript functions defined in this module would have application outside it. However, the following
646             variables are defined within the C dictionary and may be of some use.
647              
648             kix0 left edge of icon area
649             kiy0 bottom edge
650             kix1 right edge
651             kiy1 top edge
652             kvspc vertical spacing
653             khspc horizontal spacing
654             kdxicon width of icon area
655             kdyicon height of icon area
656             kdxtext width of text area
657             kdytext height of text area
658             kfont font used for text
659             ksize font size used
660             kcol colour of font used
661              
662             =cut
663              
664             =head1 BUGS
665              
666             Too much space is allocated to wrapped text if accompanied by a large icon.
667              
668             =head1 AUTHOR
669              
670             Chris Willmot, chris@willmot.org.uk
671              
672             =head1 SEE ALSO
673              
674             L, L and L for the other modules in this suite.
675              
676             L, L and L for modules that use this one.
677              
678             =cut
679              
680              
681             1;