home *** CD-ROM | disk | FTP | other *** search
/ gondwana.ecr.mu.oz.au/pub/ / Graphics.tar / Graphics / VOGLE.ZIP / SRC / TEXT.C < prev    next >
C/C++ Source or Header  |  2000-02-11  |  20KB  |  1,004 lines

  1. #include <fcntl.h>
  2. #include <stdio.h>
  3.  
  4. #ifdef    TC
  5.  
  6. extern    double    cos();
  7. extern    double    sin();
  8.  
  9. #else 
  10.  
  11. #include <math.h>
  12.  
  13. #endif
  14.  
  15. #ifdef PC
  16. #include <string.h>
  17. #else
  18. #ifdef SYS5
  19. #include <string.h>
  20. #define rindex strrchr
  21. #else
  22. #include <strings.h>
  23. #endif
  24. #endif
  25. #include "vogle.h"
  26.  
  27. #define    MAX(a, b)    ((a) < (b) ? (b) : (a))
  28. #define    XCOORD(x)    ((x) - 'R')
  29. #define    YCOORD(y)    ('R' - (y))
  30.  
  31. static    float    SCSIZEX = 1.0, SCSIZEY = 1.0;
  32. static    int    Loaded = 0;
  33. static    short    nchars;
  34.  
  35.  
  36. static    char    errmsg1[120] = "font: unable to open ";
  37.  
  38. static    struct    {
  39.     char    *p;    /* All the vectors in the font */
  40.     char    **ind;    /* Pointers to where the chars start in p */
  41.     int    as;    /* Max ascender of a character in this font */
  42.     int    dec;    /* Max decender of a character in this font */
  43.     int    mw;    /* Max width of a character in this font */
  44. } ftab;
  45.  
  46. float    strlength();
  47. static    void    actual_move();
  48. static    void    drawhardchar();
  49. float    getfontwidth();
  50. float    getfontheight();
  51.  
  52. extern char    *getenv();
  53.  
  54. /*
  55.  * font
  56.  *     loads in a font.
  57.  */
  58. void
  59. font(name)
  60.     char    *name;
  61. {
  62.     Token    *tok;
  63.  
  64.     if (!vdevice.initialised)
  65.         verror("font: vogle not initialised");
  66.  
  67.     if (vdevice.inobject) {
  68.         tok = newtokens(2 + strlen(name) / sizeof(Token));
  69.         tok[0].i = VFONT;
  70.         strcpy((char *)&tok[1], name);
  71.  
  72.         return;
  73.     }
  74.  
  75.     /*
  76.      * check we aren't loading the same font twice in a row
  77.      */
  78. #ifdef PC
  79.     if (*name == '\\') {
  80.         if (strcmp(strrchr(name, '\\') + 1, vdevice.attr->a.font) == 0)
  81.             return;
  82. #else
  83.     if (*name == '/') {
  84.         if (strcmp(rindex(name, '/') + 1, vdevice.attr->a.font) == 0)
  85.             return;
  86. #endif
  87.     } else if (strcmp(name, vdevice.attr->a.font) == 0)
  88.         return;
  89.  
  90.     vdevice.attr->a.softtext = 0;
  91.     if (hershfont(name)) {
  92.         return;
  93.     } else if (strcmp(name, "large") == 0) {
  94.         if (!(*vdevice.dev.Vfont)(vdevice.dev.large)) 
  95.             verror("font: unable to open large font");
  96.     } else if (strcmp(name, "small") == 0) {
  97.         if (!(*vdevice.dev.Vfont)(vdevice.dev.small))
  98.             verror("font: unable to open small font");
  99.     } else if (!(*vdevice.dev.Vfont)(name)) {
  100.         strcat(errmsg1, "fontfile ");
  101.         strcat(errmsg1, name);
  102.         verror(errmsg1);
  103.     }
  104.  
  105. #ifdef PC
  106.     if (*name == '\\')
  107.         strcpy(vdevice.attr->a.font, strrchr(name, '\\') + 1);
  108. #else
  109.     if (*name == '/')
  110.         strcpy(vdevice.attr->a.font, rindex(name, '/') + 1);
  111. #endif
  112.     else 
  113.         strcpy(vdevice.attr->a.font, name);
  114. }
  115.  
  116. /*
  117.  * numchars
  118.  *
  119.  *    return the number of characters in the currently loaded hershey font.
  120.  *    (The 128 is the number of chars in a hardware font)
  121.  */
  122. int
  123. numchars()
  124. {
  125.     if (vdevice.attr->a.softtext)
  126.         return((int)nchars);
  127.  
  128.     return(128);
  129. }
  130.  
  131. /*
  132.  * hershfont
  133.  *
  134.  * Load in a hershey font. First try the font library, if that fails try
  135.  * the current directory, otherwise return 0.
  136.  */
  137. int
  138. hershfont(fontname)
  139.     char    *fontname;
  140. {
  141.     FILE    *fp;
  142.     int    i, j;
  143.     short    nvects, n;
  144.     char    path[120], *flib;
  145.     
  146.     if ((flib = getenv("VFONTLIB")) == (char *)NULL) {
  147.         strcpy(path, FONTLIB);
  148. #ifdef PC
  149.         strcat(path, "\\");
  150. #else
  151.         strcat(path, "/");
  152. #endif
  153.         strcat(path, fontname);
  154.     } else {
  155.         strcpy(path, flib);
  156. #ifdef PC
  157.         strcat(path, "\\");
  158. #else
  159.         strcat(path, "/");
  160. #endif
  161.         strcat(path, fontname);
  162.     }
  163.  
  164. #ifdef PC
  165.     if ((fp = fopen(path, "r+b")) == (FILE *)NULL) 
  166.         if ((fp = fopen(fontname, "r+b")) == (FILE *)NULL) 
  167. #else
  168.     if ((fp = fopen(path, "r")) == (FILE *)NULL)
  169.         if ((fp = fopen(fontname, "r")) == (FILE *)NULL) 
  170. #endif
  171.             return (0);
  172.  
  173.     if (fread(&nchars, sizeof(nchars), 1, fp) != 1)
  174.         return (0);
  175.  
  176.     if (fread(&nvects, sizeof(nvects), 1, fp) != 1)
  177.         return(0);
  178.  
  179.     if (fread(&n, sizeof(n), 1,  fp) != 1)
  180.         return(0);
  181.  
  182.     ftab.as = (int)n;
  183.  
  184.     if (fread(&n, sizeof(n), 1, fp) != 1)
  185.         return(0);
  186.  
  187.     ftab.dec = (int)n;
  188.  
  189.     if (fread(&n, sizeof(n), 1, fp) != 1)
  190.         return(0);
  191.  
  192.     ftab.mw = (int)n;
  193.  
  194.     /*
  195.      *  Allocate space for it all....
  196.      */
  197.     if (Loaded) {
  198.         if (ftab.ind[0])
  199.             free(ftab.ind[0]);
  200.         if (ftab.ind)
  201.             free(ftab.ind);
  202.         Loaded = 0;
  203.     }
  204.  
  205.     ftab.ind = (char **)vallocate(sizeof(char *)*(nchars + 1));
  206.  
  207.     ftab.p = (char *)vallocate((unsigned)(2 * nvects));
  208.  
  209.     /*
  210.      *  As we read in each character, figure out what ind should be
  211.      */
  212.  
  213.     for (i = 0; i < nchars; i++) {
  214.         if ((j = fread(&n , sizeof(n), 1, fp)) != 1)
  215.             return(0);
  216.  
  217.         if ((j = fread(ftab.p, 1, (unsigned)n, fp)) != (unsigned)n)
  218.             return(0);
  219.  
  220.         ftab.ind[i] = ftab.p;
  221.         ftab.p += n;
  222.     }
  223.  
  224.     ftab.ind[nchars] = ftab.p;    /* To Terminate the last one */
  225.  
  226.     fclose(fp);
  227.     vdevice.attr->a.softtext = Loaded = 1;
  228.  
  229. #ifdef PC
  230.     if (*fontname == '\\')
  231.         strcpy(vdevice.attr->a.font, strrchr(fontname, '\\') + 1);
  232. #else
  233.     if (*fontname == '/')
  234.         strcpy(vdevice.attr->a.font, rindex(fontname, '/') + 1);
  235. #endif
  236.     else 
  237.         strcpy(vdevice.attr->a.font, fontname);
  238.  
  239.     return(1);
  240. }
  241.  
  242. /*
  243.  * getcharsize
  244.  *
  245.  *    get the width and height of a single character. At the moment, for
  246.  * the hershey characters, the height returned is always that of the
  247.  * difference between the maximun descender and ascender.
  248.  *
  249.  */
  250. void
  251. getcharsize(c, width, height)
  252.     char    c;
  253.     float    *width, *height;
  254. {
  255.  
  256.     float    a, b;
  257.  
  258.     if (!vdevice.initialised)
  259.         verror("getcharsize: vogle not initialised");
  260.  
  261.     if (vdevice.attr->a.softtext) {
  262.         if (!Loaded)
  263.             verror("getcharsize: no software font loaded");
  264.     
  265.         *height = (float)(ftab.as - ftab.dec) * SCSIZEY;
  266.  
  267.         if (vdevice.attr->a.fixedwidth)
  268.             *width = ftab.mw * SCSIZEX;
  269.         else
  270.             *width = (ftab.ind[c - 32][1] - ftab.ind[c - 32][0]) * SCSIZEX;
  271.     } else {
  272.         VtoWxy(vdevice.hwidth, vdevice.hheight, width, height);
  273.         VtoWxy(0.0, 0.0, &a, &b);
  274.         *height -= b;
  275.         *width -= a;
  276.     }
  277. }
  278.  
  279. /*
  280.  * drawchar
  281.  *
  282.  * Display a character from the currently loaded font.
  283.  */
  284. void
  285. drawchar(c)
  286.     int    c;
  287. {
  288.     char    *p, *e;
  289.     Token    *pr;
  290.     int    Move, i, x, y, xt, yt, sync;
  291.     float    xp, yp, tmp, xsave, ysave;
  292.     float    tcos, tsin;
  293.  
  294.     if (vdevice.inobject) {
  295.         pr = newtokens(2);
  296.  
  297.         pr[0].i = DRAWCHAR;
  298.         pr[1].i = c;
  299.  
  300.         return;
  301.     }
  302.  
  303.     if (!vdevice.attr->a.softtext) {
  304.         if (!vdevice.cpVvalid)
  305.             actual_move();
  306.         drawhardchar(c);
  307.         rmove(getfontwidth(), 0.0, 0.0);
  308.         return;
  309.     }
  310.  
  311.     if (!Loaded)
  312.         verror("drawchar: no font loaded");
  313.  
  314.     if ((sync = vdevice.sync))
  315.         vdevice.sync = 0;
  316.  
  317.     tcos = vdevice.attr->a.textcos;
  318.     tsin = vdevice.attr->a.textsin;
  319.  
  320.  
  321.     if ((i = c - 32) < 0)
  322.         i = 0;
  323.     if (i >= nchars)
  324.         i = nchars - 1;
  325.  
  326.     xsave = vdevice.cpW[V_X];
  327.     ysave = vdevice.cpW[V_Y];
  328.  
  329.     Move = 1;
  330.     xt = (vdevice.attr->a.fixedwidth ? -ftab.mw / 2 : XCOORD(ftab.ind[i][0]));
  331.     yt = ftab.dec;
  332.  
  333.     /* Justify in the x direction */
  334.     if (vdevice.attr->a.justify & V_XCENTERED) {
  335.         xt = 0;
  336.     } else if (vdevice.attr->a.justify & V_RIGHT) {
  337.         xt = (vdevice.attr->a.fixedwidth ? ftab.mw / 2 : -XCOORD(ftab.ind[i][0]));
  338.     }
  339.  
  340.     /* Justify in the y direction */
  341.     if (vdevice.attr->a.justify & V_YCENTERED) {
  342.         yt = 0;
  343.     } else if (vdevice.attr->a.justify & V_TOP) {
  344.         yt = -ftab.dec;
  345.     }
  346.  
  347.     e = ftab.ind[i+1];
  348.     p = ftab.ind[i] + 2;
  349.     while(p < e) {
  350.         x = XCOORD((int)(*p++));
  351.         y = YCOORD((int)(*p++));
  352.         if (x != -50) {            /* means move */
  353.             xp = (float)(x - xt)*SCSIZEX;
  354.             yp = (float)(y - yt)*SCSIZEY;
  355.             tmp = xp;
  356.             xp = tcos*tmp - tsin*yp + xsave;
  357.             yp = tsin*tmp + tcos*yp + ysave;
  358.             if (Move) {
  359.                 Move = 0;
  360.                 move(xp, yp, vdevice.cpW[V_Z]);
  361.             } else 
  362.                     draw(xp, yp, vdevice.cpW[V_Z]);
  363.         } else
  364.             Move = 1;
  365.     }
  366.     /*
  367.      * Move to right hand of character.
  368.      */
  369.     
  370.     tmp = vdevice.attr->a.fixedwidth ? (float)ftab.mw : (float)(ftab.ind[i][1] - ftab.ind[i][0]);
  371.     tmp *= SCSIZEX;
  372.     xsave += tcos*tmp;
  373.     ysave += tsin*tmp;
  374.     move(xsave, ysave, vdevice.cpW[V_Z]);
  375.  
  376.     if (sync) {
  377.         vdevice.sync = 1;
  378.         (*vdevice.dev.Vsync)();
  379.     }
  380. }
  381.  
  382. /*
  383.  * drawhardchar
  384.  *
  385.  *    Displays a hardware character.
  386.  *    NOTE: Only does gross clipping to the viewport.
  387.  *          Current world position becomes undefined (ie you have
  388.  *          to do an explicit move after calling hardware text)
  389.  */
  390. static void
  391. drawhardchar(c)
  392.     char    c;
  393. {
  394.  
  395.     if (!vdevice.clipoff) {
  396.         if (vdevice.cpVx - (int)vdevice.hwidth > vdevice.maxVx)
  397.             return;
  398.  
  399.         if (vdevice.cpVx < vdevice.minVx)
  400.             return;
  401.  
  402.         if (vdevice.cpVy - (int)vdevice.hheight > vdevice.maxVy)
  403.             return;
  404.  
  405.         if (vdevice.cpVy < vdevice.minVy)
  406.             return;
  407.     }
  408.  
  409.     (*vdevice.dev.Vchar)(c);
  410. }
  411.  
  412. /*
  413.  * textsize
  414.  *
  415.  * set software character scaling values 
  416.  *
  417.  * Note: Only changes software char size. Should be called
  418.  * after a font has been loaded.
  419.  *
  420.  */
  421. void
  422. textsize(width, height)
  423.     float    width, height;
  424. {
  425.     float    a;
  426.     Token    *tok;
  427.  
  428.     if (!vdevice.initialised)
  429.         verror("textsize: vogle not initialised");
  430.  
  431.     if (!vdevice.attr->a.softtext)
  432.         return;
  433.  
  434.     if (!Loaded)
  435.         verror("textsize: no font loaded");
  436.  
  437.     if (vdevice.inobject) {
  438.         tok = newtokens(3);
  439.  
  440.         tok[0].i = TEXTSIZE;
  441.         tok[1].f = width;
  442.         tok[2].f = height;
  443.  
  444.         return;
  445.     }
  446.  
  447.     a = (float)MAX(ftab.mw, (ftab.as - ftab.dec));
  448.     vdevice.attr->a.fontwidth = width;
  449.     vdevice.attr->a.fontheight = height;
  450.     SCSIZEX = width / a;
  451.     SCSIZEY = height / a;
  452. }
  453.  
  454. /*
  455.  * getfontwidth
  456.  *
  457.  * Return the maximum Width of the current font.
  458.  *
  459.  */
  460. float
  461. getfontwidth()
  462. {
  463.     float    a, b, c, d;
  464.  
  465.     if (!vdevice.initialised)
  466.         verror("getfontwidth: vogle not initialised");
  467.  
  468.  
  469.     if (vdevice.attr->a.softtext) {
  470.         if (!Loaded)
  471.             verror("getfontwidth: No font loaded");
  472.  
  473.         return((float)(SCSIZEX * MAX(ftab.mw, (ftab.as - ftab.dec))));
  474.     } else {
  475.         VtoWxy(vdevice.hwidth, vdevice.hheight, &c, &d);
  476.         VtoWxy(0.0, 0.0, &a, &b);
  477.         c -= a;
  478.         return(c);
  479.     }
  480. }
  481.  
  482. /* 
  483.  * getfontheight
  484.  *
  485.  * Return the maximum Height of the current font
  486.  */
  487. float 
  488. getfontheight()
  489. {
  490.     float    a, b, c, d;
  491.  
  492.     if (!vdevice.initialised)
  493.         verror("getfontheight: vogle not initialized");
  494.  
  495.     if (vdevice.attr->a.softtext) {
  496.         if (!Loaded)
  497.             verror("getfontheight: No font loaded");
  498.  
  499.         return((float)(SCSIZEY * MAX(ftab.mw, (ftab.as - ftab.dec))));
  500.     } else {
  501.         VtoWxy(vdevice.hwidth, vdevice.hheight, &c, &d);
  502.         VtoWxy(0.0, 0.0, &a, &b);
  503.         d -= b;
  504.         return(d);
  505.     }
  506. }
  507.  
  508. /*
  509.  * getfontsize
  510.  *
  511.  * get the current character size in user coords.
  512.  * Hardware text may or may not be really that accurate,
  513.  * depending on what type of font you are using on the device.
  514.  * For software Hershey fonts, the character width is that of
  515.  * a the widest character and the height the height of the tallest.
  516.  *
  517.  */
  518. void
  519. getfontsize(cw, ch)
  520.     float     *cw, *ch;
  521. {
  522.     *cw = getfontwidth();
  523.     *ch = getfontheight();
  524. }
  525.  
  526. /*
  527.  * drawhstr
  528.  *
  529.  * Display the text string using the currently loaded Hershey font
  530.  */
  531. static void
  532. drawhstr(string)
  533.     char    *string;
  534. {
  535.     char    c;
  536.     int    i, sync, oldClipoff, NeedClip, oldJustify;
  537.     float    p[4], q[4];
  538.     float    strlength(), getfontheight();
  539.  
  540.  
  541.     if (!vdevice.initialised) 
  542.         verror("drawhstr: not initialized");
  543.  
  544.     /*
  545.      * For the duration of hershey strings, turn off
  546.      * "vdevice.attr->a.justify" as we have already compensated
  547.      * for it in drawstr()
  548.      */
  549.     oldJustify = vdevice.attr->a.justify;
  550.     vdevice.attr->a.justify = V_LEFT;
  551.  
  552.     /*
  553.      * Determine if we can get away with "clipoff"
  554.      */
  555.     oldClipoff = vdevice.clipoff;
  556.     if (!oldClipoff) {  /* Only do this if we have to ... ie. if clipping is on */
  557.         q[0] = vdevice.cpW[V_X];
  558.         q[1] = vdevice.cpW[V_Y];
  559.         q[2] = vdevice.cpW[V_Z];
  560.         q[3] = 1.0;
  561.         multvector(p, q, vdevice.transmat->m);
  562.         NeedClip = 0;
  563.         for (i = 0; i < 3; i++)
  564.             NeedClip = ((p[3] + p[i] < 0.0) ||
  565.                     (p[3] - p[i] < 0.0)) || NeedClip;
  566.         if (!NeedClip) {       /* The other end, only if we have to */
  567.             q[0] += strlength(string);
  568.             q[1] += getfontheight();
  569.             multvector(p, q, vdevice.transmat->m);
  570.             NeedClip = 0;
  571.             for (i = 0; i < 3; i++)
  572.                 NeedClip = ((p[3] + p[i] < 0.0) || 
  573.                         (p[3] - p[i] < 0.0)) || NeedClip;
  574.         }
  575.         if (!NeedClip)
  576.             vdevice.clipoff = 1; /* ie. Don't clip */
  577.  
  578.     }
  579.  
  580.     /*
  581.      * Now display each character
  582.      *
  583.      */
  584.     if ((sync = vdevice.sync))
  585.         vdevice.sync = 0;
  586.  
  587.     while ((c = *string++))
  588.         drawchar(c);
  589.     
  590.     if (sync) {
  591.         vdevice.sync = 1;
  592.         (*vdevice.dev.Vsync)();
  593.     }
  594.  
  595.     /*
  596.      * Restore ClipOff
  597.      */
  598.     vdevice.clipoff = oldClipoff;
  599.     vdevice.attr->a.justify = oldJustify;
  600. }
  601.  
  602. /*
  603.  * drawstr
  604.  *
  605.  * Draw a string from the current pen position.
  606.  *
  607.  */
  608. void
  609. drawstr(string)
  610.     char     *string;
  611. {
  612.     float    sl, width, height, cx, cy;
  613.     float    tcos, tsin;
  614.     char    *str = string, c;
  615.     Token    *tok;
  616.  
  617.     if(!vdevice.initialised) 
  618.         verror("drawstr: vogle not initialized");
  619.  
  620.     if (vdevice.inobject) {
  621.         tok = newtokens(2 + strlen(str) / sizeof(Token));
  622.  
  623.         tok[0].i = DRAWSTR;
  624.         strcpy((char *)&tok[1], str);
  625.  
  626.         return;
  627.     }
  628.  
  629. #ifdef SUN_CC
  630.     /* Note that SUN's unbundled ANSI C compiler bitches about this
  631.     sl = (float)strlen(string);
  632.     ... so we change it to ... 
  633.     (The (float) IS an explicit cast .. isn't it?) */
  634.  
  635.     sl = (float)(size_t)strlen(string);
  636. #else
  637.     sl = (float)strlen(string);
  638. #endif
  639.  
  640.     tcos = vdevice.attr->a.textcos;
  641.     tsin = vdevice.attr->a.textsin;
  642.  
  643.     height = getfontheight();
  644.     width = strlength(string);
  645.  
  646.     /* Justify in the x direction */
  647.     if (vdevice.attr->a.justify & V_XCENTERED) {
  648.         width /= 2.0;
  649.     } else if (vdevice.attr->a.justify & V_RIGHT) {
  650.         ;    /* NO change */
  651.     } else {    /* V_LEFT as default */
  652.         width = 0.0;
  653.     }
  654.  
  655.     /* Justify in the y direction */
  656.     if (vdevice.attr->a.justify & V_YCENTERED) {
  657.         height /= 2.0;
  658.     } else if (vdevice.attr->a.justify & V_TOP) {
  659.         ;    /* NO change */
  660.     } else {    /* V_BOTTOM as default */
  661.         height = 0.0;
  662.     }
  663.  
  664.     cx = vdevice.cpW[V_X] + height * tsin - width * tcos;
  665.     cy = vdevice.cpW[V_Y] - height * tcos - width * tsin;
  666.  
  667.     move(cx, cy, vdevice.cpW[V_Z]);
  668.         
  669.         
  670.     if (vdevice.attr->a.softtext) {
  671.         /*  As we are using software text then call the routine 
  672.                 to display it in the current font */
  673.         drawhstr(string);
  674.     } else {
  675.         actual_move();    /* Really move there */
  676.  
  677.         /*   If not clipping then simply display text and return  */
  678.  
  679.         if (vdevice.clipoff)
  680.             (*vdevice.dev.Vstring)(string);
  681.         else { /* Check if string is within viewport */
  682.             if (vdevice.cpVx > vdevice.minVx &&
  683.                 vdevice.cpVx + (int)(sl * (vdevice.hwidth - 1)) < vdevice.maxVx &&
  684.                 vdevice.cpVy - (int)vdevice.hheight < vdevice.maxVy &&
  685.                     vdevice.cpVy > vdevice.minVy)
  686.                 (*vdevice.dev.Vstring)(string);
  687.             else
  688.                 while ((c = *str++)) {
  689.                     drawhardchar(c);
  690.                     vdevice.cpVx += vdevice.hwidth;
  691.                 }
  692.         }
  693.  
  694.         move(cx + getfontwidth() * sl, cy, vdevice.cpW[V_Z]);
  695.  
  696.     }
  697. }
  698.  
  699. /*
  700.  * istrlength
  701.  *
  702.  * Find out the length of a string in raw "Hershey coordinates".
  703.  */
  704. static    int
  705. istrlength(s)
  706.     char    *s;
  707. {
  708.     char    c;
  709.     int    i, len = 0;
  710.     
  711.     if (vdevice.attr->a.fixedwidth) 
  712.         return((int)(strlen(s) * ftab.mw));
  713.     else {
  714.         while ((c = *s++)) {
  715.             if ((i = (int)c - 32) < 0 || i >= nchars)
  716.                 i = nchars - 1;
  717.  
  718.             len += (ftab.ind[i][1] - ftab.ind[i][0]);
  719.         }
  720.         return (len);
  721.     }
  722. }
  723.  
  724. /*
  725.  * strlength
  726.  *
  727.  * Find out the length (in world coords) of a string.
  728.  *
  729.  */
  730. float
  731. strlength(s)
  732.     char    *s;
  733. {
  734.     if (!vdevice.initialised)
  735.         verror("strlength: vogle not initialised");
  736.  
  737.     if (vdevice.attr->a.softtext)
  738.         return((float)(istrlength(s) * SCSIZEX));
  739.     else
  740. #ifdef SUN_CC
  741.         /* Note that SUN's unbundled ANSI C compiler bitches here 
  742.         return((float)(strlen(s) * getfontwidth()));
  743.         ... so we write it as ... */
  744.         return((float)((size_t)strlen(s) * getfontwidth()));
  745. #else
  746.         return((float)strlen(s) * getfontwidth());
  747. #endif
  748. }
  749.  
  750. /*
  751.  * boxtext
  752.  *
  753.  * Draw text so it fits in a "box" - note only works with hershey text
  754.  */
  755. void
  756. boxtext(x, y, l, h, s)
  757.     float    x, y, l, h;
  758.     char    *s;
  759. {
  760.     float    oscsizex, oscsizey;
  761.     Token    *tok;
  762.  
  763.     if (!vdevice.initialised)
  764.         verror("boxtext: vogle not initialised");
  765.     
  766.     if (!vdevice.attr->a.softtext)
  767.         verror("boxtext: need a hershey vector font loaded");
  768.  
  769.     if (vdevice.inobject) {
  770.         tok = newtokens(6 + strlen(s) / sizeof(Token));
  771.  
  772.         tok[0].i = BOXTEXT;
  773.         tok[1].f = x;
  774.         tok[2].f = y;
  775.         tok[3].f = l;
  776.         tok[4].f = h;
  777.         strcpy((char *)&tok[5], s);
  778.  
  779.         return;
  780.     }
  781.  
  782.     oscsizex = SCSIZEX;
  783.     oscsizey = SCSIZEY;
  784.     /*
  785.      * set width so string length is the same a "l" 
  786.      */
  787.     SCSIZEX = l / (float)istrlength(s);
  788.  
  789.     /* 
  790.      * set character height so it's the same as "h" 
  791.      */
  792.     SCSIZEY = h / (float)(ftab.as - ftab.dec);
  793.     move(x, y, vdevice.cpW[V_Z]);
  794.     
  795.     drawstr(s);
  796.  
  797.     SCSIZEX = oscsizex;
  798.     SCSIZEY = oscsizey;
  799. }
  800.  
  801. /*
  802.  * boxfit
  803.  *
  804.  * Set up the scales etc for text so that a string of "nchars" characters
  805.  * of the maximum width in the font fits in a box.
  806.  */
  807. void
  808. boxfit(l, h, nchars)
  809.     float    l, h;
  810.     int    nchars;
  811. {
  812.     if (!vdevice.initialised) 
  813.         verror("boxfit: vogle not initialised");
  814.  
  815.     if (!vdevice.attr->a.softtext) 
  816.         verror("boxfit: cannot rescale hardware font");
  817.  
  818.     SCSIZEX = l / (float)(nchars * ftab.mw);
  819.     SCSIZEY = h / (float)(ftab.as - ftab.dec);
  820. }
  821.  
  822. /*
  823.  * centertext
  824.  *
  825.  *    Turns centering of text on or off
  826.  *    Turns off all other justifying.
  827.  *    (Just like in old VOGLE).
  828.  */
  829. void
  830. centertext(onoff)
  831.     int    onoff;
  832. {
  833.     if (onoff)
  834.         vdevice.attr->a.justify = V_XCENTERED | V_YCENTERED;
  835.     else
  836.         vdevice.attr->a.justify = V_LEFT | V_BOTTOM;
  837. }
  838.  
  839. /*
  840.  * textjustify
  841.  *
  842.  *    Directly turns on/off justification
  843.  */
  844. void
  845. textjustify(val)
  846.     unsigned val;
  847. {
  848.     vdevice.attr->a.justify = val;
  849. }
  850.  
  851. /*
  852.  * xcentertext
  853.  *
  854.  *    Directly turns on xcentering
  855.  */
  856. void
  857. xcentertext()
  858. {
  859.     vdevice.attr->a.justify |= V_XCENTERED;
  860.     vdevice.attr->a.justify &= ~(V_LEFT | V_RIGHT);
  861. }
  862.  
  863. /*
  864.  * ycentertext
  865.  *
  866.  *    Directly turns on ycentering
  867.  */
  868. void
  869. ycentertext()
  870. {
  871.     vdevice.attr->a.justify |= V_YCENTERED;
  872.     vdevice.attr->a.justify &= ~(V_TOP | V_BOTTOM);
  873. }
  874.  
  875. /*
  876.  * leftjustify
  877.  *
  878.  *    Turns on leftjustification
  879.  */
  880. void
  881. leftjustify()
  882. {
  883.     /*
  884.      * If left justification is on, then V_XCENTER must be off
  885.      * and V_RIGHT must be off
  886.      */
  887.     vdevice.attr->a.justify |= V_LEFT;
  888.     vdevice.attr->a.justify &= ~(V_RIGHT | V_XCENTERED);
  889. }
  890.  
  891. /*
  892.  * rightjustify
  893.  *
  894.  *    Turns on rightjustification
  895.  */
  896. void
  897. rightjustify()
  898. {
  899.     /*
  900.      * If right justification is on, then V_XCENTER must be off
  901.      * and V_LEFT must be off
  902.      */
  903.     vdevice.attr->a.justify |= V_RIGHT;
  904.     vdevice.attr->a.justify &= ~(V_LEFT | V_XCENTERED);
  905. }
  906.  
  907. /*
  908.  * topjustify
  909.  *
  910.  *    Turns on topjustification
  911.  */
  912. void
  913. topjustify()
  914. {
  915.     /*
  916.      * If top justification is on, then V_YCENTER must be off
  917.      * and V_BOTTOM must be off
  918.      */
  919.     vdevice.attr->a.justify |= V_TOP;
  920.     vdevice.attr->a.justify &= ~(V_BOTTOM | V_YCENTERED);
  921. }
  922.  
  923.  
  924. /*
  925.  * bottomjustify
  926.  *
  927.  *    Turns on bottomjustification
  928.  */
  929. void
  930. bottomjustify()
  931. {
  932.     /*
  933.      * If bottom justification is on, then V_YCENTER must be off
  934.      * and V_TOP must be off
  935.      */
  936.     vdevice.attr->a.justify |= V_BOTTOM;
  937.     vdevice.attr->a.justify &= ~(V_TOP | V_YCENTERED);
  938. }
  939.  
  940. /*
  941.  * fixedwidth
  942.  *
  943.  *    Turns fixedwidth text on or off
  944.  */
  945. void
  946. fixedwidth(onoff)
  947.     int    onoff;
  948. {
  949.     vdevice.attr->a.fixedwidth = onoff;
  950. }
  951.  
  952. /*
  953.  * textang
  954.  *
  955.  * set software character angle in degrees
  956.  *
  957.  * strings will be written along a line 'ang' degrees from the 
  958.  * horizontal screen direction
  959.  *
  960.  * Note: only changes software character angle
  961.  *
  962.  */
  963. void
  964. textang(ang)
  965.     float    ang;
  966. {
  967.     Token    *tok;
  968.  
  969.     if (!vdevice.initialised) 
  970.         verror("textang: vogle not initialised");
  971.  
  972.     if (vdevice.inobject) {
  973.         tok = newtokens(3);
  974.  
  975.         tok[0].i = TEXTANG;
  976.         tok[1].f = cos((double)(ang * D2R));
  977.         tok[2].f = sin((double)(ang * D2R));
  978.  
  979.         return;
  980.     }
  981.  
  982.     vdevice.attr->a.textcos = cos((double)(ang * D2R));
  983.     vdevice.attr->a.textsin = sin((double)(ang * D2R));
  984. }
  985.  
  986. /*
  987.  * Actually do a move (multplying by the current transform and updating the
  988.  * actual screen coords) instead of just setting the current spot in world
  989.  * coords.
  990.  */
  991. static void
  992. actual_move()
  993. {
  994.     Vector    v2;
  995.  
  996.     multvector(v2, vdevice.cpW, vdevice.transmat->m);
  997.     vdevice.cpVvalid = 0;
  998.  
  999.     vdevice.cpVx = WtoVx(v2);
  1000.     vdevice.cpVy = WtoVy(v2);
  1001.     
  1002.     copyvector(vdevice.cpWtrans, v2);
  1003. }
  1004.