Sign In/My Account | View Cart  
advertisement


Listen Print Discuss

Exegesis 7
by Damian Conway | Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13

Editor's note: this document is out of date and remains here for historic interest. See Synopsis 7 for the current design information.

Master Page, I am glad to see you...

Normally, form assumes that whatever data it is formatting is supposed to produce a single, arbitrarily long, unbroken piece of text. But form can also format data into multiple pages of fixed length and width, inserting customized, page-specific headers, footers, and pagefeeds for each page.

All these features are controlled by the the page option (or more precisely, by its various suboptions):


    print form
        :page{ :length( $page_len ),       # Default: 60 lines
               :width( $page_width ),      # Default: 78 columns
               :number( $first_page_num ), # Default: 1
               :header( &make_header ),    # Default: no header
               :footer( &make_footer ),    # Default: no footer
               :feed( &make_pagefeed ),    # Default: no pagefeed
               :body( &adjust_body ),      # Default: no chiropracty
             },
        $format,
        *@args;

Measure his woe the length and breadth of mine...

The :page{ :length(...) } suboption determines the number of output lines per page (including headers and footers). Normally, this suboption is set to infinity, which produces that single, arbitrarily long, unbroken page of text. But the suboption can be set to any positive integer value, to cause form to generate distinct pages of that many lines each.

The value of the :page{ :width(...) } suboption is used to determine the width of distributive fields and in some page body postprocessors. By default, this suboption is set to 78 (columns), but it may be set to any positive integer value.

The :page{ :number(...) } suboption specifies the current page number. By default it starts at 1, but may be set to any numeric value. This suboption is generally only of use in headers and footers (see below).

From the crown of his head to the sole of his foot...

The :page{ :header(...) } suboption specifies a hash containing a set of strings or subroutines that are to be used to create page headers. Each key of the hash indicates a particular kind of page that the corresponding value will provide the header for. For example:


    :header{ first => "           'The Tempest' by W. Shakespeare          ",
             last  => "                   -- The End --                    ",
             odd   => "Act $act, Scene $scene                              ",
             even  => "                                                    ",
             other => "          [Thys hedder intenshunally blanke]        ",
           }

Given the above specification, form will:

  • use the full title and author as the header of the first page,
  • write "-- The End --" across the top of the last page,
  • prepend the act and scene information to the start of any odd page (except, of course, the first or the last), and
  • provide an empty line as the header of any even page (except the last, if it happens to be even).

Note that, in this case, since we've provided specific headers for every odd and even page, the "other" header will never be used. On the other hand, if we'd specified:


    :header{ first => "           'The Tempest' by W. Shakespeare          ",
             other => "                                       'The Tempest'",
           }

then every page except the first would have just a right-justified title at the top.

Of course, if we want every page to have the same header, we can just write:


    :header{ other => "                                       'The Tempest'"}

But that's a little klunky, so form also accepts a single string instead of a hash, to specify a header to be used for every page:


    :header("                                       'The Tempest'")

Headers don't all have to be the same size either. For example, we might prefer a more imposing first header:


    :header{ first => "                  'The Tempest'                   \n"
                    ~ "                        by                        \n"
                    ~ "                  W. Shakespeare                  \n"
                    ~ "____________________________________________________",

             other => "                                       'The Tempest'",
           }

form simply notes the number of lines each header requires and then reduces the available number of lines within the page accordingly, so as to preserve the exact overall page length.

Often we'll need headers that aren't fixed strings. For example, we might want each page to include the appropriate page number. So instead of a string, we're allowed to specify a particular header as a subroutine. That subroutine is then called each time that particular header is required, and its return value is used as the required header.

When the subroutine is called, the current set of active formatting options are passed to it as a list of pairs. Typically, then, the subroutine will specify one or more named-only parameters corresponding to the options it cares about, followed by a starred hash parameter to collect the rest. For example if every page should have its (left-justified) page number for a header:


    :header( sub (+$page, *%_) { return $page{number}; } )

