[ Previous | Next | Contents | Glossary | Home | Search ]
AIX Version 4.3 Guide to Printers and Printing

Calculating Page Width Using Printer Colon File Escape Sequences

The printer colon file for an ASCII queue on an IBM 4029 LaserPrinter defines page width, in characters, with the work attribute wW. As formatted by the lsvirprt command, wW is defined as follows:

 
Page Width In Characters, Using Width From Data Base (used in 
pipelines)
wW = %?%Cw%t%f!w%e%I_w%;
 %?            <IF>
     %Cw       PUSH: (1 If -w Flag on Command Line; Otherwise 0)
 %t            <THEN>
     %f!w      For Each Flag x on Command Line: "-xArgument" -> 
OUTPUT
 %e            <ELSE>
     %I_w      INCLUDE: (COLUMNS per page)
 %;            <END>

The %Cw checks to see if the w flag was used on the command line; if it was, then a 1 is pushed onto the stack, else a 0 is pushed onto the stack. In this case, the w flag was not used on the command line so a 0 is pushed onto the stack. The %t checks for a true (non-zero) value on the stack and, not finding one, executes the %e (else) construct %I_w.

_w is defined as %IwX, shown below as formatted by the lsvirprt command.

 
Default Page Width (characters)
wX = 
%?%G_z%{1}%&%t%GwK%e%GwJ%;%?%G_p%{17}%=%t%{171}%e%G_p%{10}%*%;%*%
?%G_W%t%{6000}%e%{3000}%;%/%d
 %?            <IF>
     %G_z      PUSH: (Page ORIENTATION)
     %{1}      PUSH: (Integer Constant 1)
     %&        PUSH: (pop2 & pop1) -- Bitwise AND
 %t            <THEN>
     %GwK      PUSH: (Primary Page Length (-z 0) or Secondary 
Page Width (-z
               1), in pels)
 %e            <ELSE>
     %GwJ      PUSH: (Primary Page Width (-z 0) or Secondary Page
Length (-z
               1), in pels)
 %;            <END>
 %?            <IF>
     %G_p      PUSH: (PITCH (characters per inch))
     %{17}     PUSH: (Integer Constant 17)
     %=        PUSH: (pop2 = pop1 ?)
 %t            <THEN>
     %{171}    PUSH: (Integer Constant 171)
 %e            <ELSE>
     %G_p      PUSH: (PITCH (characters per inch))
     %{10}     PUSH: (Integer Constant 10)
     %*        PUSH: (pop2 * pop1)
 %;            <END>
 %*            PUSH: (pop2 * pop1)
 %?            <IF>
     %G_W      PUSH: (DOUBLE-WIDE print?)
 %t            <THEN>
     %{6000}   PUSH: (Integer Constant 6000)
 %e            <ELSE>
     %{3000}   PUSH: (Integer Constant 3000)
 %;            <END>
 %/            PUSH: (pop2 / pop1)
 %d            POP -> ASCII String -> OUTPUT

The calculation of _w begins by pushing the value of _z, page orientation, onto the stack. The job submission command being used in this example, qprt -a1 -Pasc -fp -p12 -scourier -C -N3 /etc/motd , specifies a z value of 1, so a 1 is pushed onto the stack. The %{1} pushes another 1 onto the stack, after which the %& pops the top two values (both1s) off the stack and performs a bitwise AND with the two values. The result of the bitwise AND, a 1, is pushed onto the stack.

Note: The test is a bitwise AND instead of a simple test for equality because the legal values for the z flag are 0, 1, 2, and 3, corresponding to the legal number of 90 degree rotations that can be applied to a printed page.

The next %t finds a true (non-zero) value on the stack and so the then clause, %GwK, is resolved before any more work is done resolving _w.

