Drawing Boxes

From Open Watcom

Revision as of 18:57, 16 October 2012; view current revision
←Older revision | Newer revision→
Jump to: navigation, search

Contents

Introduction

This page explores the box-drawing behavior of wgml 4.0. It includes the device library blocks that are used in the actual drawing as well as the control words and layout/document tags that are used to cause boxes to be drawn.

This initial version records what is known so far. Additional work is needed and will eventually be done.

The Required :BOX Block

The :BOX block, which is a required part of the :DEVICE block, defines the line-drawing characters and the font they are to be taken from.

The :ABSOLUTEADDRESS, :HLINE, :VLINE, and :DBOX Blocks

These blocks are all optional, but that does not mean that they can be used arbitrarily.

While attempting to get wgml 4.0 to interpret the :VLINE block, I discovered that :DRIVER block without an :ABSOLUTEADDRESS block but with the :HLINE, :VLINE, and :DBOX blocks would produce an "Abnormal program termination" -- but only when used to process a document specification which caused :VLINE to be interpreted.

This table shows every occurrence in the source files available to me:

Source File  :ABSOLUTEADDRESS  :HLINE  :VLINE  :DBOX
hpldrv.pcd   Yes
hplpdrv.pcd  Yes               Yes     Yes
hplpldrv.pcd Yes               Yes     Yes
mltdrv.pcd   Yes               Yes     Yes
psdrv.pcd    Yes               Yes     Yes     Yes
x2700drv.pcd Yes               Yes     Yes

No other source file available to me has any of these blocks in it.

Two obvious deductions can be drawn:

  • An :ABSOLUTEADDRESS block is a prerequisite the other three blocks.
  • The :HLINE and :VLINE blocks always appear together.

Section "15.9.15 VLINE Block" of the WGML Reference describes the intended context of :VLINE in this way:

The special symbols %y_size and %thickness are defined prior to 
processing the vline block. The symbol %y_size is set to the height 
of the vertical line, from the top edge to the bottom edge. The 
symbol %thickness is set to the value specified by the vline blocks 
thickness attribute. WATCOM Script/GML positions to the bottom left 
corner of the line before creating the rule line, and assumes the 
current point of the device is set to the top left corner of the 
line when finished.

In other words, a :VLINE block is expected to draw the line in an upward direction. Since a device that does not define :ABSOLUTEADDRESS has no ability to move the print position upwards (negative values of the attribute skip of the :NEWLINE block are not allowed, see the discussion here), it is not surprising that attempting to use :VLINE without :ABSOLUTEADDRESS causes a problem.

An :HLINE block, in contrast, is expected to draw the line from left to right, and so does not require :ABSOLUTEADDRESS -- provided wgml 4.0 always starts it on the correct line and does not attempt to go back up to print out text.

The :DBOX block starts at the bottom left corner and so would, in practice, have to go back up to draw the top and sides of the box, although it does not produce an "Abnormal program termination" in the absense of an :ABSOLUTEADDRESS block.

Clearly, which blocks are present depends on the characteristics of the device. Many devices have none of them, presumably because they produce their output one line at a time.

Control Word BX

This is an example of a four-section box drawn using control word BX:

.in 2
.tb set >
.tb 28
.bx 1 26 62
%binary(3)>the appropriate graphic appears
.br
%binary1(4)>the appropriate graphic appears
.bx
%binary2(5)>the appropriate graphic appears
.br;
%binary4(6)>the appropriate graphic appears
.bx off
.tb set
.tb
.in

Because control word BX only draws the box itself, control words IN and TB are also needed to position the text so that it appears within the box sections. This is quite common. There are, of course, many control words, and this example is not intended to exhaust the number of control words that may be useful in drawing boxes.

Control word BX always used the BOX characters with character devices. With line-drawing devices (or, at least, with PS), these rules apply:

  • The DBOX block is never used; if it is not defined, this has no effect on how the box is drawn.
  • If both of the HLINE and VLINE blocks are defined, they are both used.
  • If the HLINE block is not defined, then the BOX characters are used for the horizontal lines and no vertical lines appear, whether the VLINE block is defined or not.
  • If the HLINE block is defined, but the VLINE block is not defined, then horizontal lines (with no descenders) are drawn using the HLINE block but no vertical lines appear.

