/*
* With the -u option, TeX \special commands are used to include
* PostScript commands into the PS file created by the PS driver
* (dvi2ps, psdvi, dvips, dvitops, or whatever). The driver should
* not attempt repositioning or anything -- just copy to its output.
* Some drivers may not be willing to do this, and others may need
* the \special to begin with a certain keyword before they will do it.
* Here is a list of those keywords I know about:
* "ps-string "
* "ps-string="
* "ps::"
* "pstext="
* The following define is for this keyword; it might have to be set
* to one of the above. (My version of dvi2ps does not require a
* keyword.)
*/
#ifndef SKEY
#define SKEY "ps::"
#endif
/*
* Dimensions are in terms of em and ex (of the current font), with
* basic values assuming cmr 10, which has 1em = 10pt, 1ex = 4.31pt.
*
* Each character or space is reckoned to be 1/2 em, which is the
* width of a digit in the cmr fonts. However, each `col' unit in
* the tree is a half-space wide -- 1/4 em, that is.
*
* The normal width of rules is .4pt, which becomes .04em.
* The height of lines is that of a TeX \strut: 8.5pt above the
* baseline and 3.5pt below, and in ex units:
* 8.5pt/4.31=1.972ex
* 3.5pt/4.31=0.81206ex
* The horizontal bars made of "underlines" have vertical dimension
* starting 3.5pt below the baseline and going up .4pt to:
* 3.1pt/4.31=0.71925ex
* The ones made of "overlines" go to 8.5pt and start .4pt lower:
* 8.1pt/4.31=1.87935ex
* Rule width is .4pt/4.31 = .09280742ex.
*/
float hskip = 0; /* amount to hskip before next box or bar */
dohskip () {
/* must emit this even when hskip is 0 to get out of
* vertical mode before an \hbox
*/
printf ("\\hskip %.2fem", hskip);
hskip = 0;
}
/* record keeping for lines connecting discontinuous constituents */
int m_ulabel[11];
int d_ulabel[11];
/*
* Keep track of the attributes from the \M and \D label
* commands. For the first of a matching pair, save our treeid
* in the ulabel array and issue PS commands to define the current
* (x,y) coordinates with names corresponding to the treeid. For
* the second of a matching pair, issue PS commands to draw a
* line back to the previous location.
*/
setiattrib(node, is_daughter)
TREE node;
int *is_daughter;
{
int type = node->type;
int iattrib = 0;
if (type == VBAR && (iattrib = has(node,'D')) ) {
/* this is a daughter label */
*is_daughter = TRUE;
/* ... unless we're in an inverted tree */
/*if (node->mother && node->mother->type == OBAR)*/
if (has(node,'U') == 'i')
*is_daughter = FALSE;
if (iattrib == '+') iattrib = 10;
else iattrib -= '0';
d_ulabel[iattrib] = node->treeid;
/* if known already, get old treeid in iattrib, otherwise
* note with "-1" that current location must be defined
*/
if (m_ulabel[iattrib]) iattrib = m_ulabel[iattrib];
else iattrib = -1;
}
else if ((type==HBAR || type==VBAR || type==OBAR)
&& (iattrib = has(node,'M'))) {
/* this is a mother label */
*is_daughter = FALSE;
/* ... unless we're an OBAR: */
if (type == OBAR) *is_daughter = TRUE;
if (type == VBAR && has(node,'U') == 'i') *is_daughter = TRUE;
if (iattrib == '+') iattrib = 10;
else iattrib -= '0';
m_ulabel[iattrib] = node->treeid;
if (d_ulabel[iattrib]) iattrib = d_ulabel[iattrib];
else iattrib = -1;
}
if (debug_opt) printf("%% note iattrib %d\n", iattrib);
return(iattrib);
}
/* TeX height and depth to be used in current line for strut, etc. */
float h, d;
/* used only in following routine, except also set in print_tex_tree */
int curr_row = -1;
/* look at entire row to see what the smallest height and depth we can
* get away with are
*/
static
set_hd (node)
TREE node;
{ int row_type = 0;
int top_row = node->row;
if (top_row == curr_row) return;
curr_row = top_row;
while (node && (node->row == top_row)) {
row_type |= node->type;
node = node->right;
}
if (row_type & NODENAME) {
if (debug_opt) printf("%% Next row needs a full size strut.\n");
h = 1.972;
d = .812;
}
else {
/* Make rows without text for node names about 2/3 of normal size.
*/
if (debug_opt) printf("%% Setting height + depth to 2 ex's.\n");
h = 1.5;
d = .5;
}
}
/* Generate PS code for Bezier curve connecting discontinuous
* constituents (needs work). This has to be independent of scale
* or translation of coordinate system, since the PS driver may
* have changed these.
*/
curvegen(sf, is_daughter)
char *sf;
int is_daughter;
{
if (is_daughter) printf("\\lower%1.3fex",d);
else printf("\\raise%1.3fex",h);
printf("\\hbox{\\special{%scurrentpoint ",SKEY);
/* Daughter is at, say, (DX,DY) and mother at (MX,MY);
* if we're now at the daughter, we need to calculate two
* inflexion points, somehow. (Even though the PS variable
* is called "mom", if we're at a mother node, it's actually
* a daughter.)
*/
if (is_daughter) {
printf("currentpoint pop mom%sy ",sf); /* (DX,MY) */
printf("mom%sx currentpoint exch pop ",sf); /* (MX, (DY+MY)/2 ) */
printf("mom%sy add 2 div ",sf);
}
else {
/* We're at the mother, and the first inflexion point has to bend
* us down, then the second has be up above the daughter so we hit
* it going more or less downward.
*/
/* cpx */
printf("currentpoint pop ");
/* cpy 2 mul dy sub */
printf("currentpoint exch pop 2 mul mom%sy sub ",sf);
/* (MX, MY*2 - DY) */
/* dx */
printf("mom%sx ",sf);
/* dy 2 mul cpy sub */
printf("mom%sy 2 mul currentpoint exch pop sub ",sf);
/* (DX, DY*2 - MY) */
/* dx dy */
}
/* and wind up at (DX,DY) or (MX,MY) */
printf("mom%sx mom%sy curveto stroke ",sf,sf);
printf("moveto}}%%\n");
}
char m[4], n[4];
/* m and n are suffixes for PS identifiers initialized here for
* later use. m is for local lines; n is for discontinuous
* constituent lines requested through labels
*/
setsuffix(s, id)
char *s;
int id;
{
s[0] = 'a'; s[1] = 'a'; s[2] = 'a'; s[3] = '\0';
s[0] += id / (26*26);
s[1] += (id / 26) % 26;
s[2] += id % 26;
}
/* The vertical bars start horizontally half way through a
* character space and go .4pt further to the right. For the
* -u option, issue PS to draw a line to the previously defined
* position under the node name above. (For upside down trees,
* the position over the node name below is unfortunately not yet
* defined -- haven't figured out what to do about that -- not
* sure I care enough.)
*/
texvbar(node, iattrib, r, is_daughter)
TREE node;
int iattrib, is_daughter;
float r;
{ TREE mom = node->mother;
int do_a_line = FALSE,
do_a_bar = FALSE,
do_a_tbar = FALSE,
do_nothing = FALSE,
def_a_sister = FALSE,
def_an_osister = FALSE,
def_a_mother = FALSE,
do_a_triangle = FALSE,
do_a_box = FALSE,
do_an_obox = FALSE;
/* Horizontal bars also start half way through a space.
* They have a vertical bar in the center and extend .4pt
* further than half way through a character space at the
* end to cap the vertical bar that will come below.
* For the -u option, instead of rules, generate PS code
* to remember the coordinates under the node name above, so
* later can draw line from daughters back to here.
*/
texhbar(node, iattrib, r, rm, is_daughter)
TREE node;
int iattrib, is_daughter;
float r, rm;
{ TREE mom = node->mother;
texnodename(node, r)
TREE node;
float r;
{
if (has(node,'P')) hskip += r/2;
else {
dohskip ();
if (has(node,'R'))
printf ("\\hbox to %.2fem{\\hss{%s}}%%\n",
r/2, node->n);
else printf ("\\hbox to %.2fem{\\hss{%s}\\hss}%%\n",
r/2, node->n);
}
}
/* emit TeX code for a bar or a box containing a node name */
boxitup (node)
TREE node;
{
float r = node->l, rm;
int iattrib = 0;
int is_daughter;
int i;
TREE mom = node->mother;
if (b = has(node,'B')) {
if (b == '+') b = 9;
else if (isdigit(b)) b -= '0';
else b = 0;
greyness = b;
}
else {
for (b = black_opt; b > 10; b /= 10) ;
for (greyness = black_opt; greyness >= 100; greyness /= 10);
}
if (greyness > 9) greyness = 100 - greyness;
else if (greyness) greyness = 10 - greyness;
/* set height and depth for raising and lowering and for strut
* at end of line
*/
set_hd(node);
iattrib = setiattrib(node, &is_daughter);
/* halve width to compensate for doubling col values */
r /= COLMUL;
i = node->mid;
if (COLMUL > 2) i /= (COLMUL/2);
rm = i;
if (COLMUL > 1) rm /= 2;
/* now generate TeX code for VBARs, HBARs, and NODENAMEs */
switch(node->type) {
case VBAR: texvbar(node, iattrib, r, is_daughter);
break;
case HBAR: texhbar(node, iattrib, r, rm, is_daughter);
break;
case OBAR: texobar(node, iattrib, r, rm, is_daughter);
break;
case NODENAME: texnodename(node, r);
break;
}
}
/* Like preceding routine tex(), except collect spaces and
* emit globs of TeX \hskip, \hbox, and \vrule commands (for
* respectively space, node names, and tree lines).
*/
tex(tree)
TREE tree;
{
int row = tree->row,
col = 0,
i;
hskip = (float) indent / 2;
/* mark all labels as undefined */
for (i = 0; i < 11; i++) {
m_ulabel[i] = 0;
d_ulabel[i] = 0;
}
/* make sure set_hd looks at first row */
curr_row = -1;
/* Each line of the tree will be a paragraph, so prevent white space
* breaking up segments of vertical rules; put a strut at the
* end of each line to make it high enough. I'm not using regular
* \strut commands, because my own TeX code is not careful about
* keeping \strut defined appropriately for the font in use.
*/
printf ("\n\n{\\parskip=0pt\\offinterlineskip%%\n");
while (1) {
if (!tree) {
printf ("\\vrule width0em height%1.3fex depth%1.3fex",h,d);
printf ("\\par}\n");
bufp = buf; /* can reuse string buffer for next tree */
/* (could free malloc'd memory for tree nodes, too) */
return;
}
if (tree->row > row) {
/* Put a strut at the end of each line. The height and
* depth were determined by set_hd, called by boxitup.
*/
printf ("\\vrule width0em height%1.3fex depth%1.3fex",h,d);
/* prevent page breaks in midst of tree */
printf ("\\par\\penalty10000\n");
row++;
col = 0;
hskip = (float) indent / 2;
}
else if ((tree->row == row)
&& (tree->col == col)) {
boxitup(tree);
col += tree->l;
tree = tree->right;
}
else {
/* to advance one column, hskip 1/4 em, which is only 1/2
* en space, since we doubled all the col values
*/
hskip +=.50/COLMUL;
col++;
}
}
}