From: dennisp@informix.com (Dennis Pimple)
Newsgroups: comp.databases.informix
Subject: Re: different report length
Date: 10 Sep 94 22:13:16 GMT

Peter Schubert <100043.1556@CompuServe.COM> writes:

>We need diffent report length on unknown printers.
>(e.g. Lineprinter Length 72, Laserprinter Length 66 ...)

>Is there a way to use variables for the PAGE LENGTH parameter ???

No, BUT ...

Below are two attacks I've made concerning using a single REPORT
function break at different page lengths. The first is a complete
program and uses PAGE LENGTH 1 and a lot of duplicate code in each
report control block to handle page breaks. As a demo, it handles
either 24 or 60 page output (for screen or printer output, natch)

The second is a REPORT function from an actual report I wrote last week
for a client that wanted the report to go to either legal or standard
paper (78 or 66 lines, on this system). It's not as complete as the
first, and doesn't show how to handle page footers, although I'm sure
it wold be doable with a bit of duplicate code and LINENO testing. It
uses the idea of setting PAGE LENGTH to the highest desired number and
TOP OF PAGE "^L", which is used to send a top-of-page signal when
breaking pages rather than the ole' Informix standard of repeated blank
lines to fill out the pages. I hope it's commented well enough to be of
use.

And if Walt is watching, he should feel free to add these to the
repository.

=======================================================================
Dennis J. Pimple         dennisp@informix.com    Opinions expressed
Senior Consultant        --------------------    are mine, and do not
Informix Software Inc    Voice:  303-850-0210    necessarily reflect
Denver Colorado USA      Fax:    303-779-4025    those of my employer.

-------------------- CUT HERE FOR scr_prt.4gl -------------------------
###########     #   INFORMIX PROFESSIONAL SERVICES
#######     #  ##   Denver, Colorado
######     #  ###   ==================================================
#####     #  ####   File: %M%  SCCS: %I% %P%
####     #  #####   Program: scr_prt.4go
###     #  ######   Client: Example
##     #  #######   Author: Dennis J Pimple
#     ###########   Date: %G% %U%
# Example of how to use the same REPORT function to run to either
# screen or printer, with appropriate page breaks.
# The general approach we take is to handle PAGE HEADERs & TRAILERs
# in the ON EVERY ROW section of the REPORT, to avoid any forced page
# breaks. This example will output 60-line pages to a printer,
# or 23-line pages to a screen (70 lines of report output)

MAIN
DEFINE outpt    CHAR(1)     # Screen/Printer
DEFINE x        SMALLINT    # Loop counter
DEFINE y        SMALLINT    # Loop counter

CLEAR SCREEN

LET outpt = NULL
WHILE outpt IS NULL
    PROMPT "Run to Screen or Printer (S/P)? " FOR CHAR outpt
    LET outpt = UPSHIFT(outpt)
    IF outpt IS NULL OR outpt NOT MATCHES "[SP]" THEN
        ERROR "Type S, P, or INTERRUPT to quit"
        LET outpt = NULL
    END IF
END WHILE

IF outpt = "P" THEN
    START REPORT rpt_scr_ptr TO PRINTER
ELSE
    START REPORT rpt_scr_ptr TO PIPE " pg"
END IF

FOR x = 1 TO 10
    FOR y = 1 TO 7
        OUTPUT TO REPORT rpt_scr_ptr(outpt, x, y)
    END FOR
END FOR

FINISH REPORT rpt_scr_ptr

END MAIN

REPORT rpt_scr_ptr(outpt, x, y)
DEFINE outpt    CHAR(1)     # File or Printer
DEFINE x        SMALLINT
DEFINE y        SMALLINT
DEFINE pglgth   SMALLINT    # Page Length
DEFINE pgno     SMALLINT    # Page Number (instead of PAGENO)
DEFINE lnno     SMALLINT    # Line Number (instead of LINENO)
DEFINE tm       SMALLINT    # Top Margin
DEFINE bm       SMALLINT    # Bottom Margin
DEFINE pt       SMALLINT    # Number of lines for Page Trailer
DEFINE lastln   SMALLINT    # Last line to print on each page:
                            # pglgth - bm - pt