Thus, vertical lines only appear when both the HLINE and VLINE blocks are defined.

The Box Operands

Control word BX has several operands prompting specific actions. Exploring these is a bit of a problem: character strings are needed to see what they do but producing them turned out to be a bit of a problem: the only font that displays the 8-bit line drawing characters is Terminal and testing showed that, when the OUTTRANS block was used to emit the Unicode line drawing characters, the characters were not emitted, just the numeric value.

And copying the result from a text file displayed using font Terminal to this page does not work: instead of

┌────────────────────────┬───────────────────────────────────┐

the result is

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿

which is a bit annoying.

However, the topic is both necessary and interesting.

Empty lines are used between the boxing lines to make it clear what each line contains. In an actual box, there would be text lines which, for a character device, would have the vertical line embedded at the appropriate positions.

This set of commands:

.bx del
.bx 1 26 62
.bx 1 26 62
.bx off

produces:

┌────────────────────────┬───────────────────────────────────┐

├────────────────────────┼───────────────────────────────────┤

└────────────────────────┴───────────────────────────────────┘

as does this set of commands:

.bx del
.bx 1 26 62
.bx
.bx off

This example illustrates the use of BX with no operand to establish a set of columns and print out the top line, BX with the same columns or no columns to print out a middle line, and BX OFF to print out a bottom line.

This set of commands:

.bx del
.bx 26
.bx
.bx off

produces:

│

│

│

which would be what what the TSO means by a "box with only one vertical descender".

This set of commands:

.bx del
.bx 1 26 /32 62
.bx
.bx off

produces:

┌────────────────────────┐     ┌────────────────────────┐
 
├────────────────────────┤     ├────────────────────────┤

└────────────────────────┘     └────────────────────────┘

as does this set of commands:

.bx del
.bx 1 26 / 32 62
.bx
.bx off

which illustrates the effect of / and shows that it need not have whitespace on both sides. Further testing shows that it must be preceded by whitespace, and that it cannot be used with the first box position on a BX line, even if it is not the leftmost position on the resulting combined set of box column positions.

This set of commands:

.bx del
.bx 1 26 32 62
.bx 3 30 40 65
.bx off

produces:

┌────────────────────────┬─────┬─────────────────────────────┐

└─┬──────────────────────┴───┬─┴──────┬──────────────────────┴──┐

  └──────────────────────────┴────────┴─────────────────────────┘

which illustrates the basics of how different sets of box columns positions are merged.

This set of commands:

.bx del
.bx 1 26 
.bx 32 62
.bx off

produces:

┌────────────────────────┐
 
└────────────────────────┴─────┬────────────────────────────────┐

                               └────────────────────────────────┘

while this set of commands:

.bx del
.bx 1 26 
.bx on 32 62
.bx off

produces:

┌────────────────────────┐
 
└────────────────────────┘     ┌────────────────────────────────┐

                               └────────────────────────────────┘

which shows on BX ON can be used to do. Further testing shows that, for this effect to appear, there must be no overlap at all between the two sets of column positions. If overlasp exists, the effect is the same as that shown above whether ON is used or not.

This set of commands:

.bx del
.bx 10 20 
.bx 1 30
.bx off
.bx off

produces:

         ┌─────────┐

┌────────┴─────────┴─────────┐

└────────────────────────────┘

which shows the effect of BX OFF after the last box has been closed, while this set of commands:

.bx del
.bx 10 20 
.bx new 1 30
.bx off
.bx off

produces:

         ┌─────────┐

┌────────┴─────────┴─────────┐

└────────┬─────────┬─────────┘

         └─────────┘

which illustrates the "box on box" effect which can be produced by NEW, while this set of commnds:

.bx del
.bx 1 30
.bx new 10 20 
.bx off
.bx off

produces:

┌────────────────────────────┐

│        ┌─────────┐         │

│        └─────────┘         │

└────────────────────────────┘

which illustrates the "box in box" effect which can be produced with NEW.

Both illustrate that BX OFF only closes the most recently-opened box; so some sort of stack of boxes exists. They also illustrate that BX OFF will generate vertical descenders (or lines) for the "background" box.

