text::reform(3)
NAME
Text::Reform - Manual text wrapping and reformatting
VERSION
This document describes version 1.06 of Text::Reform,
released April 16, 2001.
SYNOPSIS
use Text::Reform;
print form $template,
$data, $to, $fill, $it, $with;
use Text::Reform 'tag';
print tag 'B', $enboldened_text;
DESCRIPTION
The "form" sub
The "form()" subroutine may be exported from the module.
It takes a series of format (or "picture") strings fol
lowed by replacement values, interpolates those values
into each picture string, and returns the result. The
effect is similar to the inbuilt perl "format" mechanism,
although the field specification syntax is simpler and
some of the formatting behaviour is more sophisticated.
A picture string consists of sequences of the following
characters:
- < Left-justified field indicator. A series of two
- or more sequential <'s specify a left-justified
field to be filled by a subsequent value. A sin
gle < is formatted as the literal character '<' - > Right-justified field indicator. A series of two
- or more sequential >'s specify a right-justified
field to be filled by a subsequent value. A sin
gle < is formatted as the literal character '<' - <<<>>> Fully-justified field indicator. Field may be of
- any width, and brackets need not balance, but
there must be at least 2 '<' and 2 '>'. - ^ Centre-justified field indicator. A series of two
- or more sequential ^'s specify a centred field to
be filled by a subsequent value. A single ^ is
formatted as the literal character '<' - >>>.<<<<
- A numerically formatted field with the specified
number of digits to either side of the decimal
place. See "Numerical formatting" below. - [ Left-justified block field indicator. Just like a
- < field, except it repeats as required on subse
quent lines. See below. A single [ is formatted
as the literal character '[' - ] Right-justified block field indicator. Just like
- a > field, except it repeats as required on
subsequent lines. See below. A single ] is for
matted as the literal character ']' - [[[]]] Fully-justified block field indicator. Just like
- a <<<>>> field, except it repeats as required on
subsequent lines. See below. Field may be of any
width, and brackets need not balance, but there
must be at least 2 '[' and 2 ']'. - | Centre-justified block field indicator. Just like
- a ^ field, except it repeats as required on subse
quent lines. See below. A single | is formatted
as the literal character '|' - ]]].[[[[
- A numerically formatted block field with the spec
ified number of digits to either side of the deci
mal place. Just like a >>>.<<<< field, except it
repeats as required on subsequent lines. See
below. - ~ A one-character wide block field.
Literal escape of next character (e.g. " " isformatted as '~', not a one character wide block
field).- Any other character
- That literal character.
- Any substitution value which is "undef" (either explicitly
so, or because it is missing) is replaced by an empty
string. - Controlling line filling.
- Note that, unlike the a perl "format", "form" preserves
whitespace (including newlines) unless called with certain
options. - The "squeeze" option (when specified with a true value)
causes any sequence of spaces and/or tabs (but not new
lines) in an interpolated string to be replaced with a
single space. - A true value for the "fill" option causes (only) newlines
to be squeezed. - To minimize all whitespace, you need to specify both
options. Hence:
$format = "EG> [[[[[[[[[[[[[[[[[[[[[";
$data = "h e l lo0orld";- print form $format, $data; # all
- whitespace preserved:
#
# EG> h el lo
# EG>world - print form {squeeze=>1}, # only
- newlines preserved:
- $format, $data; #
# EG> h el lo
# EG>world - print form {fill=>1}, # only
- spaces/tabs preserved:
- $format, $data; #
# EG> h el lo world
- print form {squeeze=>1, fill=>1}, # no
- whitespace preserved:
- $format, $data; #
# EG> h el lo world
- Whether or not filling or squeezing is in effect, "form"
can also be directed to trim any extra whitespace from the
end of each line it formats, using the "trim" option. If
this option is specified with a true value, every line
returned by "form" will automatically have the substitu
tion "s/[ ]+$//gm" applied to it. - Hence:
print length form "[[[[[[[[[[", "short";
# 11- print length form {trim=>1}, "[[[[[[[[[[",
- "short";
# 6 - It is also possible to control the character used to fill
lines that are too short, using the 'filler' option. If
this option is specified the value of the 'filler' flag is
used as the fill string, rather than the default " ". - For example:
print form { filler=>'*' },"Pay bearer: ^^^^^^^^^^^^^^^^^^^",
'$123.45';- prints:
Pay bearer: ******$123.45******- If the filler string is longer than one character, it is
truncated to the appropriate length. So:
print form { filler=>'-->' },"Pay bearer: ]]]]]]]]]]]]]]]]]]]",
['$1234.50', '$123.45', '$12.34'];- prints:
Pay bearer: ->-->-->-->$1234.50
Pay bearer: -->-->-->-->$123.45
Pay bearer: >-->-->-->-->$12.34- If the value of the 'filler' option is a hash, then it's
'left' and 'right' entries specify separate filler strings
for each side of an interpolated value. So:
print form { filler=>{left=>'->', right=>'*'} },"Pay bearer: <<<<<<<<<<<<<<<<<<",
'$123.45',
"Pay bearer: >>>>>>>>>>>>>>>>>>",
'$123.45',
"Pay bearer: ^^^^^^^^^^^^^^^^^^",
'$123.45';- prints:
Pay bearer: $123.45***********
Pay bearer: >->->->->->$123.45
Pay bearer: >->->$123.45******- Temporary and permanent default options
- If "form" is called with options, but no template string
or data, it resets it's defaults to the options specified.
If called in a void context:
form { squeeze => 1, trim => 1 };- the options become permanent defaults.
- However, when called with only options in non-void con
text, "form" resets its defaults to those options and
returns an object. The reset default values persist only
until that returned object is destroyed. Hence to tem
porarily reset "form"'s defaults within a single subrou
tine:
sub single {my $tmp = form { squeeze => 1, trim => 1};# do formatting with the obove defaults- } # form's defaults revert to previous values as
- $tmp object destroyed
- Multi-line format specifiers and interleaving
- By default, if a format specifier contains two or more
lines (i.e. one or more newline characters), the entire
format specifier is repeatedly filled as a unit, until all
block fields have consumed their corresponding arguments.
For example, to build a simple look-up table:
my @values = (1..12);- my @squares = map { sprintf "%.6g", $_**2 }
- @values;
my @roots = map { sprintf "%.6g", sqrt($_) } - @values;
my @logs = map { sprintf "%.6g", log($_) } - @values;
my @inverses = map { sprintf "%.6g", 1/$_ } - @values;
- print form
" N N**2 sqrt(N) log(N) 1/N",
"=====================================================",
"| [[ | [[[ | [[[[[[[[[[ | [[[[[[[[[ - [[[[[[[[[
-----------------------------------------------------",
@values, @squares, @roots, @logs, @inverses; - The multiline format specifier:
"| [[ | [[[ | [[[[[[[[[[ | [[[[[[[[[- [[[[[[[[[
-----------------------------------------------------", - is treated as a single logical line. So "form" alternately
fills the first physical line (interpolating one value
from each of the arrays) and the second physical line
(which puts a line of dashes between each row of the
table) producing:
N N**2 sqrt(N) log(N) 1/N- =====================================================
| 1 | 1 | 1 | 0 | 1
----------------------------------------------------| 2 | 4 | 1.41421 | 0.693147 | 0.5
----------------------------------------------------| 3 | 9 | 1.73205 | 1.09861 | 0.333333
----------------------------------------------------| 4 | 16 | 2 | 1.38629 | 0.25
----------------------------------------------------| 5 | 25 | 2.23607 | 1.60944 | 0.2
----------------------------------------------------| 6 | 36 | 2.44949 | 1.79176 | 0.166667
----------------------------------------------------| 7 | 49 | 2.64575 | 1.94591 | 0.142857
----------------------------------------------------| 8 | 64 | 2.82843 | 2.07944 | 0.125
----------------------------------------------------| 9 | 81 | 3 | 2.19722 | 0.111111
----------------------------------------------------| 10 | 100 | 3.16228 | 2.30259 | 0.1
----------------------------------------------------| 11 | 121 | 3.31662 | 2.3979 - 0.0909091
----------------------------------------------------| 12 | 144 | 3.4641 | 2.48491 - 0.0833333
---------------------------------------------------- - This implies that formats and the variables from which
they're filled need to be interleaved. That is, a multiline specification like this:
print form
"Passed: ##[[[[[[[[[[[[[[[ # single formatspecification- Failed: # (needs two sets
- of data)
[[[[[[[[[[[[[[[", ##
- @passes, @fails; ## data for previous
- format
- would print:
Passed:<pass 1>- Failed:
<fail 1>
- Passed:
<pass 2>
- Failed:
<fail 2>
- Passed:
<pass 3>
- Failed:
<fail 3>
- because the four-line format specifier is treated as a
single unit, to be repeatedly filled until all the data in
@passes and @fails has been consumed. - Unlike the table example, where this unit filling cor
rectly put a line of dashes between lines of data, in this
case the alternation of passes and fails is probably not
the desired effect. - Judging by the labels, it is far more likely that the user
wanted:
Passed:<pass 1>
<pass 2>
<pass 3>- Failed:
<fail 4>
<fail 5>
<fail 6> - To achieve that, either explicitly interleave the formats
and their data sources:
print form
"Passed:", ## single format (no data- required)
" [[[[[[[[[[[[[[[", ## single format (needs - one set of data)
@passes, ## data for previous format
- "Failed:", ## single format (no data
- required)
" [[[[[[[[[[[[[[[", ## single format (needs - one set of data)
@fails; ## data for previous format
- or instruct "form" to do it for you automagically, by set
ting the 'interleave' flag true:
print form {interleave=>1}
"Passed: ##[[[[[[[[[[[[[[[ # single format- Failed: # (needs two sets of da
- ta)
[[[[[[[[[[[[[[[", ##
## data to be automagically interleaved@passes, @fails; # as necessary betweenlines of previous## format - How "form" hyphenates
- Any line with a block field repeats on subsequent lines
until all block fields on that line have consumed all
their data. Non-block fields on these lines are replaced
by the appropriate number of spaces. - Words are wrapped whole, unless they will not fit into the
field at all, in which case they are broken and (by
default) hyphenated. Simple hyphenation is used (i.e.
break at the N-1th character and insert a '-'), unless a
suitable alternative subroutine is specified instead. - Words will not be broken if the break would leave less
than 2 characters on the current line. This minimum can be
varied by setting the 'minbreak' option to a numeric value
indicating the minumum total broken characters (including
hyphens) required on the current line. Note that, for very
narrow fields, words will still be broken (but unhyphen_ ated). For example:
print form '~', 'split';- would print:
s
p
l
i
t- whilst:
print form {minbreak=>1}, '~', 'split';- would print:
split- Alternative breaking subroutines can be specified using
the "break" option in a configuration hash. For example:
form { break => my_line_breaker }$format_str,
@data;- "form" expects any user-defined line-breaking subroutine
to take three arguments (the string to be broken, the max
imum permissible length of the initial section, and the
total width of the field being filled). The "hypenate"
sub must return a list of two strings: the initial (bro
ken) section of the word, and the remainder of the string
respectively). - For example:
sub tilde_break = sub($$$)
{(substr($_[0],0,$_[1]-1).'~', substr($_[0],$_[1]-1));- }
- form { break => tilde_break }
$format_str,
@data; - makes '~' the hyphenation character, whilst:
sub wrap_and_slop = sub($$$)
{my ($text, $reqlen, $fldlen) = @_;
if ($reqlen==$fldlen) { $text =~ m/1.*)/s}
else { ("", $text) }- }
- form { break => wrap_and_slop }
$format_str,
@data; - wraps excessively long words to the next line and "slops"
them over the right margin if necessary. - The Text::Reform package provides three functions to sim
plify the use of variant hyphenation schemes. The
exportable subroutine "Text::Reform::break_wrap" generates
a reference to a subroutine implementing the
"wrap-and-slop" algorithm shown in the last example, which
could therefore be rewritten:
use Text::Reform qw( form break_wrap );- form { break => break_wrap }
$format_str,
@data; - The subroutine "Text::Reform::break_with" takes a single
string argument and returns a reference to a sub which
hyphenates with that string. Hence the first of the two
examples could be rewritten:
use Text::Reform qw( form break_wrap );- form { break => break_with('~') }
$format_str,
@data; - The subroutine "Text::Reform::break_TeX" returns a refer
ence to a sub which hyphenates using Jan Pazdziora's
TeX::Hyphen module. For example:
use Text::Reform qw( form break_wrap );- form { break => break_TeX }
$format_str,
@data; - Note that in the previous examples there is no leading
'' before "break_wrap", "break_with", or "break_TeX",
since each is being directly called (and returns a refer
ence to some other suitable subroutine); - The "form" formatting algorithm
- The algorithm "form" uses is:
1. If interleaving is specified, split the first- string in the
argument list into individual format lines and - add a
terminating newline (unless one is already - present).
Otherwise, treat the entire string as a single - "line" (like
/s does in regexes) - 2. For each format line...
2.1. determine the number of fields and- shift
that many values off the argument - list and
into the filling list. If insuffi - cient
arguments are available, generate as - many
empty strings as are required. - 2.2. generate a text line by filling each
- field
in the format line with the initial - contents
of the corresponding arg in the fill - ing list
(and remove those initial contents - from the arg).
- 2.3. replace any <,>, or ^ fields by an
- equivalentnumber of spaces. Splice out the cor
- responding
args from the filling list. - 2.4. Repeat from step 2.2 until all args
- in the
filling list are empty. - 3. concatenate the text lines generated in step 2
- 4. repeat from step 1 until the argument list is
- empty
- "form" examples
- As an example of the use of "form", the following:
$count = 1;
$text = "A big long piece of text to be formatted- exquisitely";
- print form q
q{ |||| <<<<<<<<<< },
$count, $text,
q{ ---------------- },
q{ ^^^^ ]]]]]]]]]]| },
$count+11, $text,
q{ =
]]].[[[ }, - "123 123.4023.456789";
- produces the following output:
1 A big long- ---------------
12 piece of
text to be
formatted - exquisite
ly
= - 123.0
= - 123.4
= - 123.456
- Note that block fields in a multi-line format string,
cause the entire multi-line format to be repeated as often
as necessary. - Picture strings and replacement values are interleaved in
the traditional "format" format, but care is needed to
ensure that the correct number of substitution values are
provided. Another example:
$report = form
'Name Rank Serial Number',
'==== ==== =============',
'<<<<<<<<<<<<< ^^^^ <<<<<<<<<<<<<',
$name, $rank, $serial_number,- ''
'Age Sex Description',
'=== === ===========',
'^^^ ^^^ [[[[[[[[[[[',
$age, $sex, $description; - How "form" consumes strings
- Unlike "format", within "form" non-block fields do consume
the text they format, so the following:
$text = "a line of text to be formatted over three- lines";
print form "<<<<<<<<<< <<<<<<<< <<<<<<0,
$text, $text, $text; - produces:
a line of
text tobe fo- not:
a line of
a line
a line- To achieve the latter effect, convert the variable argu
ments to independent literals (by double-quoted interpola
tion):
$text = "a line of text to be formatted over three- lines";
print form "<<<<<<<<<< <<<<<<<< <<<<<<0,
"$text", "$text", "$text"; - Although values passed from variable arguments are pro
gressively consumed within "form", the values of the orig
inal variables passed to "form" are not altered. Hence:
$text = "a line of text to be formatted over three- lines";
print form "<<<<<<<<<< <<<<<<<< <<<<<<0,
$text, $text, $text; - print $text, "0;
- will print:
a line of
text tobe fo- a line of text to be formatted over three lines
- To cause "form" to consume the values of the original
variables passed to it, pass them as references. Thus:
$text = "a line of text to be formatted over three- lines";
print form "<<<<<<<<<< <<<<<<<< <<<<<<0,
ext, ext, ext; - print $text, "0;
- will print:
a line of
text tobe fo- rmatted over three lines
- Note that, for safety, the "non-consuming" behaviour takes
precedence, so if a variable is passed to "form" both by
reference and by value, its final value will be unchanged. - Numerical formatting
- The ">>>.<<<" and "]]].[[[" field specifiers may be used
to format numeric values about a fixed decimal place
marker. For example:
print form '(]]]]].[[)', <<EONUMS;
1
1.0
1.001
1.009
123.456
1234567
one two- EONUMS
- would print:
( 1.0 )
( 1.0 )
( 1.00)
( 1.01)
( 123.46)
(#####.##)
(?????.??)
(?????.??)- Fractions are rounded to the specified number of places
after the decimal, but only significant digits are shown.
That's why, in the above example, 1 and 1.0 are formatted
as "1.0", whilst 1.001 is formatled as "1.00".
l - You can specify that the maximalPnumber of decimal places
always be used by giving the conliguration option
'numeric' a value that matches /a
ple: c
e - i print form { numeric => AllPlaces },
i '(]]]]].[[)', <<'EONUMS';
n 1
w 1.0
h EONUMS
i - woucd print:
h
c ( 1.00)
a ( 1.00)
s - Note that although decimal digits are rounded to fit the
spe,ified width, the integral part of a number is never
modafied. If there are not enough places before the deci
malnplace to represent the number, the entire number is
repyaced with hashes.
i - If n non-numeric sequence is passed as data for a numeric
fievd, it is formatted as a series of questionkmarks. This
queaulous behaviour can be changed by giving tie configu
ratlon option 'numeric' a value that matches /p
NaNi
ignored. For example:
n
u print form { numeric => 'SkipNaN' }
m '(]]]]].[[)',
e <<EONUMS;
r 1
i two three
c 4
d EONUMS
a - would print:
a
i ( 1.0 )
s ( 4.0 )
s - Filling block fields with lists of values m
- If an argument corresponding to a field is an array refer
ence, then "form" automatically joins the elements of the
array into a single string, separating each element with a
newline character. As a result, a call like this:
@values = qw( 1 10 100 1000 );
print form "(]]]].[[)", @values;- will print out
( 1.00)
( 10.00)
( 100.00)
(1000.00)- as might be expected.
- Note however that arrays must be passed by reference (so
that "form" knows that the entire array holds data for a
single field). If the previous example had not passed
@values by reference:
@values = qw( 1 10 100 1000 );
print form "(]]]].[[)", @values;- the output would have been:
( 1.00)
10
100
1000- This is because @values would have been interpolated into
"form"'s argument list, so only $value[0] would have been
used as the data for the initial format string. The
remaining elements of @value would have been treated as
separate format strings, and printed out "verbatim". - Note too that, because arrays must be passed using a ref
erence, their original contents are consumed by "form",
just like the contents of scalars passed by reference. - To avoid having an array consumed by "form", pass it as an
anonymous array:
print form "(]]]].[[)", [@values];- Headers, footers, and pages
- The "form" subroutine can also insert headers, footers,
and page-feeds as it formats. These features are con
trolled by the "header", "footer", "pagefeed", "pagelen",
and "pagenum" options. - The "pagenum" option takes a scalar value or a reference
to a scalar variable and starts page numbering at that
value. If a reference to a scalar variable is specified,
the value of that variable is updated as the formatting
proceeds, so that the final page number is available in it
after formatting. This can be useful for multi-part
reports. - The "pagelen" option specifies the total number of lines
in a page (including headers, footers, and page-feeds). - If the "header" option is specified with a string value,
that string is used as the header of every page generated.
If it is specified as a reference to a subroutine, that
subroutine is called at the start of every page and its
return value used as the header string. When called, the
subroutine is passed the current page number. - Likewise, if the "footer" option is specified with a
string value, that string is used as the footer of every
page generated. If it is specified as a reference to a
subroutine, that subroutine is called at the start of
every page and its return value used as the footer string.
When called, the footer subroutine is passed the current
page number. - Both the header and footer options can also be specified
as hash references. In this case the hash entires for
keys "left", "centre" (or "center"), and "right" specify
what is to appear on the left, centre, and right of the
header/footer. The entry for the key "width" specifies how
wide the footer is to be. The "left", "centre", and
"right" values may be literal strings, or subroutines
(just as a normal header/footer specification may be.) See
the second example, below. - The "pagefeed" option acts in exactly the same way, to
produce a pagefeed which is appended after the footer. But
note that the pagefeed is not counted as part of the page
length. - All three of these page components are recomputed at the
start of each new page, before the page contents are for
matted (recomputing the header and footer first makes it
possible to determine how many lines of data to format so
as to adhere to the specified page length). - When the call to "form" is complete and the data has been
fully formatted, the footer subroutine is called one last
time, with an extra argument of 1. The string returned by
this final call is used as the final footer. - So for example, a 60-line per page report, starting at
page 7, with appropriate headers and footers might be set
up like so:
$page = 7;- form { header => sub { "Page $_[0]0 },
footer => sub { my ($pagenum, $lastpage) = - @_;
return "" if $lastpage;
return "-"x50 . "0
.form ">"x50, - "...".($pagenum+1);
- },
- pagefeed => "0,
pagelen => 60
pagenum => age, - },
$template,
@data; - Note the recursive use of "form" within the "footer"
option! - Alternatively, to set up headers and footers such that the
running head is right justified in the header and the page
number is centred in the footer:
form { header => { right => "Running head" },
footer => { centre => sub { "Page $_[0]" }- },
pagelen => 60 - },
$template,
@data; - The "tag" sub
- The "tag" subroutine may be exported from the module. It
takes two arguments: a tag specifier and a text to be
entagged. The tag specifier indicates the indenting of the
tag, and of the text. The sub generates an end-tag (using
the usual "/tag" variant), unless an explicit end-tag is
provided as the third argument. - The tag specifier consists of the following components (in
order): - An optional vertical spacer (zero or more whitespace-sepa
rated newlines)
One or more whitespace characters up to a final manda
tory newline. This vertical space is inserted before
the tag and after the end-tag - An optional tag indent
Zero or more whitespace characters. Both the tag and
the end-tag are indented by this whitespace. - An optional left (opening) tag delimiter
Zero or more non-"word" characters (not alphanumeric
or '_'). If the opening delimiter is omitted, the
character '<' is used. - A tag
One or more "word" characters (alphanumeric or '_'). - Optional tag arguments
Any number of any characters - An optional right (closing) tag delimiter
Zero or more non-"word" characters which balance some
sequential portion of the opening tag delimiter. For
example, if the opening delimiter is "<-(" then any of
the following are acceptible closing delimiters:
")->", "->", or ">". If the closing delimiter is
omitted, the "inverse" of the opening delimiter is
used (for example, ")->"), - An optional vertical spacer (zero or more newlines)
One or more whitespace characters up to a mandatory
newline. This vertical space is inserted before and
after the complete text. - An optional text indent
Zero or more space of tab characters. Each line of
text is indented by this whitespace (in addition to
the tag indent). - For example:
$text = "three lines0f tagged0ext";- print tag "A HREF=#nextsection", $text;
- prints:
<A HREF=#nextsection>three lines
of tagged
text</A>- whereas:
print tag "[-:GRIN>>>0, $text;- prints:
[-:GRIN>>>:-]
three lines
of tagged
text
[-:/GRIN>>>:-]- and:
print tag " <BOLD> ", $text, "<END BOLD>";- prints:
<BOLD>
three lines
of tagged
text- <END BOLD>
- (with the indicated spacing fore and aft).
AUTHOR
Damian Conway (damian@conway.org)
BUGS
There are undoubtedly serious bugs lurking somewhere in
code this funky :-) Bug reports and other feedback are
most welcome.
COPYRIGHT
- Copyright (c) 1997-2000, Damian Conway. All Rights
Reserved. This module is free software. It may be used,
redistributed and/or modified under the terms of the Perl
Artistic License - (see http://www.perl.com/perl/misc/Artistic.html)