# set the margins and page length to minimum,
# because we'll handle all that in the body of the report
OUTPUT
    TOP MARGIN 0
    BOTTOM MARGIN 0
    PAGE LENGTH 1
    # for my purposes, I'm going to control the LEFT MARGIN
    # in the PRINT statements as well, but this isn't required
    LEFT MARGIN 0

# we're going to break by group on x, so inform the report that
# x is already sorted.
ORDER EXTERNAL BY x

FORMAT
FIRST PAGE HEADER
# use this control block to initialize variables
IF outpt = "S" THEN
    # need to check your machine's implementation of "pg" command,
    # if you're using it. I found that on some machines/termtypes
    # it outputs 23/lines/screen, and on others it's 24.
    # Whatever it is, set pglgth to 1 less than it displays,
    # because "pg" displays bottom line of one screen as the top
    # line of the next, so we really have 1 line less than what's
    # available in your implementation of "pg" for display.
    LET pglgth = 22
    # Also, since on the first page of the report the 1st line is
    # displayed only once (no previous screen shown), we'll print
    # an extra blank line at the top of page 1 to align everything
    # properly (see code below).
ELSE
    # I'm testing on a laser printer that prints 60 lines/page
    LET pglgth = 60
END IF
# set the top & bottom margin
# they, too can be different for screen/printer,
# although of course they don't have to be
IF outpt = "S" THEN
    LET tm = 0
    # WARNING: Make your bm at least 1,
    # because "pg" gets confused if not.
    LET bm = 1
ELSE
    LET tm = 3
    LET bm = 3
END IF
# set the number of lines in your page trailer
LET pt = 2
# calculate the page line we're going to print the body of the report on
LET lastln = pglgth - bm - pt
LET pgno = 0
LET lnno = 0

BEFORE GROUP OF x
# a lot of the code in BEFORE GROUP OF and AFTER GROUP OF are cloned
# from the ON EVERY ROW. This is a pain, but necessary. We can't just
# CALL functions for the duplicate code, because a PRINT statement has
# to be in the REPORT function itself.
IF lnno = 0 THEN
    # print the top margin
    IF tm > 0 THEN
        FOR lnno = 1 TO tm
            PRINT ""
        END FOR
    END IF
    LET lnno = tm
# NOTICE: if using "more" in your "OUTPUT TO PIPE" command,
# remove the IF block below. "more" doesn't require the extra
# line on the first page.
    IF outpt = "S" AND pgno = 0 THEN
        # this is the first page, so we need to print the extra
        # blank line referred to in the FIRST PAGE HEADER
        PRINT ""
    END IF
    # increment the page counter
    LET pgno = pgno + 1
    # print the page header
    PRINT "PAGE HEADER OF REPORT: ", TODAY USING "mm/dd/yyyy"
    PRINT ""
    LET lnno = lnno + 2
END IF
# make sure we have room for the group header
# + at least 2 lines of data + 3 lines for group trailer
IF lnno > (lastln - 2 - 3) THEN
    # close off this page
    WHILE lnno < lastln
        PRINT ""
        LET lnno = lnno + 1
    END WHILE
    # print the page trailer
    PRINT ""
    PRINT "                                     - ",
          pgno USING "<&", " -"
    IF bm > 0 THEN
        FOR lnno = 1 TO bm
            PRINT ""
        END FOR
    END IF
    # print the top margin
    IF tm > 0 THEN
        FOR lnno = 1 TO tm
            PRINT ""
        END FOR
    END IF
    LET lnno = tm
    # increment the page counter
    LET pgno = pgno + 1
    # print the page header
    PRINT "PAGE HEADER OF REPORT: ", TODAY USING "mm/dd/yyyy"
    PRINT ""
    LET lnno = lnno + 2
END IF
# print the group header
PRINT "GROUP HEADER: ", x USING "#&"
PRINT "----------------"
LET lnno = lnno + 2