Of course, this is also an excellent candidate for the cleaner (but equivalent) syntax of placeholder variables in raw blocks:


    :header{ $^page{number}; }

And, naturally, we can mix-and-match static and dynamic headers:


    :header{ odd  => "                                        'The Tempest'",
             even => { $^page{number}; },
           }

Footers work in exactly the same way in almost all respects; the obvious exception being that they're placed at the end of a page, rather than the start.

Pagefeeds work the same way too. A pagefeed is a string that is placed between the footer of one page and the header of the next. They're like formfeeds, except they can be any string we choose (not just \c[FF]). They're called "pagefeeds" instead of "formfeeds" because they're placed between pages, not between calls to form.

Here's a complete example to illustrate the full set of features:


    my @tobe = slurp 'Soliloquy.txt'  err die;

    my %page = (
        :length(15),
        :header{ first => "Hamlet's soliloquy begins...\n\n",
                 odd   => "Hamlet's soliloquy continues...\n\n",
                 even  => { form '{>>{*}>>}', "Hamlet's soliloquy continues...\n\n"; },
                 last  => "Hamlet's soliloquy concludes...\n\n",
               },
        :footer{
                 last  => { form "\n{||{*}||}", "END OF TEXT"; }
                 other => { form "\n{>>{*}>>}", "../"~($^page{number}+1); },
               },
        :feed("\f"),
    );

    print form
        :page(%page),
        '{]]]]]}  {"{*}"}  {[[[[[}',
        [1..@tobe], @tobe,  [1..@tobe];

which prints:


    Hamlet's soliloquy begins...

          1  To be, or not to be -- that is the question:          1
          2  Whether 'tis nobler in the mind to suffer             2
          3  The slings and arrows of outrageous fortune           3
          4  Or to take arms against a sea of troubles             4
          5  And by opposing end them. To die, to sleep --         5
          6  No more -- and by a sleep to say we end               6
          7  The heartache, and the thousand natural shocks        7
          8  That flesh is heir to. 'Tis a consummation            8
          9  Devoutly to be wished. To die, to sleep --            9
         10  To sleep -- perchance to dream: ay, there's the rub,  10
         11  For in that sleep of death what dreams may come       11

                                                                      ../2
    ^L
                                           Hamlet's soliloquy continues...

         12  When we have shuffled off this mortal coil,           12
         13  Must give us pause. There's the respect               13
         14  That makes calamity of so long life.                  14
         15  For who would bear the whips and scorns of time,      15
         16  Th' oppressor's wrong, the proud man's contumely      16
         17  The pangs of despised love, the law's delay,          17
         18  The insolence of office, and the spurns               18
         19  That patient merit of th' unworthy takes,             19
         20  When he himself might his quietus make                20
         21  With a bare bodkin? Who would fardels bear,           21
         22  To grunt and sweat under a weary life,                22

                                                                      ../3
    ^L
    Hamlet's soliloquy continues...

         23  But that the dread of something after death,          23
         24  The undiscovered country, from whose bourn            24
         25  No traveller returns, puzzles the will,               25
         26  And makes us rather bear those ills we have           26
         27  Than fly to others that we know not of?               27
         28  Thus conscience does make cowards of us all,          28
         29  And thus the native hue of resolution                 29
         30  Is sicklied o'er with the pale cast of thought,       30
         31  And enterprise of great pitch and moment              31
         32  With this regard their currents turn awry             32
         33  And lose the name of action. -- Soft you now,         33

                                                                      ../4
    ^L
    Hamlet's soliloquy concludes...

         34  The fair Ophelia! -- Nymph, in thy orisons            34
         35  Be all my sins remembered.                            35
              
              
              
              
              
              
              
              
              
              
                                 END OF TEXT
    ^L

Note, in particular, the nested calls to form within some of the subroutines – to center or right-justify a particular header or footer. Permitting just this kind of "recursive" formatting is one of the main reasons Perl 5's built-in format has become the (reentrant) form subroutine in Perl 6.