Further testing with NEW showed that many strange effects can be produced if it is used inappropriately because it will always emit the descenders of the "background" box as shown.

BX OFF was also tested with columns following. The effect is to add them to the box column set and then generate a bottom line.

BX SET was tested; it does indeed appear to be treated like BX NEW except for not generating the line.

For a line-drawing device, of course, things work a bit differently: instead of ascenders and descenders being generated as part of the horizontal line, vertical lines are generated as appropriate.

Splitting Boxes Drawn By Control Word BX

The PS output will be described first.

Testing with CO ON and control word SK with steadily-increasing values showed that the example table shown above would, at some point, end up with its bottom line drawn at the top of the next page with no ascenders while the rest of the box showed horizontal lines at (apparently) their normal length.

Further increases in the SK value showed each line being transferred, with horizontal lines being treated the same as text lines in this regard; however, when the last line on a page was a top line, then vertical descenders were visible and the vertical lines on the next page appeared to be curtailed.

This box showed extra space between the top line and the first line of text, but not between the last line of text and the bottom line.

Testing with a simpler box, with ten lines of text, which used CO OFF instead of BR to place each line of text on its own output line, produced the same result. The same box with CO ON, and enough text to produce two lines, also produced the same result so far as splitting the box was concerned. The text, of course, continued on past the right vertical line, as that was to the left of the right margin.

Testing with a character device shows that the basic behavior is the same; indeed, the only differences are:

  1. No extra space appears above or below any text line.
  2. The bottom lines include ascenders.

So it appears that boxes drawn with BX are split in the simplest possible way, keeping in mind that, for line-drawing devices, top lines occupy visible space and so have descenders, while bottom lines do not and so have no ascenders while, for character devices, both top and bottom lines, being composed of characters, occupy the same height as a text line in the given font and have ascenders and/or descenders as needed.

The Layout/Document Tags

There are three tags which can cause wgml to draw boxes if a particular attribute is given the value "box":

Tag             Type      Attribute
:FIG            Document  frame
:FIG            Layout    default_frame
:IXHEAD         Layout    frame

The :FIG tags pertain to figures; the :IXHEAD tag pertains to index headings.

The observed behavior (so far, with limited testing) is:

  • If :DBOX is present, then it is used.
  • If :DBOX is not present, but :HLINE and :VLINE are present, then :HLINE and :VLINE are used to draw the box for figures but not for index headings (no box appears at all around the index headings in this case). Note that :VLINE, in the limited testing performed, was only used here when it was used with .bx.
  • If neither :DBOX nor :HLINE/:VLINE are present, then the characters specified by :BOX are used for both figures and index headings.

It is surprising that :IXHEAD behaves differently from :FIG when :DBOX is not provided but :HLINE and :VLINE are.

This is the :FIG used for testing:

:FIG frame=box width='6i' place=inline.
%x_address() returns the current horizontal print position
%y_address() returns the current vertical print position
:FIGCAP.Sample caption with multiple words
:eFIG.

There are, of course, many other options possible, all of which the layout code will most likely need to implement. This page, however, is concerned with drawing boxes, not with other options.

Text Line Formatting

While exploring how text lines were output by wgml 4.0, boxing was tested using each of control word BX and tags FIG and IXHEAD. This produced results which turned out to be more generally applicable, and which are discussed to a very limited extent here; however, this section will accumulate more details as they are discovered.

If the box is drawn using BOX block characters then, whether the box is created by control word BX, tag FIG, or tag IXHEAD, each vertical line BOX block character added to the text is placed in its own text_chars instance. Horizontal lines containing no spaces are placed in a single text_chars instance.

The boxes produced by tag IXHEAD only contain a single character which, by necessity, is placed in a single text_chars instance.

When tag FIG is used, the text_line struct for the text itself contains one text_chars instance for each tab in actual use which controls all of the text starting at that tab stop, including the embedded spaces. This text is never right-justified, even when right justification is turned on. Text given with tag FIGCAP is processed normally.