ON EVERY ROW
# if we're at the top of a page ...
IF lnno = 0 THEN
    # print the top margin
    IF tm > 0 THEN
        FOR lnno = 1 TO tm
            PRINT ""
        END FOR
    END IF
    LET lnno = tm
    # increment the page counter
    LET pgno = pgno + 1
    # print the page header
    PRINT "PAGE HEADER OF REPORT: ", TODAY USING "mm/dd/yyyy"
    PRINT ""
    LET lnno = lnno + 2
    # print the continued group header
    PRINT "GROUP HEADER: ", x USING "#&", " (cont)"
    PRINT "-----------------------"
    LET lnno = lnno + 2
END IF
# do we have room to print this line, as well as any necessary
# 3-line group trailer?
IF lnno > lastln - 4 THEN
    # close off this page
    # let the user know this group continues next page
    PRINT "(cont)"
    LET lnno = lnno + 1
    WHILE lnno < lastln
        PRINT ""
        LET lnno = lnno + 1
    END WHILE
    # print the page trailer
    PRINT ""
    PRINT "                                     - ",
          pgno USING "<&", " -"
    IF bm > 0 THEN
        FOR lnno = 1 TO bm
            PRINT ""
        END FOR
    END IF
    # print the top margin
    IF tm > 0 THEN
        FOR lnno = 1 TO tm
            PRINT ""
        END FOR
    END IF
    LET lnno = tm
    # increment the page counter
    LET pgno = pgno + 1
    # print the page header
    PRINT "PAGE HEADER OF REPORT: ", TODAY USING "mm/dd/yyyy"
    PRINT ""
    LET lnno = lnno + 2
    # print the continued group header
    PRINT "GROUP HEADER: ", x USING "#&", " (cont)"
    PRINT "-----------------------"
    LET lnno = lnno + 2
END IF
PRINT "Line ", y USING "#&"
LET lnno = lnno + 1

AFTER GROUP OF x
# we can't be at the top of a page, because in the ON EVERY ROW
# block we made sure at least one row was printed on the same page
# as the group trailer
PRINT "-----------------"
PRINT "GROUP TRAILER: ", x USING "#&"
PRINT ""
LET lnno = lnno + 3
IF lnno >= lastln THEN
    # bottom of the page
    # print the page trailer
    PRINT ""
    PRINT "                                     - ",
          pgno USING "<&", " -"
    IF bm > 0 THEN
        FOR lnno = 1 TO bm
            PRINT ""
        END FOR
    END IF
    LET lnno = 0
END IF

ON LAST ROW
# if we're at the top of a page,
# or there isn't room to print the 3 end of page lines ...
IF lnno = 0 OR lnno > lastln - 3 THEN
    IF lnno > lastln - 3 THEN
        # print blanks down to the bottom of page,
        WHILE lnno < lastln
            PRINT ""
            LET lnno = lnno + 1
        END WHILE
        # print the page trailer
        PRINT ""
        PRINT "                                     - ",
              pgno USING "<&", " -"
        IF bm > 0 THEN
            FOR lnno = 1 TO bm
                PRINT ""
            END FOR
        END IF
    END IF
    # print the top margin
    IF tm > 0 THEN
        FOR lnno = 1 TO tm
            PRINT ""
        END FOR
    END IF
    LET lnno = tm
    # increment the page counter
    LET pgno = pgno + 1
    # print the page header
    PRINT "PAGE HEADER OF REPORT: ", TODAY USING "mm/dd/yyyy"
    PRINT ""
    LET lnno = lnno + 2
    # no need to print group header; there is none
END IF
# finish up the last page
# print blanks down to the bottom of page,
# except for 3 lines for ON LAST ROW stuff
WHILE lnno < lastln - 3
    PRINT ""
    LET lnno = lnno + 1
END WHILE
# print the end of report stuff
PRINT "                    ----------------------------------------"
PRINT "                               - END OF REPORT -"
PRINT "                    ----------------------------------------"
# print the page trailer
PRINT ""
PRINT "LAST PAGE                            - ",
      pgno USING "<&", " -"