As formatted by lsvirprt, wK is defined as follows:

 
Primary Page Length (-z 0) or Secondary Page Width (-z 1), in pels
wK = 
%G_Q%Pq%?%GWu%{3}%<%t%?%gq%{1}%=%t%{3200}%e%gq%{2}%=%t%{4100}%e%g
q%{3}%=%t%{2935}%e%gq%{4}%=%t%{3407}%e%{3050}%;%e%?%gq%{1}%=%t%{2
150}%e%gq%{2}%=%t%{2562}%e%gq%{3}%=%t%{2750}%e%gq%{4}%=%t%{2498}%
e%gq%{5}%=%t%{2604}%e%{2852}%;%;%d
 %G_Q          PUSH: (PAPER SIZE override for input paper source)
 %Pq           POP -> Internal Variable q
 %?            <IF>
     %GWu      PUSH: (Calculate value for paper source based on 
_O and _u.)
     %{3}      PUSH: (Integer Constant 3)
     %<        PUSH: (pop2 < pop1 ?)
 %t            <THEN>
     %?        <IF>
         %gq   PUSH: (Internal Variable q)
         %{1}  PUSH: (Integer Constant 1)
         %=    PUSH: (pop2 = pop1 ?)
     %t        <THEN>
         %{3200}  PUSH: (Integer Constant 3200)
     %e        <ELSE>
         %gq   PUSH: (Internal Variable q)
         %{2}  PUSH: (Integer Constant 2)
         %=    PUSH: (pop2 = pop1 ?)
     %t        <THEN>
         %{4100}  PUSH: (Integer Constant 4100)
     %e        <ELSE>
         %gq   PUSH: (Internal Variable q)
         %{3}  PUSH: (Integer Constant 3)
         %=    PUSH: (pop2 = pop1 ?)
     %t        <THEN>
         %{2935}  PUSH: (Integer Constant 2935)
     %e        <ELSE>
         %gq   PUSH: (Internal Variable q)
         %{4}  PUSH: (Integer Constant 4)
         %=    PUSH: (pop2 = pop1 ?)
     %t        <THEN>
         %{3407}  PUSH: (Integer Constant 3407)
     %e        <ELSE>
         %{3050}  PUSH: (Integer Constant 3050)
     %;        <END>
 %e            <ELSE>
     %?        <IF>
         %gq   PUSH: (Internal Variable q)
         %{1}  PUSH: (Integer Constant 1)
         %=    PUSH: (pop2 = pop1 ?)
     %t        <THEN>
         %{2150}  PUSH: (Integer Constant 2150)
     %e        <ELSE>
         %gq   PUSH: (Internal Variable q)
         %{2}  PUSH: (Integer Constant 2)
         %=    PUSH: (pop2 = pop1 ?)
     %t        <THEN>
         %{2562}  PUSH: (Integer Constant 2562)
     %e        <ELSE>
         %gq   PUSH: (Internal Variable q)
         %{3}  PUSH: (Integer Constant 3)
         %=    PUSH: (pop2 = pop1 ?)
     %t        <THEN>
         %{2750}  PUSH: (Integer Constant 2750)
     %e        <ELSE>
         %gq   PUSH: (Internal Variable q)
         %{4}  PUSH: (Integer Constant 4)
         %=    PUSH: (pop2 = pop1 ?)
     %t        <THEN>
         %{2498}  PUSH: (Integer Constant 2498)
     %e        <ELSE>
         %gq   PUSH: (Internal Variable q)
         %{5}  PUSH: (Integer Constant 5)
         %=    PUSH: (pop2 = pop1 ?)
     %t        <THEN>
         %{2604}  PUSH: (Integer Constant 2604)
     %e        <ELSE>
         %{2852}  PUSH: (Integer Constant 2852)
     %;        <END>
 %;            <END>
 %d            POP -> ASCII String -> OUTPUT

The calculation of wK begins by pushing the value of _Q, the paper size override for the input paper source, onto the stack. The value of _Q is defined as %IwQ. At this point in the calculation of Wk, we are exactly where we were in the calculation of wJ, that is, trying to determine a value for wQ and Wu. Within the context of a single job submission command, the final values of wQ and Wu are not going to change just because a final value was requested from a different attribute calculation. Thus we'll use the previously calculated values of 1 for wQ and 1 for Wu.

The 1 returned to the in-progress calculation of wK is the value of _Q, and is pushed onto the stack. It is immediately popped back off the stack and stored in the internal variable q. Wu, already determined to be 1, is again pushed onto the stack. %{3} pushes a 3 onto the stack, then the %< pops the top two values (a 3 and a 1) off the stack and checks to see if the second value popped is less than the first value popped. 1 is less than 3 today, so a 1 is pushed onto stack. The %t find the 1 and so enters the if-then-else-then-else-then-else... sequence looking for an integer to pair with the paper size value calculated for _Q.