Control word BX, as such, has no effect on output line formatting, as such. Instead, all of the normal factors (concatenation on/off, justification on/off, whether the current word will fit on any line or must be split, and so on). This can produce a variety of effects, which can be confusing; however, commenting out the BX control word lines (so that the box is not drawn) will show that the text is formatted the same way as it is when the box is drawn.

It usually happens that control word TB is used in conjunction with control word BX. This adds all of the effects associated with control word TB:

  1. Control word BR may not be needed, provided user tab stops are created and used in a way that causes a break to be generated without control word BR being present.
  2. Text following a tab character will, when appropriate, continue on the same line even if the right margin or even the right edge of the page is passed. For character devices, this causes the line to wrap; for the PS device (and probably all similar devices), the text is truncated at the right page edge when displayed.

The second point means that an extremely long "word" which contains no tab character is split at the right margin while the same "word", if preceded immediately by a tab character, is not split. It also applies to lines composed of words with spaces in between them.

For a character device, vertical ascenders are inserted into each text line included in the box. Each such ascender is placed in its own text_chars instance. The effect is that ascenders only appear when a space character would otherwise be produced.

However, this is not what is actually happening: instead, text_chars instances containing ascenders are only placed between the existing text_chars instances. If there is no "between" -- that is, if an existing text_chars instance writes anything, even a space character, in the position where a vertical ascender would normally be found, then no vertical ascender appears. This can easily happen when concatenation is turned OFF within the box.

For the PS device, and so presumably for any device using the VLINE block, the vertical line appears even if it ends up running through a text character.

Of course, in actual documents, such as the Open Watcom documents, each box would be designed so that all of the text was displayed in the correct location and none of it overlapped the vertical lines or ascenders.

Drawing Boxes

These section describes exactly how boxes are drawn. PS and WHELP are taken as models of two different types of devices: line-drawing devices, which define the ABSOLUTEADDRESS block and at least the HLINE block and character devices, which use the characters and font prescribed by the BOX block, respectively. Since PS is the only line-drawing device available, it is possible that some aspects of the process described are actually PS-specific augmentations. There are several character devices available, and they all appear to work the same way.

Drawing a box with DBOX or with HLINE and VLINE is very different from drawing a box with the BOX block characters.

Line-Drawing Devices

As noted above, for line-drawing devices, while tags FIG and IXHEAD will use the DBOX block if it is available, control word BX will not do so but will instead use the HLINE and VLINE blocks.

These notes are based on the behavior seen with the sample .bx and sample :FIG shown above.

For the DBOX block, the sequence noted is very simple:

  1. Use the ABSOLUTEADDRESS block to locate the print head to the lower left corner of the box.
  2. Use the DBOX block to draw the box from that point.

For HLINE, at least at the top of the box, these vertical adjustments are made:

  • The vertical position is moved downward by 84 vbus.
  • The vertical position of the first line in the box (and so all subsequent lines or other items in the box) is moved down by 167 vbus.

This happens without regard to the LPI setting: invoking wgml 4.0 with LPI 10 (instead of the default "6") does indeed cause the vertical positions to change, but the adjustments shown are still 84 vbus and 167 vbus. Experimentation with a test device shows that these values are actually based on the formula:

(vbus/inch)/6

That is, the number of vertical base units per inch for the given device is used, but it is divided by 6 (that is, 6 lines per inch) to get the line height. One half of this result, rounded up if the result is odd, is then used to position the HLINE block.

For HLINE, at least at the top of the box, this horizontal adjustment is made:

  • The column width using the actual value of CPI minus one-half of the column width using a CPI of "10" is used for the horizontal position of the HLINE.

Thus, with a CPI of "10", the HLINE will start in the middle of its column, 50 hbus from the left edge, but, with a CPI of "5", the HLINE will start 150 hbus from the left edge, that is, 200 hbus (the column width with a CPI of "5") minus 50 hbus (the column width with a CPI of "10"). Experimentation with the number of hbus/inch confirmed this formula:

(hbus/inch)/CPI - ((hbus/inch)/10)/2

For some unexplored reason, when there are 800 hbus/inch, everything is shifted 10 hbus to the right; at least the HLINE and the items inside the box are. Since the only affected device, PS, has 1000 hbus/inch, this was not investigated further.