IF bm > 0 THEN
    FOR lnno = 1 TO bm
        PRINT ""
    END FOR
END IF

END REPORT
# rpt_scr_ptr(outpt, x, y)

-------------------- CUT HERE FOR the other report example ------------
#---------------------------------------------------------------------#
REPORT rpt_module(lcol1, lcol2)
# Arguments: GROUP BY variables
# Purpose:   Report format
# Returns:   N/A
#---------------------------------------------------------------------#
DEFINE lcol1        LIKE table.col1
DEFINE lcol2        LIKE table.col2
DEFINE lstr         CHAR(80)    # long string
DEFINE sp1          CHAR(1)     # " "
DEFINE str4         CHAR(4)     # "###&"
DEFINE col          SMALLINT    # column counter
DEFINE x            SMALLINT

OUTPUT
    LEFT MARGIN 0
    # this report sometimes prints on legal paper
    PAGE LENGTH 78
    TOP OF PAGE "^L"

ORDER EXTERNAL BY lcol1, lcol2

FORMAT

PAGE HEADER
# set up psuedo-symbolic constants used to save string space
IF PAGENO = 1 THEN
    LET str4 = "###&"
    LET sp1 = " "
END IF

LET hstr = "PAGE HEADER STRING"
IF PAGENO = 1 AND m_module.paper_size = "L"
AND LENGTH(m_printdef.p_upper_tray) > 0 THEN
    # put the escape sequence to set in legal mode in front
    # this was the HP laserjet printer definitions tableized
    LET hstr = ASCII 027, m_printdef.p_upper_tray CLIPPED, hstr CLIPPED
END IF
PRINT hstr CLIPPED, " Page ", PAGENO USING "#&"

# center the next string
LET col = (80 - LENGTH(g_table.column))/2
PRINT COLUMN col, g_table.column CLIPPED
SKIP 1 LINE

# END PAGE HEADER

BEFORE GROUP OF lcol1
# page break for each facility
# this is how I assure a page break at col1,
# without using SKIP TO TOP OF PAGE
# I dislike SKIP TO TOP OF PAGE in a BEFORE GROUP block because
# is wastes a sheet of paper before the first group
NEED 75 LINES

BEFORE GROUP OF lcol2
# Make sure there's room to print the whole group on one page
# assume the most rows in a group is 21
LET x = 21
IF m_module.paper_size = "S" THEN
    # HERE'S THE TRICK: If printing to standard size (60 lines/page)
    # instead of legal size, (78 lines/page), I incread the NEED LINES
    # count by 18, because I want to break 18 lines earlier than before
    # By having TOP OF PAGE "^L", extra lines are not padded in.
    LET x = x + 18
END IF
NEED x LINES

ON EVERY ROW
IF m_module.paper_size = "S" AND LINENO > 59 THEN
    # we have our page length set for Legal size;
    # if we're printing on standard need to page break NOW
    NEED 75 LINES
END IF
PRINT ...

AFTER GROUP OF lcol1
LET x = 2
IF m_module.paper_size = "S" THEN
    LET x = x + 18
END IF
NEED x LINES
SKIP 1 LINE
PRINT ...

ON LAST ROW
IF m_module.paper_size = "L"
AND LENGTH(m_printdef.p_upper_tray) > 0 THEN
    # print the escape sequence to reset to letter mode
    PRINT ASCII 027, m_printdef.p_lower_tray CLIPPED
END IF


END REPORT
# rpt_module()

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

From: dennisp@informix.com (Dennis Pimple)
Newsgroups: comp.databases.informix
Subject: Re: Page Line Length
Date: 30 Oct 1995 16:22:14 GMT

M J Spuffer (spuffer@clark.net) wrote:
: I'm a new kid to the the Informix world.  My group is just learning about 
: 4gl and all it's tricks.  Having lived in the Pick world for many years, 
: its quite and adjustment.  Now for the question:  How do you write one 
: report which will run on a lazer printer (57 print lines) or a line 
: printer (66 lines) without replicating the entire report section? At 
: first thought and IF statement around the PAGE LENGTH  statement would 
: seem proper, but to no avail.  Any ideas would be appreciated.