The %gq fetches the stored value of _Q from the internal variable q, and pushes it onto the stack. The %{1} pushes a 1 onto the stack. The %= pops the top two values (two 1s) off the stack and, checking them for equality, succeeds; a 1 is pushed onto the stack. The %t finds the 1 and so evaluates the %{3200}, which pushes a 3200 onto the stack. The calculation of wK then falls through all but the last line of the remaining printer colon file escape sequences defining wK. The last escape sequence, %d, pops the top value, 3200, off the stack and returns it, in ASCII format, to the in-progress calculation of wX.

The 3200 returned to the in-progress calculation of wX is the value of wK, and is pushed onto the stack. The %GwJ in the else clause is skipped and the %; terminates the if-then-else sequence. At this point in the calculation of wJ, the remainder of the attribute definition dealt with factors that affected page length (in lines), such as vertical line density. In the calculation of page width, however, we will be interested in pitch and in whether or not double-wide printing was selected.

The next escape sequence evaluated is %G_p. This fetches the value of the _p attribute, which defines the pitch in characters per inch for this queue. The default value for this queue is 10 but the command line being used in this example specified a pitch of 12 (-p12), so a 12 is pushed onto the stack. The %{17} pushes a 17 onto the stack. The %= pops the top two values (a 17 and a 12) off the stack and, checking them for equality, fails; a 0 is pushed onto the stack. The %t finds the 0 (a false value) and the following else clause is evalutated. %G_p again pushes a 12 onto the stack. The %{10} pushes a 10 onto the stack. The %* pops the top two values (a 12 and a 10) off the stack and multiplies them together; the resulting 120 is pushed onto the stack. The %; terminates this if-then-else sequence.

The following %* pops the top two values (a 120 and a 3200) off the stack and multiplies them together; the resulting 384000 is pushed onto the stack. The %G_W fetches the value of _W and pushes it onto the stack; _W is a yes (1) or no (0) question concering whether or not double-wide printing is needed. The default value is 0 and we did not override it on the command line, so a 0 is pushed onto the stack. The %t finds the 0 and so executes the else clause. The %{3000} pushes a 3000 onto the stack. The %; terminates this if-then-else sequence. The following %/ pops the top two values (a 3000 and a 384000) off the stack and divides the second value popped by the first value popped; the resulting 128 is pushed onto the stack. The %d pops the top value, 128, off the stack and returns it, in ASCII format, to the in-progress calculation of wW.

The 128 returned to the in-progress calculation of wW is the value of _w. The value of wW was originally referenced in the determination of the value of the ia attribute, the input datastream pipeline for ASCII jobs. The number 128 replaces the %IwW in that determination, so the value of the -! flag to pioformat becomes /usr/lib/lpd/pio/fmtrs/piof5202 -l48 -w128 . The -w128 can be seen in the original diagnostic message from piobe that was the basis of this discussion; it is part of the PIPELINE OF FILTERS section of the mail sent by the qdaemon on behalf of piobe.

