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