Do to this body what extremes you can...

Sometimes it's useful to be able to grab the entire body of a page (i.e. the contents of the page between the header and footer) after it's been formatted together. For example, we might wish to centre those contents, or to crop them at a particular column.

To this end, the :page{ :body(...) } suboption allows us to specify a page body post-processor. That is, a subroutine or format that lays out the page's formatted text between the page's header and footer. Like the :header, :footer, and :feed suboptions, the :body suboption can take either a closure, a hash, or a string.

If the value of the :body suboption is a string or a hash of pairs, the text of the body is (recursively) form'ed using that string (or those string values) as its format. A very common usage is to arrange for the formatted text to be horizonally and vertically centred on each page:


    :body('{=I{*}I=}')

A more sophisticated variation on this is to use a hash to insert a left or right "gutter" for each page:


    $gutter = " " x $gutter_width;

    :body{ odd   =>  $gutter ~ '{"""{*}"""}',
           even  =>  '{"""{*}"""}' ~ $gutter,
         }

On the other hand, if the value of the :body suboption is closure, the body text is passed to that closure as an array of lines. A second array is also passed in, containing as many newlines as would be needed to pad out the body text to the correct number of lines for the page. Finally, the current formatting options are passed as a list of pairs. As with the :header etc. suboption, the closure is expected to return a single string (representing the final formatting of the page body).

For example, to add line numbers to the text each page (but not to the headers or footers or filler lines):


    sub numerate (@lines, @fill, +$page, *%_) {

        # Remember line numbers from call to call...
        state $linenum = 1;
    
        # Compute range of line numbers
        my @linenums = ($linenum .. $linenum+@lines-1);

        # Reformat body lines verbatim,
        # with a left-justified line number before each...
        my $body = form '{[[[[} {"""{*}"""}',
                         @linenums, @lines,
                        *@fill;

        # Update the final line number and return the new body text...
        $linenum += @lines;
        return $body;
    }

    print form
        :page{ :body(&numerate),
               :header("\n==========\n\n"),
               :length(12),
             },
        # Left-justify the Briton...
        "{[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[}",
        %soliloquy{RichardIII},
                         # Right-justify the Dane...
        "                 {]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]}",
                          %soliloquy{Hamlet};

which produces:


    ==========
                
    1      Now is the winter of our discontent /  
    2      Made glorious summer by this sun of    
    3      York; / And all the clouds that lour'd 
    4      upon our house / In the deep bosom of  
    5      the ocean buried. / Now are our brows  
    6      bound with victorious wreaths; / Our   
    7      bruised arms hung up for monuments; /  
    8      Our stern alarums changed to merry     
    9      meetings, / Our dreadful marches to    
                
    ==========
                
    10     delightful measures. Grim-visaged war  
    11     hath smooth'd his wrinkled front; / And
    12     now, instead of mounting barded steeds 
    13     / To fright the souls of fearful       
    14     adversaries, / He capers nimbly in a   
    15     lady's chamber.                        
                
                
                
                
    ==========
                
    16                      To be, or not to be -- that is the question: /
    17                         Whether 'tis nobler in the mind to suffer /
    18                       The slings and arrows of outrageous fortune /
    19                         Or to take arms against a sea of troubles /
    20                       And by opposing end them. To die, to sleep --
    21                         / No more -- and by a sleep to say we end /
    22                      The heartache, and the thousand natural shocks
    23                      / That flesh is heir to. 'Tis a consummation /
    24                        Devoutly to be wished. To die, to sleep -- /
                
    ==========
                
    25                         To sleep -- perchance to dream: ay, there's
    26                          the rub, / For in that sleep of death what
    27                         dreams may come / When we have shuffled off
    28                             this mortal coil, / Must give us pause.
    29                        There's the respect / That makes calamity of
    30                                                       so long life.
                   
                   
                   
                   
                   
                   
                    

Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13

Next Pagearrow