The Calculation of Page Width figure depicts the stacks operations (as described above) used to obtain a final numeric value for page width in characters. The following numbered steps correspond to the numbers on the left side of the columns in the figure, and provide a step-by-step description of the evaluation of the printer colon file escape sequences defining page width, in characters, for this particular queue (asc), colon file, and command line.

  1. %Cw - Pushes a 0 onto the stack since the w flag was not used on the command line.
  2. %I_w - Calls for the evaluation of _w.
  3. %G_z - Pushes a 1 onto the stack.
  4. %{1} - Pushes a 1 onto the stack.
  5. %& - Pops the top two values (two 1s) off the stack, performs a bitwise AND on the two values, and pushes the resultant 1 onto the stack.
  6. %t - Pops the 1 off the stack and, since it is a TRUE (non-zero) value, calls for the evaluation of %GwK.
  7. %GwK - Calls for the evaluation of wK.
  8. %G_Q - Calls for the evaluation of _Q.
  9. %GwQ - Calls for the evaluation of wQ.
  10. %GWu - Calls for the evaluation of Wu.
  11. %CO - Pushes a 0 onto the stack since the O flag was not used on the command line.
  12. %t - Pops the 0 off the stack and, since it is a FALSE (zero) value, calls for the evaluation of %G_u. The stack labeled Wu is now empty.
  13. %G_u - Pushes a 1 onto the stack.
  14. %d - Pops the 1 off the stack and returns it, in ASCII format, to the in-progress calculation of wQ.
  15. %{0} - Pushes a 0 onto the stack.
  16. %= - Pops the 0 and 1 off the stack, compares them for equality, and pushes the resultant 0 onto the stack.
  17. %t - Pops the 0 off the stack and, since it is a FALSE (zero) value, calls for the evaluation of %GWu.
  18. %GWu - This value is already known, so a 1 is pushed onto the stack.
  19. %{1} - Pushes a 1 onto the stack.
  20. %= - Pops the two 1s off the stack, compares them for equality, and pushes the resultant 1 onto the stack.
  21. %t - Pops the 1 off the stack and, since it is a TRUE (non-zero) values, calls for the evaluation of %Gs1.
  22. %Gs1 - Pushes a 1 onto the stack.
  23. %d - Pops the 1 off the stack and returns it, in ASCII format, to the in-progress calculation of wK.
  24. %Pq - Pops the 1 off the stack and stores it in the internal variable q.
  25. %GWu - This value is already known, so a 1 is pushed onto the stack.
  26. %{3} - Pushes a 3 onto the stack.
  27. %< - Pops the top two values off the stack (a 3 and a 1) and, since 1 is less than 3, pushes a 1 onto the stack.
  28. %t - Pops the 1 off the stack and, since it is a TRUE (non-zero) value, calls for the evaluation of %pq.
  29. %pq - Pushes the value of the internal variable q, a 1, onto the stack.
  30. %{1} - Pushes a 1 onto the stack.
  31. %= - Pops the top two values (two 1s) off the stack, compares them for equality, and pushes the resultant 1 onto the stack.
  32. %t - Pops the 1 off the stack and, since it is a TRUE (non-zero) value, calls for the evaluation of %{3200}.
  33. %{3200} - Pushes a 3200 onto the stack.
  34. %d - Pops the 3200 off the stack and returns it to the in-progress calculation of _w.
  35. %G_p - Pushes a 12 onto the stack.
  36. %{17} - Pushes a 17 onto the stack.
  37. %= - Pops the top two values (a 17 and a 12) off the stack, compares them for equality, and pushes the resultant 0 onto the stack.
  38. %t - Pops the 0 off the stack and, since it is a FALSE (zero) value, calls for the evaluation of %G_p.
  39. %G_p - Pushes a 12 onto the stack.
  40. %{10} - Pushes a 10 onto the stack.
  41. %* - Pops the top two values (a10 and a 12) off the stack, multiplies them together, and pushes the resultant 120 onto the stack.
  42. %* - Pops the top two values (a 120 and a 3200) off the stack, multiplies them together, and pushes the resultant 384000 onto the stack.
  43. %G_w - Pushes a 0 onto the stack.
  44. %t - Pops the 0 off the stack and, since it is a FALSE (zero) value, calls for the evaluation of %{3000}.
  45. %{3000} - Pushes a 3000 onto the stack.
  46. %/ - Pops the top two values (a 3000 and a 384000) off the stack, divides the second value popped by the first value popped, and pushes the resultant 128 onto the stack.
  47. %d - Pops the 128 off the stack and returns it, in ASCII format, to the in-progress calculation of ia, the input data stream pipeline for ASCII jobs.

Why the Stack Language Describing Page Width Works

Going beyond the mechanical description of what happens when piobe resolves the reference to %wW, here is a description of why the printer colon file escape sequence logic described above works.

The IBM LaserPrinter 4029 Series technical reference contains a figure and a table that together describe the printable and unprintable areas on a page, and the paper and envelope dimensions, in pels, for standard paper and envelope sizes. For instance, the printable area on an 8.5 x 11 (width by length) inch page is 2400 x 3200 pels (width by length). Note that if the page is rotated either 90 or 270 degrees for landscape printing, the dimensions are swapped and become 3200 x 2400 pels (width by length).

The evaluation of %IwW begins by checking to see if the w flag was used on the command line; if it was, then there are no calculations to perform. The requested value will be used. (That is not a promise that it will work, just that it will be used.) If the w flag was not used on the command line, then piobe has to figure out how wide the page is under the current job environment, as determined by other command line flags and by colon file defaults.

The first item checked in the evaluation of _w (page width) is page orientation (_z). As noted above, rotating the page by odd multiples of 90 degrees flips the page dimensions. Looking at the if-then-else statement that is the beginning of the definition of wK, it can be seen that the value of _z is a switch that controls which of wJ and wK will be used for page width. If the page has a portrait orientation, then wJ is width. If the page has a landscape orientation, then wK is width. After the page width in pels is resolved, the remainder of the escape sequences in the definition of wK just take pitch and character-width (double wide or not) into account while converting the number of pels to the number of characters.