If you can live with it, set your page length to the minimum in the
OUTPUT section of the REPORT functions (PAGE LENGTH 57 in your
example), and set TOP OF PAGE to control-L so that the report sends a
page feed to break pages instead of the default behavior of padding
blank lines to break pages (TOP OF PAGE "^L" in the OUTPUT section).
This would work, although you're wasting some space on the line
printers.

If you can't live with the wasted space (e.g. you want the same report
to run to screen (24 lines) as well as to printer (66 lines) with
appropriate headers always in view, etc.), try the code below. It's a
straight 4gl solution, although I can envision a c solution that would
involve creating 2 or 3 seperate reports (Page Header, Page Trailer,
and Body), and then calling a c-function that would know to paste the
header and trailer to every n lines of body text. (sounds like an
interesting weekend project - any takers?)

Some duplication within control blocks is required, and with more group
headers/trailers things might get sticky.

=======================================================================
Dennis J. Pimple         dennisp@informix.com    Opinions expressed
Principal Consultant     --------------------    are mine, and do not
Informix Software Inc    Voice:  303-850-0210    necessarily reflect
Denver Colorado USA      Fax:    303-779-4025    those of my employer.

###########     #   INFORMIX PROFESSIONAL SERVICES
#######     #  ##   Denver, Colorado
######     #  ###   ==================================================
#####     #  ####   File: %M%  SCCS: %I% %P%
####     #  #####   Program: varlgthrpt.4go
###     #  ######   Client: Example
##     #  #######   Author: Dennis J Pimple
#     ###########   Date: %G% %U%
# Example of how to use the same REPORT function to run to variable
# page length and/or screen.
# The problem is we can't use a variable in our PAGE LENGTH statement.
#
# The general approach we take is to set PAGE LENGTH to the maximum 
# possible, use TOP OF PAGE "^L" so that page breaks are sending out
# a line feed instead of padding the page with blank lines, and using
# variable testing to break at appropriate times.
# This example prompts for a page length of 10-100

MAIN
DEFINE pglgth   SMALLINT    # Page Length (10-100)
DEFINE pl       CHAR(3)     # for prompt
DEFINE x        SMALLINT    # Loop counter
DEFINE y        SMALLINT    # Loop counter

CLEAR SCREEN

LET pl = NULL
WHILE pl IS NULL
    PROMPT "What Page Length (10-100) ? " FOR pl
    IF pl IS NULL THEN
        ERROR "Enter a number between 10 and 100, or INTERRUPT to quit"
        LET pl = NULL
    END IF
    FOR x = 1 TO LENGTH(pl)
        IF pl[x] NOT MATCHES "[0123456789]" THEN
            ERROR "Enter a number between 10 and 100, ",
                  "or INTERRUPT to quit"
            LET pl = NULL
        END IF
    END FOR
    LET pglgth = pl CLIPPED
    IF pglgth < 10 OR pglgth > 100 THEN
        ERROR "Enter a number between 10 and 100, or INTERRUPT to quit"
        LET pl = NULL
    END IF
END WHILE

START REPORT rpt_varlgth TO "rpt.out"

FOR x = 1 TO 10
    FOR y = 1 TO 7
        OUTPUT TO REPORT rpt_varlgth(pglgth, x, y)
    END FOR
END FOR

FINISH REPORT rpt_varlgth

CLEAR SCREEN
DISPLAY "Report is in file rpt.out"

END MAIN

REPORT rpt_varlgth(pglgth, x, y)
DEFINE pglgth   SMALLINT    # Page Length (10-100)
DEFINE x        SMALLINT
DEFINE y        SMALLINT
DEFINE bm       SMALLINT    # Bottom Margin
DEFINE pt       SMALLINT    # Number of lines for Page Trailer
DEFINE lastln   SMALLINT    # Last line to print on each page:
                            # pglgth - bm - pt