For HLINE and VLINE, the sequence noted is not so simple:

  1. For the top line of the box:
    1. Use the ABSOLUTEADDRESS block to locate the print head to the upper left corner of the box.
    2. Use the HLINE block to draw the top line from that point.
  2. For the middle line of the box:
    1. Use the ABSOLUTEADDRESS block to locate the print head to the leftmost position on the middle line.
    2. Use the VLINE block to draw the left side of the top part of the box from that point.
    3. Use the ABSOLUTEADDRESS block to locate the print head to the middle position on the middle line.
    4. Use the VLINE block to draw the middle line of the top part of the box from that point.
    5. Use the ABSOLUTEADDRESS block to locate the print head to the rightmost position on the middle line.
    6. Use the ABSOLUTEADDRESS block to locate the print head to the rightmost position on the middle line again.
    7. Use the VLINE block to draw the right line of the top part of the box from that point.
    8. Use the ABSOLUTEADDRESS block to locate the print head to the leftmost position on the middle line.
    9. Use the HLINE block to draw the middle line from that point.
  3. For the bottom line of the box:
    1. Use the ABSOLUTEADDRESS block to locate the print head to the lower left corner of the box.
    2. Use the HLINE block to draw the bottom line from that point.
    3. Use the ABSOLUTEADDRESS block to locate the print head to the leftmost position on the bottom line.
    4. Use the VLINE block to draw the left side of the bottom part of the box from that point.
    5. Use the ABSOLUTEADDRESS block to locate the print head to the middle position on the bottom line.
    6. Use the ABSOLUTEADDRESS block to locate the print head to the middle position on the bottom line again.
    7. Use the VLINE block to draw the bottom line of the top part of the box from that point.
    8. Use the ABSOLUTEADDRESS block to locate the print head to the rightmost position on the bottom line.
    9. Use the ABSOLUTEADDRESS block to locate the print head to the rightmost position on the bottom line again.
    10. Use the VLINE block to draw the right line of the bottom part of the box from that point.

Testing shows that the PS device does show the duplicated invocations of the :ABSOLUTEADDRESS block in the positions indicated.

When the functions intended to be used for invoking the :DBOX, :HLINE and :VLINE blocks were written, they were written to invoke the :ABSOLUTEADDRESS block as well. Indeed, the function used to invoke the :ABSOLUTEADDRESS block was a static function, used entirely within its module. Since the layout code will need to invoke the :ABSOLUTEADDRESS block redundantly from time to time to produce bytewise identity between the output of wgml 4.0 and our wgml for device PS, the function used to do this is now globally accessible.

Character Devices

At this point, there isn't much to say: the BOX characters are used as their names would suggest.

Implementation Notes

Our wgml is intended to be a drop-in replacement for wgml 4.0 in the Open Watcom document build system. As such, it will be used with two devices (PS and WHELP). One of these devices (PS) provides all four of the ABSOLUTEADDRSS, HLINE, VLINE and DBOX blocks, and so boxes requested by tags IXHEAD and FIG appear and are drawn by wgml 4.0 using DBOX. The other device (WHELP) provides none of them, and so boxes requested by tags IXHEAD and FIG appear and are drawn by wgml 4.0 using the characters and font prescribed by the BOX block.

Control Word BX

The control word BX is used in the Open Watcom documents with these operands (copied from the TSO):

<h1 </> ... hn>
CAN
NEW <h1 </> ... hn>
OFF <h1 </> ... hn>
ON <h1 </> ... hn>

These operands are not used in the Open Watcom documents but do work in wgml 4.0:

DEL
SET <h1 </> ... hn>

These operands are recognized by wgml 4.0, but nothing happens: the line is treated as if it was a comment:

CHAR <TRM|32T|TNC|38C|GPC|APL>
CHAR HEX

This operand appears in the Open Watcom documents, but only commented out. Since wgml 4.0 does not accept it (nor BEGIN, NO, PURGE, or YES, despite what the TSO says in its introductory sections), this is just as well:

END (commented out)

The implementation should, when finished recognize all the operands that wgml 4.0 does and treat them in the same way that wgml 4.0 does.

Personal tools