wK is selected because the page orientation is landscape. Thus far all that is known is that the dimensions have been flipped; what the dimensions actually are is still unknown. The evaluation of wK begins by fetching the value (if any) of a command line usage of the Q flag, which is a printer-dependent value requesting a specific paper size. If the Q flag was used on the command line, then that value will be used to select the paper width in pels, otherwise a value for Q will be determined by evaluating Wu, which is a value for the paper source based on the attributes _O (type of input paper handling) and _u (input paper source). Note that _Q is defined as %IwQ, whose definition begins with %IWu.

Since Q was not used on the command line, the evaluation of Wu determines that the O flag wasn't used either, and so executes the else clause in the outer if-then-else statement in the definition of Wu, returning the default colon file value of _u, 1, to the evaluation of wQ.

Since this is as deep as the nesting of escape sequences goes for the evaluation of _w, it is worth taking a closer look at the logic defining Wu. Keep in mind the definitions and legal values for O, u, and Q, which are:

The escape sequences defining Wu say this:

The definition of wQ is an if-then-else-then-else-then-else-then-else statement that repeatedly compares the value of Wu to the integers 0, 1, 2, and 3, looking for a match. The match selects the value of one of the attriubes s0, s1, s2, s3, or s4, respectively (s4 is selected when there is no other match). The items these attributes define are as follows:

In the virtual printer definition for an ASCII queue on an IBM 4029 LaserPrinter, there are only two unique values for these five attributes: s0, s1, and s2 are all 1, while s3 and s4 are both 3.

Looking back up the nested escape sequences, you can see that the definition of wK is composed of an outer if-then-else statement. Both the if and the else pieces of this statement contain a chain of if-then-else-then-else... statements. The value of Wu (which is a value for paper source, based on O and u) determines whether the if or the else piece of the outer statement executes; if Wu is 1 or 2 (less than 3), then the if piece executes, otherwise the else piece executes. It is in the final determination of wK that the page width, in pels, is fixed.

Case 1: Either the command line value of u or the default from the colon file (1, primary paper tray) is returned to the evaluation of wQ. The remaining escape sequences in the definiton of wQ test the value of Wu and select the value of one of s0, s1, s2, s3, or s4. That value is in turn returned to the evaluation of wK. If u is 1 or 2 , then Q will be 1 (non-envelope paper size). If u is 3, then Q will be 3 (envelope paper size). When the evaluation of wK is resumed, a u value of 1 or 2 will direct the process into the if piece of the outer if-then-else statement, and the Q value of 1 will select a page width of 3200 pels. A u value of 3 will direct the process into the else piece of the outer if-then-else statement, and the Q value of 3 will select an envelope page width of 2750 pels.

Case 2: Same as case 1.

Case 3: The user-specified manual paper handling on the command line but did not specify a paper source so Wu is assigned the value 0, and that value is returned to the evaluation of wQ. The 0 will cause wQ to be assigned the value of s0 (the paper size for manual paper feed, a 1). When the evaluation of wK is resumed, the u value of 0 will direct the process into the if piece of the outer if-then-else statement, and the Q value of 1 (s0) will select a page width of 3200 pels.

Case 4: The user-specified manual paper handling on the command line and also used the u flag to specify either the primary or alternate paper source (but definitely not envelopes). As with case 3, a page width of 3200 pels will be chosen.

Case 5: The user-specified manual paper handling on the command line and also used the u flag to specify an envelope paper source so Wu is assigned the value 4, and that value is returned to the evaluation of wQ. The 4 will cause wQ to be assigned the value of s4 (the envelope size for manual envelope size, a 3). When the evaluation of wK is resumed, the u value of 4 will direct the process into the else piece of the outer if-then-else statement, and the Q value of 3 will select an envelope width of 2498 pels.

The remaining printer colon file escape sequences defining _w reason that if there are 3200 pels available (horizontally), and if we want 12 characters per inch, and if the resolution of printer is 300 pels per inch, then 128 characters can be printed across the page. Both the pitch and the printer resolution are multiplied by 10 to account for the possibility of a 17-pitch being specified. A 17-pitch is actually 17.1, so multiplying both the numerator and the denominator by 10 causes the .1 to be accounted for in the final calculation of page width. The value 128 is returned to the evaluation of ia. That's basically where the -128 in the PIPELINE OF FILTERS came from.


[ Previous | Next | Contents | Glossary | Home | Search ]