# set the margins and page length to minimum,
# because we'll handle it in the body of the report
OUTPUT
    TOP MARGIN 2
    BOTTOM MARGIN 0
    # for my purposes, I'm going to control the LEFT MARGIN
    # in the PRINT statements as well, but this isn't required
    LEFT MARGIN 0
    # set page length to maximum
    PAGE LENGTH 100
    # set TOP OF PAGE so that linefeeds are imbedded instead of blank
    # lines to fill out the page
    TOP OF PAGE "^L"

# we're going to break by group on x, so inform the report that
# x is already sorted.
ORDER EXTERNAL BY x

FORMAT
PAGE HEADER
IF PAGENO = 1 THEN
    # use this control block to initialize variables
    # set the bottom margin
    LET bm = 2
    # set the number of lines in your page trailer
    LET pt = 2
    # calculate the page line we're going to print the body
    # of the report on
    LET lastln = pglgth - bm - pt
END IF
PRINT "PAGE HEADER OF REPORT: ", TODAY USING "mm/dd/yyyy"
SKIP 1 LINE

BEFORE GROUP OF x
# a lot of the code in BEFORE GROUP OF and AFTER GROUP OF are cloned
# from the ON EVERY ROW. This is a pain, but necessary. We can't just
# CALL functions for the duplicate code, because a PRINT statement has
# to be in the REPORT function itself.
# make sure we have room for the group header
# + at least 1 line of data + 2 lines for group trailer
IF LINENO > (lastln - 1 - 2) THEN
    # close off this page
    WHILE LINENO < lastln
        PRINT ""
    END WHILE
    # print the page trailer
    PRINT ""
    PRINT "                                     - ",
          PAGENO USING "<&", " -"
    SKIP TO TOP OF PAGE
END IF
# print the group header
PRINT "GROUP HEADER: ", x USING "#&"
PRINT "----------------"

ON EVERY ROW
# do we have room to print this line, as well as any necessary
# 2-line group trailer?
IF LINENO > lastln - 3 THEN
    # close off this page
    # let the user know this group continues next page
    PRINT "(cont)"
    # close off this page
    WHILE LINENO < lastln
        PRINT ""
    END WHILE
    # print the page trailer
    PRINT ""
    PRINT "                                     - ",
          PAGENO USING "<&", " -"
    SKIP TO TOP OF PAGE
END IF
# if we're at the top of a page ...
IF LINENO < 5 THEN
    # print the (continued) group header
    PRINT "GROUP HEADER: ", x USING "#&", " (cont)"
    PRINT "-----------------------"
END IF
PRINT "Line ", y USING "#&", " (LINENO ", LINENO USING "<<&", ")"

AFTER GROUP OF x
# we can't be at the top of a page, because in the ON EVERY ROW
# block we made sure at least one row was printed on the same page
# as the group trailer
PRINT "-----------------"
PRINT "GROUP TRAILER: ", x USING "#&"
PRINT ""
# close off the page if there isn't enough room to start the next group
IF LINENO > lastln - 3 THEN
    # close off this page
    WHILE LINENO < lastln
        PRINT ""
    END WHILE
    # print the page trailer
    PRINT ""
    PRINT "                                     - ",
          PAGENO USING "<&", " -"
    SKIP TO TOP OF PAGE
END IF

ON LAST ROW
# close off this page, unless we're at the top of a page
IF LINENO > 4 THEN
    WHILE LINENO < lastln
        PRINT ""
    END WHILE
    # print the page trailer
    PRINT ""
    PRINT "                                     - ",
          PAGENO USING "<&", " -"
    SKIP TO TOP OF PAGE
END IF
# run the blank page down to the bottom of the page
WHILE LINENO < lastln - 3
    PRINT ""
END WHILE
# print the ON LAST ROW STUFF
PRINT "                    ----------------------------------------"
PRINT "                               - END OF REPORT -"
PRINT "                    ----------------------------------------"
# print the page trailer
PRINT ""
PRINT "LAST PAGE                            - ",
      PAGENO USING "<&", " -"

END REPORT
# rpt_varlgth(pglgth, x, y)
