home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 5 / DATAFILE_PDCD5.iso / utilities / m / makedrawf / Source / c / stomach < prev    next >
Encoding:
Text File  |  1997-04-20  |  43.5 KB  |  1,444 lines

  1. /* stomach.c
  2.  *
  3.  * That portion of mkdrawf 3 dealing with what happens to tokens once
  4.  * they've been expanded as far as possible.
  5.  *
  6.  * The mouth/stomach terminology is, I believe, due to Knuth.
  7.  */
  8.  
  9. #include <ctype.h>
  10. #include <math.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14.  
  15. #include "mkdrawf.h"
  16.  
  17. #ifdef EXPRO
  18. #include "exprofle.h"
  19. #endif
  20.  
  21. /* -------------------------- forward declarations -------------------------- */
  22.  
  23.  
  24. static void do_object(int);
  25.  
  26.  
  27. /* -------------------------- buffers -------------------------- */
  28.  
  29.  
  30. /* The output from this program can get very long, but we don't know
  31.  * how long. We could do all our memory allocation with a Flex-style
  32.  * shifting heap, but that would be inefficient: instead we build the
  33.  * output in a number of 16k buffers, and allocate more of them as
  34.  * they are needed.
  35.  *
  36.  * We never check for overflow past |max_n_buffers|; if you are going
  37.  * to produce a drawfile larger than 16Mb, you can probably cope with
  38.  * recompiling.
  39.  */
  40.  
  41. #define buffer_size 4096    /* in words, not in bytes */
  42. #define max_n_buffers 1024
  43.  
  44. static int *buffers[max_n_buffers];
  45. static int n_buffers=0;
  46.  
  47. /* The following function copies a word-aligned block of memory into the
  48.  * drawfile. It grabs as many new blocks as are needed.
  49.  * |from| is an honest-to-goodness pointer.
  50.  * |to| is measured in words from the start of the file.
  51.  * |size| is measured in words.
  52.  * This will go wrong if called with silly values. So don't.
  53.  */
  54. static void put_block(int *from, uint to, uint size) {
  55.   uint b0,b1;    /* buffer number */
  56.   uint a0,a1;    /* offsets within buffers */
  57.   b0=to/buffer_size; a0=to-b0*buffer_size;
  58.   b1=(to+size-1)/buffer_size; a1=(to+size)-b1*buffer_size;
  59.   /* make sure the space exists */
  60.   while (b1>=n_buffers)
  61.     buffers[n_buffers++]=(int *)xmalloc(buffer_size*sizeof(Word),"a buffer");
  62.   if (b1==b0) { memcpy(buffers[b0]+a0,from,size*sizeof(Word)); return; }
  63.   memcpy(buffers[b0]+a0,from,(buffer_size-a0)*sizeof(Word));
  64.   memcpy(buffers[b1],from+size-a1,a1*sizeof(Word));
  65.   from+=buffer_size-a0;
  66.   /* now we've done both ends. What about the middle? */
  67.   while (++b0<b1) {
  68.     memcpy(buffers[b0],from,buffer_size*sizeof(Word));
  69.     from+=buffer_size;
  70.   }
  71. }
  72.  
  73. /* This performs the inverse operation to that performed by |put_block()|.
  74.  * |from| is a word offset from start-of-file.
  75.  * |to| is a pointer.
  76.  * |size| is in words.
  77.  * This is used for computing path bboxes; nowhere else.
  78.  */
  79.  
  80. #ifndef SLOPPY
  81. static void get_block(uint from, int *to, uint size) {
  82.   uint b0,b1;
  83.   uint a0,a1;
  84.   b0=from/buffer_size; a0=from-b0*buffer_size;
  85.   b1=(from+size-1)/buffer_size; a1=(from+size)-b1*buffer_size;
  86.   /* If space doesn't exist, it's an error */
  87.   if (b1>=n_buffers) minor("Impossible memory reference");
  88.   while (b1>=n_buffers)
  89.     buffers[n_buffers++]=(int *)xmalloc(buffer_size*sizeof(Word),"a buffer");
  90.   if (b1==b0) { memcpy(to,buffers[b0]+a0,size*sizeof(Word)); return; }
  91.   memcpy(to,buffers[b0]+a0,(buffer_size-a0)*sizeof(Word));
  92.   memcpy(to+size-a1,buffers[b1],a1*sizeof(Word));
  93.   to+=buffer_size-a0;
  94.   /* now we've done both ends. What about the middle? */
  95.   while (++b0<b1) {
  96.     memcpy(to,buffers[b0],buffer_size*sizeof(Word));
  97.     to+=buffer_size;
  98.   }
  99. }
  100. #endif
  101.  
  102. /* This is just like |put_block| except that everything's measured in bytes.
  103.  * It's only used for making font tables.
  104.  */
  105. static void put_bytes(char *from, uint to, uint size) {
  106.   uint b0,b1;
  107.   uint a0,a1;
  108.   b0=to/(buffer_size*4); a0=to-b0*(buffer_size*4);
  109.   b1=(to+size-1)/(buffer_size*4); a1=(to+size)-b1*(buffer_size*4);
  110.   /* make sure the space exists */
  111.   while (b1>=n_buffers)
  112.     buffers[n_buffers++]=(int *)xmalloc(buffer_size*sizeof(Word),"a buffer");
  113.   if (b1==b0) { memcpy((char*)(buffers[b0])+a0,from,size); return; }
  114.   memcpy((char*)(buffers[b0])+a0,from,buffer_size-a0);
  115.   memcpy((char*)(buffers[b1]),from+size-a1,a1);
  116.   from+=buffer_size*4-a0;
  117.   /* now we've done both ends. What about the middle? */
  118.   while (++b0<b1) {
  119.     memcpy(buffers[b0],from,buffer_size*sizeof(Word));
  120.     from+=buffer_size*4;
  121.   }
  122. }
  123.  
  124. /* If all you want to do is write one word:
  125.  */
  126. static void put_word(int w, int at) {
  127.   uint a,b;
  128.   b=at/buffer_size; a=at-b*buffer_size;
  129.   while (b>=n_buffers)
  130.     buffers[n_buffers++]=(int *)xmalloc(buffer_size*sizeof(Word),"a buffer");
  131.   buffers[b][a]=w;
  132. }
  133.  
  134. /* One very common operation is writing a single word at the end
  135.  * of the file-so-far. Here's how we do that:
  136.  */
  137.  
  138. static uint curr_addr=0;    /* measured in words from start of file */
  139.  
  140. static void put_word_at_end(int w) {
  141.   uint a,b;
  142.   b=curr_addr/buffer_size; a=curr_addr-b*buffer_size;
  143.   if (b>=n_buffers)
  144.     buffers[n_buffers++]=(int *)xmalloc(buffer_size*sizeof(Word),"a buffer");
  145.   buffers[b][a]=w;
  146.   ++curr_addr;
  147. }
  148.  
  149. /* This is as good a moment as any for introducing |output_file|, which
  150.  * is just what you think it is.
  151.  */
  152. static FILE *output_file=0;
  153.  
  154.  
  155. /* -------------------------- more readers -------------------------- */
  156.  
  157.  
  158. /* Sometimes we want to read a whole bounding box, or a transformation
  159.  * matrix. Here are functions to do those things.
  160.  */
  161.  
  162. /* Read a bounding box: x0,y0,x1,y1. |b| should point to `x0'.
  163.  */
  164. static void read_bbox(int *b) {
  165.   b[0]=read_real640();
  166.   b[1]=read_real640();
  167.   b[2]=read_real640();
  168.   b[3]=read_real640();
  169. }
  170.  
  171. /* Read a matrix: a,b,c,d,xx,yy. |m| should point to `a'.
  172.  */
  173. static void read_matrix(int *m) {
  174.   m[0]=(int)floor(read_double()*65536+.5);
  175.   m[1]=(int)floor(read_double()*65536+.5);
  176.   m[2]=(int)floor(read_double()*65536+.5);
  177.   m[3]=(int)floor(read_double()*65536+.5);
  178.   m[4]=read_real1000();
  179.   m[5]=read_real1000();
  180. }
  181.  
  182.  
  183. /* -------------------------- the font table -------------------------- */
  184.  
  185.  
  186. /* We remember the names of all fonts used in the drawfile. This makes
  187.  * it possible to use the Font Manager to compute bounding boxes, and
  188.  * also makes it possible to build a font table "automatically".
  189.  *
  190.  * |fname[i]| is the name of the font that's number |i| in the font table.
  191.  * NB |fname[0]| is never actually used.
  192.  */
  193. static char *fname[256];
  194.  
  195. /* For those automatic font tables, we need to remember some facts
  196.  * about what fonts we've seen so far.
  197.  * The variable |had_ftable| contains 0 initially; 1 after a font table
  198.  * object has occurred; 2 if an implicit font table is being made.
  199.  * To make things easier at the end of the program, we also keep track
  200.  * of the total amount of space we need for the font table, in the
  201.  * "automatic font table" case.
  202.  */
  203. static int had_ftable=0;
  204. static int ft_size=0;
  205.  
  206. /* |font_number(s)| returns the number of the font whose name is |s|,
  207.  * if there is one; otherwise, it allocates a new number, if possible;
  208.  * otherwise it moans and returns 0 (system font).
  209.  *
  210.  * If it allocates a new font, it also updates |ft_size|.
  211.  */
  212. int font_number(const char *s) {
  213.   int i;
  214.   /* Does it exist already? */
  215.   for (i=1;i<256;++i)
  216.     if (fname[i] && cistreq(s,fname[i])) return i;
  217.   /* Allocate a new one */
  218.   if (had_ftable==1) {
  219.     minor("Font name `%s' isn't in the font table",s);
  220.     return 0; }
  221.   had_ftable=2;
  222.   for (i=1;i<256 && fname[i];++i) ;
  223.   if (i==256) {
  224.     minor("More than 255 fonts in one drawfile");
  225.     return 0; }
  226.   fname[i]=(char*)s;
  227.   ft_size+=strlen(s)+2;    /* 1 for terminating null, 1 for font number */
  228.   return i;
  229. }
  230.  
  231. /* You should also read the code for |do_ftable()|,
  232.  * and the bit of |main()| that deals with implicit font tables.
  233.  */
  234.  
  235. /* -------------------------- bounding boxes -------------------------- */
  236.  
  237.  
  238. /* There is a global bounding box. But the variable |global_bounding_box|
  239.  * doesn't always contain *the* global bbox, because of the way we do
  240.  * group and tagged objects. (Namely, we make it point somewhere else,
  241.  * so that the usual routines compute the group/tagged object bbox,
  242.  * and put it back when we've finished.)
  243.  * So we do it like this:
  244.  */
  245. static int gbgbgb[4]={0x7FFFFFFF,0x7FFFFFFF,0x80000000,0x80000000};
  246. static int *global_bounding_box=gbgbgb;
  247.  
  248. /* |update_bbox(h,p)| updates the bbox in the object header at |h|
  249.  * to make sure it includes the point at |p|.
  250.  */
  251. static void update_bbox(int *h, int *p) {
  252.   int x,y;
  253.   x=p[0]; y=p[1];
  254.   if (x<h[2]) h[2]=x;
  255.   if (y<h[3]) h[3]=y;
  256.   if (x>h[4]) h[4]=x;
  257.   if (y>h[5]) h[5]=y;
  258. }
  259.  
  260. /* |update_gbbox(h)| updates the global bbox to make sure it includes
  261.  * everything in the bbox in the object header at |h|.
  262.  */
  263. static void update_gbbox(int *h) {
  264.   if (h[2]<global_bounding_box[0]) global_bounding_box[0]=h[2];
  265.   if (h[3]<global_bounding_box[1]) global_bounding_box[1]=h[3];
  266.   if (h[4]>global_bounding_box[2]) global_bounding_box[2]=h[4];
  267.   if (h[5]>global_bounding_box[3]) global_bounding_box[3]=h[5];
  268. }
  269.  
  270. /* The hardest sort of bbox to compute in the first place is that for a
  271.  * piece of text. Here's a routine that uses various Font_.. SWIs to do
  272.  * that.
  273.  * On entry we are guaranteed that n is 0 or a number in 1..255 corresponding
  274.  * to something in the font table.
  275.  * NOTE: This doesn't give the same result as !Draw, because !Draw calls
  276.  *       the (deprecated) Font_StringBBox instead of Font_ScanString.
  277.  */
  278. static void compute_text_bbox(int n, char *s, int x, int y, int xs, int ys,
  279.                               int flags, int *bp, int *matrix) {
  280.   int hdl=0;
  281.   int zog[9];
  282.   if (!s[0]) {    /* empty string -- special case */
  283.     bp[0]=bp[2]=x; bp[1]=bp[3]=y;
  284.     return; }
  285.   if (n) {
  286.     hdl=findfont(fname[n],(xs+20)/40,(ys+20)/40,0,0);
  287.     if (!hdl) {
  288.       warn("I can't find font `%s'; I'm guessing for its bbox",fname[n]);
  289.       n=0; }
  290.   }
  291.   if (!hdl) {
  292.     /* Have to guess. Be pessimistic. */
  293.     bp[0]=x; bp[1]=y-xs/2;
  294.     bp[2]=x+xs*strlen(s); bp[3]=y+ys;
  295.     return;
  296.   }
  297.   stringbbox(hdl,s,zog,flags,matrix);
  298.   losefont(hdl);
  299.   bp[0]=x+(int)floor(zog[5]*0.64); bp[1]=y+(int)floor(zog[6]*0.64);
  300.   bp[2]=x+(int)ceil(zog[7]*0.64); bp[3]=y+(int)ceil(zog[8]*0.64);
  301. }
  302.  
  303. /* Computing path bboxes accurately is in fact even worse. Fortunately
  304.  * the OS already has a routine to do that. The following calls it.
  305.  * |path| should be the file offset (in words) of the start of a
  306.  * newly constructed path object. |data| should be the file offset
  307.  * of the start of the path data. |hdr| should point
  308.  * to the start of the header (beginning with the object type and the
  309.  * size of the object). |size| should be in words, and should include
  310.  * all the path data.
  311.  */
  312. #ifndef SLOPPY
  313. static void compute_path_bbox(uint path, uint data, int *hdr, int size) {
  314.   int *pp;
  315.   int ugh[4];        /* cap and join specification */
  316.   int pst=hdr[9];    /* path style word */
  317.   pp=(int*)xmalloc(size<<2,"path data for Draw_ProcessPath");
  318.   get_block(path+10,pp,size-10);
  319.   ugh[0]=(pst&3)+((pst&12)<<6)+((pst&48)<<12);    /* joins & caps */
  320.   ugh[1]=0xA0000;    /* mitre limit = 10 */
  321.   ugh[2]=((pst&0xFF0000)>>12)+((pst&0xFF000000)>>4);    /* triangles */
  322.   ugh[3]=ugh[2];    /* both triangle caps are the same */
  323.   processpath(
  324.     pp+(data-path-10),        /* path data */
  325.     ((pst&64)>>5)+0x70000030,    /* fill style. Sorry */
  326.     0,            /* no transformation matrix */
  327.     25,            /* flatness used by !Draw */
  328.     hdr[8],        /* width */
  329.     ugh,        /* cap and join */
  330.     (pst&128)?pp:0,    /* dash specification, if any */
  331.     (int*)(((int)(hdr+2))+0x80000000)    /* bounding box */
  332.   );
  333.   xfree(pp);
  334. }
  335. #else
  336. #define compute_path_bbox(x,y,z)
  337. #endif
  338.  
  339. /* If we get an explicit <BoundingBox> keyword in a Group or Tagged
  340.  * object, we set the local bounding box to the right thing, and then
  341.  * ignore changes in the "global" bbox thereafter. The easiest way
  342.  * to do this is to "redirect" the global bbox to a dummy one.
  343.  * We set it up so that it never has to change; this makes things
  344.  * slightly faster.
  345.  */
  346. static int dummy_bbox[4]={0x80000000,0x80000000,0x7FFFFFFF,0x7FFFFFFF};
  347.  
  348.  
  349. /* -------------------------- objects -------------------------- */
  350.  
  351.  
  352. /* The next several sections define functions for reading in and
  353.  * dealing with object descriptions. The usual pattern is that we
  354.  * define a structure for the object header and any fixed-size
  355.  * data in the object, fill it in and use |put_block()|; the
  356.  * parsing itself is usually a |while|-loop and a switch, which
  357.  * just grabs successive "items".
  358.  *
  359.  * We put new objects at |curr_addr|, and increment it appropriately.
  360.  * There is often a variable called |p| which contains the value of
  361.  * |curr_addr| at the start of the object; it's used to compute the
  362.  * object size.
  363.  *
  364.  * Here goes...
  365.  */
  366.  
  367.  
  368. /* -------------------------- OBJECT: FontTable -------------------------- */
  369.  
  370.  
  371. static void do_ftable(void) {
  372.   double x;
  373.   char *s;
  374.   char zog[max_line_length+4];
  375.   int l;
  376.   int ignore=0;
  377.   uint ca;
  378.   if (had_ftable) {
  379.     minor("Only one font table is allowed per drawfile");
  380.     ignore=1; }
  381.   had_ftable=1;
  382.   read_openbr();
  383.   ca=(curr_addr+=2)*4;
  384.   while (1) {
  385.     if (get_x_token(0)) { minor("EOF or error in font table"); return; }
  386.     if (curr_token.type==t_closebr) break;
  387.     if (curr_token.type!=t_real) { minor("Font number expected"); continue; }
  388.     x=curr_token.value.D;
  389.     if (x<1 || x>255 | x!=floor(x)) {
  390.       minor("Invalid font number");
  391.       (void)read_string();
  392.       continue; }
  393.     s=read_string();
  394.     fname[(int)x]=s;
  395.     zog[0]=(char)x;
  396.     l=strlen(s)+2;
  397.     memcpy(zog+1,s,l-1);
  398.     if (!ignore) put_bytes(zog,ca,l);
  399.     ca+=l;
  400.     /* The PRMs lie! Entries in the font table are *not* padded to
  401.      * word boundaries. */
  402.   }
  403.   if (!ignore) {
  404.     ((int*)zog)[0]=0;
  405.     put_bytes(zog,ca,4-(ca&3));    /* clean up last word */
  406.     ca=(ca+3)&~3;
  407.     put_word(0,curr_addr-2);
  408.     put_word(8+ca-curr_addr*4,curr_addr-1);
  409.     curr_addr=ca>>2;
  410.   }
  411. }
  412.  
  413.  
  414. /* -------------------------- OBJECT: Text -------------------------- */
  415.  
  416.  
  417. typedef struct {
  418.   int ty; int sz;
  419.   int x0,y0,x1,y1;
  420.   uint fg,bg,st,xs,ys,xx0,yy0;
  421.   char tx[max_line_length];
  422. } txt;
  423.  
  424. static struct {
  425.   int a,b,c,d, xx,yy;
  426.   uint ff, fg,bg,st,xs,ys;
  427. } text_prev = {
  428.   65536,0,0,65536, 0,0,
  429.   0, 0,0xffffff00, 0, 12*640,12*640
  430. };
  431.  
  432. static void set_text_bbox_vars(int *bbox) {
  433.   set_variable("$_x0",bbox[0]/640.0);
  434.   set_variable("$_y0",bbox[1]/640.0);
  435.   set_variable("$_x1",bbox[2]/640.0);
  436.   set_variable("$_y1",bbox[3]/640.0);
  437. }
  438.  
  439. static void do_text(void) {
  440.   txt t={1,52,0,0,0,0,0,0xffffff00,0,7680,7680,0,0,""};
  441.   char *s=0;
  442.   int l;
  443.   int c;
  444.   int bbg=0;
  445.   int centred=0;
  446.   int cx=0,cy=0;    /* coords of centre. =0 just to pacify compiler. */
  447.   int virt=0;        /* virtual? */
  448.   memcpy(&t.fg,&text_prev.fg,5*sizeof(int));
  449.   read_openbr();
  450.   while (1) {
  451.     c=read_kwd_or_cbr(); if (c<0) break;
  452.     else switch(c) {
  453.       case k_BoundingBox:
  454.         read_bbox(&t.x0);
  455.         bbg=1; break;
  456.       case k_Colour: t.fg=read_colour(); break;
  457.       case k_Background: t.bg=read_colour(); break;
  458.       case k_Style: t.st=read_int(); break;
  459.       case k_Size: t.xs=read_real640();
  460.                    t.ys=read_real640(); break;
  461.       case k_StartAt: centred|=4;    /* for checking */
  462.                       t.xx0=read_real640();
  463.                       t.yy0=read_real640(); break;
  464.       case k_Text: s=read_string(); break;
  465.       case k_HCentreIn: centred|=1; /* fall through: */
  466.       case k_CentreIn:
  467.         centred|=2;
  468.         cx=read_real640(); cy=read_real640();
  469.         cx=(cx+read_real640())>>1;
  470.         cy=(cy+read_real640())>>1;
  471.         break;
  472.       case k_HCentreOn:
  473.         centred|=1;
  474.         cx=(read_real640()+read_real640())>>1; /* order doesn't matter :-) */
  475.         t.yy0=read_real640();
  476.         break;
  477.       case k_CentreAt:
  478.         centred|=2;
  479.         cx=read_real640(); cy=read_real640();
  480.         break;
  481.       case k_Virtual:
  482.         virt=1;
  483.         break;
  484.       default: minor("Illegal keyword in text object");
  485.     }
  486.   }
  487.   memcpy(&text_prev.fg,&t.fg,5*sizeof(int));
  488.   if (!s) { minor("No text in text object"); s=""; }
  489.   l=(strlen(s)>>2)+1;
  490.   memcpy(t.tx,s,l<<2);
  491.   t.sz=(l<<2)+52;
  492.   if (t.st>255 || t.st && !fname[t.st]) {
  493.     minor("Undeclared font number %d; replacing it with 0",t.st);
  494.     t.st=0; }
  495.   if (!bbg) compute_text_bbox(t.st,t.tx,t.xx0,t.yy0,t.xs,t.ys,0,&t.x0,0);
  496. #ifdef ONLINE_MEDIA
  497.   /* A text item with explicit bbox, first char '~' and in system font
  498.    * is a hot spot. Make sure its bbox doesn't get changed (too much)
  499.    * when it's reloaded into !Draw.
  500.    * This ghastly hack will be removed when the hotspot object type
  501.    * is introduced.
  502.    */
  503.   if (bbg && s[0]=='~' && t.st==0)
  504.     t.xs=(t.x1-t.x0)/strlen(t.tx);
  505. #endif
  506.   if (centred&3) {
  507.     int *bb=&t.x0;
  508.     int q[4];
  509.     int dx;
  510.     if ((centred&4) && !(centred&1))
  511.       warn("StartAt is ignored for centred text");
  512.     if (bbg) compute_text_bbox(t.st,t.tx,t.xx0,t.yy0,t.xs,t.ys,0,bb=q,0);
  513.     dx=cx-((bb[0]+bb[2]+1)>>1);
  514.     t.xx0+=dx; if (!bbg) { t.x0+=dx; t.x1+=dx; }
  515.     if (!(centred&1)) {
  516.       int dy=cy-((bb[1]+bb[3]+1)>>1);
  517.       t.yy0+=dy; if (!bbg) { t.y0+=dy; t.y1+=dy; }
  518.     }
  519.   }
  520.   update_gbbox((int*)&t);
  521.   set_text_bbox_vars(&t.x0);
  522.   if (!virt) {
  523.     put_block((int *)&t,curr_addr,l+13);
  524.     curr_addr+=l+13;
  525.   }
  526. }
  527.  
  528.  
  529. /* -------------------------- OBJECT: Path -------------------------- */
  530.  
  531. /* Warning: this is long. It's not all that difficult, but you will
  532.  * need to be aware of the different types of path element and the
  533.  * make-up of the path style word, and so on.
  534.  */
  535.  
  536. typedef struct {
  537.   int ty; int sz;
  538.   int x0,y0,x1,y1;
  539.   int fc,oc;
  540.   uint ow,st;
  541. } pth;
  542.  
  543. /* If we're calculating path bboxes the Right Way, we don't
  544.  * need to invoke |update_bbox()| in |do_path()|.
  545.  */
  546. #ifndef SLOPPY
  547. #define update_bbox(x,y)
  548. #endif
  549.  
  550. static void do_path() {
  551.   pth t={2,40,0x7FFFFFFF,0x7FFFFFFF,0x80000000,0x80000000,-1,0,0,66};
  552.   int p=curr_addr;
  553.   int dashOK=1;        /* are we allowed a dash spec now? */
  554.   int c;
  555.   int q[7];
  556.   int lx=0,ly=0;
  557.   int bbg=0;
  558.   uint qq=p+10;        /* always points to start of path data */
  559.   curr_addr=qq;        /* points after (initially empty) dash spec */
  560.   read_openbr();
  561.   while(1) {
  562.     c=read_kwd_or_cbr(); if (c<0) break;
  563.     else switch(c) {
  564.       case k_BoundingBox:
  565.         read_bbox(&t.x0);
  566.         bbg=1; break;
  567.       case k_FillColour: t.fc=read_colour(); break;
  568.       case k_OutlineColour: t.oc=read_colour(); break;
  569.       case k_Width: t.ow=read_real640(); break;
  570.       case k_Style:
  571.         read_openbr();
  572.         while(1) {
  573.           if (get_x_token(0)) error("EOF or error in path object");
  574.           if (curr_token.type==t_closebr) break;
  575.           if (curr_token.type!=t_keyword) {
  576.             minor("Expected a keyword"); continue; }
  577.           switch(curr_token.value.U) {
  578.             case k_Mitred: t.st&=~3; break;
  579.             case k_Round: t.st=(t.st&~3)|1; break;
  580.             case k_Bevelled: t.st=(t.st&~3)|2; break;
  581.             case k_EndCap: switch(read_kwd()) {
  582.               case k_Butt: t.st&=~12; break;
  583.               case k_Round: t.st=(t.st&~12)|4; break;
  584.               case k_Square: t.st=(t.st&~12)|8; break;
  585.               case k_Triangular: t.st|=12; break;
  586.               default: minor("Unknown endcap style"); } break;
  587.             case k_StartCap: switch(read_kwd()) {
  588.               case k_Butt: t.st&=~48; break;
  589.               case k_Round: t.st=(t.st&~48)|16; break;
  590.               case k_Square: t.st=(t.st&~48)|32; break;
  591.               case k_Triangular: t.st|=48; break;
  592.               default: minor("Unknown startcap style"); } break;
  593.             case k_WindingRule: switch(read_kwd()) {
  594.               case k_NonZero: t.st&=~64; break;
  595.               case k_EvenOdd: t.st|=64; break;
  596.               default: minor("Unknown winding rule"); } break;
  597.             case k_Dash: {
  598.               uint q=curr_addr, ndash=0;
  599.               if (!dashOK) { minor("Disallowed Dash"); continue; }
  600.               read_openbr();
  601.               put_word_at_end(0); put_word_at_end(0);
  602.               t.st|=128;
  603.               while (1) {
  604.                 if (get_x_token(0)) error("EOF or error in dash spec");
  605.                 if (curr_token.type==t_closebr) break;
  606.                 if (curr_token.type==t_keyword) {
  607.                   if (curr_token.value.U!=k_Offset) {
  608.                     minor("Illegal keyword in dash spec"); continue; }
  609.                   put_word(read_real640(),q);
  610.                   continue; }
  611.                 if (curr_token.type!=t_real) {
  612.                   minor("Non-number in dash spec"); curr_token.value.D=0; }
  613.                 /* NB: the following is the unique dependency on points
  614.                  * other than those in |read_real640()| and |read_real1000()|.
  615.                  * If you make a "millimetres" version, change this.
  616.                  */
  617.                 put_word_at_end((int)floor(640*unit*curr_token.value.D+.5));
  618.                 ++ndash;
  619.               }
  620.               qq=curr_addr;
  621.               put_word(ndash,q+1); }
  622.               break;
  623.             case k_CapWidth:
  624.               c=read_int();
  625.               if (c<0 || c>255) { minor("Out-of-range cap width"); c=0; }
  626.               t.st=(t.st&~0xFF0000)|(c<<16);
  627.               break;
  628.             case k_CapLength:
  629.               c=read_int();
  630.               if (c<0 || c>255) { minor("Out-of-range cap length"); c=0; }
  631.               t.st=(t.st&~0xFF000000)|(c<<24);
  632.               break;
  633.             default: minor("Illegal keyword in path style");
  634.           }
  635.         }
  636.         break;    /* end of style processing */
  637.       case k_Move:
  638.         dashOK=0;
  639.         q[0]=2; q[1]=lx=read_real640(); q[2]=ly=read_real640();
  640.         put_block(q,curr_addr,3); curr_addr+=3;
  641.         update_bbox((int*)&t,q+1);
  642.         break;
  643.       case k_Close:
  644.         dashOK=0;
  645.         put_word_at_end(5);
  646.         break;
  647.       case k_Line:
  648.         dashOK=0;
  649.         q[0]=8; q[1]=lx=read_real640(); q[2]=ly=read_real640();
  650.         put_block(q,curr_addr,3); curr_addr+=3;
  651.         update_bbox((int*)&t,q+1);
  652.         break;
  653.       case k_Curve:
  654.         dashOK=0;
  655.         q[0]=6;
  656.         q[1]=read_real640(); q[2]=read_real640();
  657.         q[3]=read_real640(); q[4]=read_real640();
  658.         q[5]=lx=read_real640(); q[6]=ly=read_real640();
  659.         put_block(q,curr_addr,7); curr_addr+=7;
  660.         update_bbox((int*)&t,q+1);
  661.         update_bbox((int*)&t,q+3);
  662.         update_bbox((int*)&t,q+5);
  663.         break;
  664.       case k_RMove:
  665.         dashOK=0;
  666.         q[0]=2; lx+=read_real640(); ly+=read_real640(); q[1]=lx; q[2]=ly;
  667.         put_block(q,curr_addr,3); curr_addr+=3;
  668.         update_bbox((int*)&t,q+1);
  669.         break;
  670.       case k_RLine:
  671.         dashOK=0;
  672.         q[0]=8; lx+=read_real640(); ly+=read_real640(); q[1]=lx; q[2]=ly;
  673.         put_block(q,curr_addr,3); curr_addr+=3;
  674.         update_bbox((int*)&t,q+1);
  675.         break;
  676.       case k_RCurve:
  677.         dashOK=0;
  678.         q[0]=6;
  679.         q[1]=lx+read_real640(); q[2]=ly+read_real640();
  680.         q[3]=lx+read_real640(); q[4]=ly+read_real640();
  681.         q[5]=lx+read_real640(); q[6]=ly+read_real640(); lx=q[5]; ly=q[6];
  682.         put_block(q,curr_addr,7); curr_addr+=7;
  683.         update_bbox((int*)&t,q+1);
  684.         update_bbox((int*)&t,q+3);
  685.         update_bbox((int*)&t,q+5);
  686.         break;
  687.       default: minor("Illegal keyword in path object");
  688.     }
  689.   }
  690.   put_word_at_end(0); /* end-of-path marker */
  691.   t.sz=(curr_addr-p)<<2;
  692.   if (!bbg) compute_path_bbox(p,qq,(int*)&t,curr_addr-p);
  693.   update_gbbox((int*)&t);
  694.   put_block((int*)&t,p,10);
  695. }
  696.  
  697. /* Get |update_bbox()| back again for text areas!
  698.  */
  699. #ifndef SLOPPY
  700. #undef update_bbox
  701. #endif
  702.  
  703. /* -------------------------- OBJECT: Sprite -------------------------- */
  704.  
  705.  
  706. /* Before we get started with the |do_sprite()| function, we need
  707.  * the following. |sprite_from_file(f,s)| searches the sprite file
  708.  * called |f| for a sprite called |s| and (if it finds one) reads it
  709.  * into the drawfile.
  710.  */
  711. static void sprite_from_file(char *file, char *sprite) {
  712.   FILE *f=fopen(file,"rb");
  713.   int ofs,n,t;
  714.   void *buf;
  715.   char z[13]; z[12]=0;
  716.   if (!f) { minor("Sprite file `%s' not found",file); return; }
  717.   fread(&n,4,1,f);    /* number of sprites */
  718.   if (n<=0) { minor("No sprites in file `%s'",file); fclose(f); return; }
  719.   fread(&ofs,4,1,f);    /* offset to first sprite */
  720.   ofs-=4;        /* correct for absence of size word in file */
  721.   do {
  722.     if (fseek(f,ofs,SEEK_SET)) {
  723.       minor("File `%s' doesn't seem to be a well-formed sprite file",file);
  724.       fclose(f); return;
  725.     }
  726.     fread(&t,4,1,f); ofs+=t;     /* offset to next sprite */
  727.     fread(z,1,12,f);    /* sprite name */
  728.   } while (!cistreq(sprite,z) && (--n>=0));
  729.   if (n<0) {
  730.     minor("Sprite `%s' not found in file `%s'",sprite,file);
  731.     fclose(f); return; }
  732.   fseek(f,-16,SEEK_CUR);
  733.   buf=xmalloc(t,"a sprite");
  734.   if (!fread(buf,t,1,f)) {
  735.     minor("Something went wrong reading sprite `%s' from file `%s'",
  736.           sprite,file);
  737.     fclose(f); return; }
  738.   put_block((int*)buf,curr_addr,(t+3)>>2); curr_addr+=(t+3)>>2;
  739.   xfree(buf);
  740.   fclose(f);
  741. }
  742.  
  743. typedef struct {
  744.   int ty; int sz;
  745.   int x0,y0,x1,y1;
  746. } spr;
  747.  
  748. static void do_sprite(void) {
  749.   spr t={5,6,0,0,1,1};
  750.   uint p=curr_addr;
  751.   int bbg=0;    /* bbox given? */
  752.   read_openbr();
  753.   curr_addr+=6;
  754.   while (1) {
  755.     if (get_x_token(0)) { minor("EOF or error in sprite object"); return; }
  756.     if (curr_token.type==t_real) {
  757.       put_word_at_end((uint)(curr_token.value.D));
  758.       continue; }
  759.     else if (curr_token.type==t_keyword) {
  760.       if (curr_token.value.I==k_BoundingBox) {
  761.         read_bbox(&t.x0);
  762.         bbg=1;
  763.       }
  764.       else if (curr_token.value.I==k_FromFile) {
  765.         char *fn,*sn;
  766.         fn=read_string(); sn=read_string();
  767.         sprite_from_file(fn,sn);
  768.       }
  769.       else minor("Illegal keyword in sprite object");
  770.     }
  771.     else {
  772.       if (curr_token.type!=t_closebr) minor("Illegal token in sprite object");
  773.       break;
  774.     }
  775.   }
  776.   if (!bbg) warn("Sprite object with no bbox");
  777.   t.sz=(curr_addr-p)<<2;
  778.   put_block((int*)&t,p,6);
  779. }
  780.  
  781.  
  782. /* -------------------------- OBJECT: Group -------------------------- */
  783.  
  784.  
  785. /* You should read the stuff about treatment of bboxes in Group and
  786.  * Tagged objects, in the section on bounding boxes.
  787.  */
  788.  
  789. typedef struct {
  790.   int ty; int sz;
  791.   int x0,y0,x1,y1;
  792.   char na[13];    /* 12 really */
  793. } grp;
  794.  
  795. static void do_group(void) {
  796.   grp t={6,36,0x7FFFFFFF,0x7FFFFFFF,0x80000000,0x80000000,"            "};
  797.   int *old_gbbox;
  798.   int c;
  799.   char *s;
  800.   uint p=curr_addr;
  801.   old_gbbox=global_bounding_box;
  802.   global_bounding_box=&t.x0;
  803.   read_openbr();
  804.   curr_addr+=9;
  805.   while (1) {
  806.     c=read_kwd_or_cbr(); if (c<0) break;
  807.     else if (c==k_Name) {
  808.       s=read_string();
  809.       c=strlen(s); if (c>12) {
  810.         warn("Overlong group name; truncating to 12c"); c=12; }
  811.       memcpy(t.na,s,c); }
  812.     else if (c==k_BoundingBox) {
  813.       read_bbox(&t.x0);
  814.       global_bounding_box=dummy_bbox; }
  815.     else do_object(c);
  816.   }
  817.   t.sz=(curr_addr-p)<<2;
  818.   put_block((int*)&t,p,9);
  819.   global_bounding_box=old_gbbox;
  820.   update_gbbox((int*)&t);
  821. }
  822.  
  823.  
  824. /* -------------------------- OBJECT: Tagged -------------------------- */
  825.  
  826.  
  827. /* You should read the stuff about treatment of bboxes in Group and
  828.  * Tagged objects, in the section on bounding boxes.
  829.  */
  830.  
  831. typedef struct {
  832.   int ty; int sz;
  833.   int x0,y0,x1,y1;
  834.   uint id;
  835. } tgd;
  836.  
  837. static void do_tagged(void) {
  838.   tgd t={7,28,0x7FFFFFFF,0x7FFFFFFF,0x80000000,0x80000000,0};
  839.   int *old_gbbox;
  840.   int c;
  841.   int had=0;    /* had the object yet? */
  842.   uint p=curr_addr;
  843.   old_gbbox=global_bounding_box;
  844.   global_bounding_box=&t.x0;
  845.   read_openbr();
  846.   curr_addr+=7;
  847.   while (1) {
  848.     c=read_kwd_or_cbr(); if (c<0) break;
  849.     else if (c==k_BoundingBox) {
  850.       read_bbox(&t.x0);
  851.       global_bounding_box=dummy_bbox; }
  852.     else if (c==k_Identifier) t.id=read_int();
  853.     else if (c==k_OtherData) {
  854.       if (had) put_word_at_end(read_int());
  855.       else {
  856.         minor("OtherData precedes object in Tagged object");
  857.         (void) read_int(); }
  858.     }
  859.     else {
  860.       if (had) minor("Only one object is allowed in a Tagged object");
  861.       do_object(c); had=1; }
  862.   }
  863.   if (!had) minor("No object in Tagged object");
  864.   t.sz=(curr_addr-p)<<2;
  865.   put_block((int*)&t,p,7);
  866.   global_bounding_box=old_gbbox;
  867.   update_gbbox((int*)&t);
  868. }
  869.  
  870.  
  871. /* -------------------------- OBJECT: TextArea -------------------------- */
  872.  
  873.  
  874. /* A text area object contains a number of columns. We don't know
  875.  * how many there are. This program imposes an arbitrary limit of
  876.  * 16, which should be plenty; if you do need more, just change the
  877.  * #define below.
  878.  */
  879. #define max_text_columns 16
  880.  
  881. typedef struct {
  882.   int ty; int sz;
  883.   int x0,y0,x1,y1;
  884. } tahdr;    /* this is used for columns as well as for the whole
  885.          * text area */
  886. typedef struct {
  887.   int zero;
  888.   int res1,res2;    /* also 0 */
  889.   uint fg,bg;
  890. } tar;
  891.  
  892. /* Evil Fact: |do_textarea| has to get at the actual input line,
  893.  * because of the syntax for the text in text areas. Believe me,
  894.  * the alternative is worse.
  895.  */
  896.  
  897. static void do_textarea(void) {
  898.   tahdr t[max_text_columns+1];    /* 1 for text area header */
  899.   tar u={0,0,0,0,0xFFFFFF00};
  900.   char d[1024]; /* arbitrary, integral #words, bigger than max_line_length */
  901.   int nc=0;
  902.   int c;
  903.   int had=0;    /* any text? */
  904.   int l,m=0;    /* chars in line,buffer */
  905.   uint p=curr_addr;
  906.   read_openbr();
  907.   t[0].ty=9;
  908.   t[0].x0=0x7FFFFFFF; t[0].y0=0x7FFFFFFF;
  909.   t[0].x1=0x80000000; t[0].y1=0x80000000;
  910.   while (1) {
  911.     c=read_kwd_or_cbr();
  912.     if (c<0) break; else switch(c) {
  913.       case k_Column:
  914.         if (had) {
  915.           minor("Misplaced text column");
  916.           (void)read_real640(); (void)read_real640();
  917.           (void)read_real640(); (void)read_real640();
  918.           break; }
  919.         if (++nc>max_text_columns) {
  920.           minor("Too many text columns");
  921.           (void)read_real640(); (void)read_real640();
  922.           (void)read_real640(); (void)read_real640();
  923.           break; }
  924.         t[nc].ty=10; t[nc].sz=24;
  925.         t[nc].x0=read_real640(); t[nc].y0=read_real640();
  926.         t[nc].x1=read_real640(); t[nc].y1=read_real640();
  927.         update_bbox((int*)t,&t[nc].x0);
  928.         update_bbox((int*)t,&t[nc].x1);
  929.         break;
  930.       case k_Colour:
  931.         u.fg=read_colour();
  932.         break;
  933.       case k_Background:
  934.         u.bg=read_colour();
  935.         break;
  936.       case k_Text:
  937.         if (!nc) minor("No columns in text object");
  938.         had=1;
  939.         curr_addr+=6*nc+11;
  940.         read_openbr();
  941.         while (1) {
  942.           if (get_line()) { minor("EOF or error in text area text"); return; }
  943.           c=0; l=0;
  944.           while(*line_tail&&*line_tail!='\n'&&isspace(*line_tail))
  945.             ++line_tail;
  946.           while (line_tail[l]) {
  947.             if (c) { if (!isspace(line_tail[l])) c=2; }
  948.             else {
  949.               if (line_tail[l]=='}') c=1;
  950.               else if (!isspace(line_tail[l])) c=2; }
  951.             ++l;
  952.           }
  953.           if (c==1) break;    /* just } and whitespace */
  954.           if (l+m<=1024) { memcpy(d+m,line_tail,l); m+=l; }
  955.           else {
  956.             c=1024-m; memcpy(d+m,line_tail,c);
  957.             put_block((int*)d,curr_addr,256); curr_addr+=256;
  958.             memcpy(d,line_tail+c,m=l-c);
  959.           }
  960.         }
  961.         if (m) {
  962.           while (m&3) d[m++]=0;
  963.           put_block((int*)d,curr_addr,m>>2); curr_addr+=m>>2;
  964.         }
  965.         break;
  966.       default: minor("Illegal keyword in text area object"); }
  967.   }
  968.   if (!had) minor("No text in text area");
  969.   read_closebr();
  970.   update_gbbox((int*)t);
  971.   t[0].sz=(curr_addr-p)<<2;
  972.   put_block((int*)t,p,6*(nc+1));
  973.   put_block((int*)&u,p+6*(nc+1),5);
  974. }
  975.  
  976.  
  977. /* -------------------------- OBJECT: Options -------------------------- */
  978.  
  979.  
  980. typedef struct {
  981.   int ty,sz;
  982.   int x0,y0,x1,y1;
  983.   int ps,pl;    /* paper size, options */
  984.   double gs; int gd,gt,ga,gn,gl,gu;    /* grid things */
  985.   int zm,zd,zl;    /* zoom things */
  986.   int tp;    /* toolbox present? */
  987.   int em;    /* entry mode */
  988.   int ub;    /* undo buffer size */
  989. } opt;
  990.  
  991. static void do_options(void) {
  992.   opt t={11,88, 0,0,0,0,
  993.          0x500,0x100, 1.0,2,0,0,0,0,1, 1,1,0, 1, 128, 5000};
  994.   int c;
  995.   read_openbr();
  996.   while (1) {
  997.     c=read_kwd_or_cbr(); if (c<0) break; else switch(c) {
  998.       case k_PaperSize: t.ps=(read_int()+1)<<8; break;
  999.       case k_Limits:
  1000.         read_openbr();
  1001.         while (1) {
  1002.           c=read_kwd_or_cbr(); if (c<0) break; else switch(c) {
  1003.             case k_Shown: t.pl|=1; break;
  1004.             case k_Landscape: t.pl|=16; break;
  1005.             case k_NonDefault: t.pl&=~256; break;
  1006.             default: minor("Illegal paper-limit option keyword"); }
  1007.         }
  1008.         break;
  1009.       case k_Grid:
  1010.         read_openbr();
  1011.         while (1) {
  1012.           c=read_kwd_or_cbr(); if (c<0) break; else switch(c) {
  1013.             case k_Spacing: t.gs=read_double(); break;
  1014.             case k_Divisions: t.gd=read_int(); break;
  1015.             case k_Isometric: t.gt=1; break;
  1016.             case k_AutoAdjust: t.ga=1; break;
  1017.             case k_Shown: t.gn=1; break;
  1018.             case k_Lock: t.gl=1; break;
  1019.             case k_Inches: t.gu=0; break;
  1020.             default: minor("Illegal grid option keyword"); }
  1021.         }
  1022.         break;
  1023.       case k_Zoom:
  1024.         read_openbr();
  1025.         while (1) {
  1026.           c=read_kwd_or_cbr(); if (c<0) break; else switch(c) {
  1027.             case k_Ratio: t.zm=read_int(); t.zd=read_int(); break;
  1028.             case k_Lock: t.zl=1; break;
  1029.             default: minor("Illegal zoom option keyword"); }
  1030.         }
  1031.         break;
  1032.       case k_NoToolbox: t.tp=0; break;
  1033.       case k_Mode:
  1034.         switch(read_kwd()) {
  1035.           case k_Line: t.em=1; break;
  1036.           case k_ClosedLine: t.em=2; break;
  1037.           case k_Curve: t.em=4; break;
  1038.           case k_ClosedCurve: t.em=8; break;
  1039.           case k_Rectangle: t.em=16; break;
  1040.           case k_Ellipse: t.em=32; break;
  1041.           case k_Text: t.em=64; break;
  1042.           case k_Select: t.em=128; break; }
  1043.         break;
  1044.       case k_UndoSize: t.ub=read_int(); break;
  1045.       default: minor("Illegal keyword in options object");
  1046.     }
  1047.   }
  1048.   put_block((int*)&t,curr_addr,22); curr_addr+=22;
  1049. }
  1050.  
  1051.  
  1052. /* -------------------------- OBJECT: XfText -------------------------- */
  1053.  
  1054.  
  1055. typedef struct {
  1056.   int ty,sz;
  1057.   int x0,y0,x1,y1;
  1058.   int a,b,c,d,xx,yy;
  1059.   int ff, fg,bg, st;
  1060.   int xs,ys,xx0,yy0;
  1061.   char tx[max_line_length];
  1062. } xft;
  1063.  
  1064. static void do_xftext(void) {
  1065.   xft t={12,80, 0,0,0,0, 65536,0,0,65536, 0,0,
  1066.          0, 0,0xffffff00, 0, 7680,7680, 0,0,""};
  1067.   char *s=0;
  1068.   int l;
  1069.   int c;
  1070.   int had=0;    /* matrix? */
  1071.   int bbg=0;
  1072.   int centred=0;
  1073.   int cx=0,cy=0;    /* coords of centre. =0 just to pacify compiler. */
  1074.   int virt=0;        /* virtual? */
  1075.   read_openbr();
  1076.   memcpy(&t.a,&text_prev.a,12*sizeof(int));
  1077.   while (1) {
  1078.     c=read_kwd_or_cbr(); if (c<0) break;
  1079.     else switch(c) {
  1080.       case k_BoundingBox:
  1081.         read_bbox(&t.x0);
  1082.         bbg=1; break;
  1083.       case k_Colour: t.fg=read_colour(); break;
  1084.       case k_Background: t.bg=read_colour(); break;
  1085.       case k_Style: t.st=read_int(); break;
  1086.       case k_Size: t.xs=read_real640();
  1087.                    t.ys=read_real640(); break;
  1088.       case k_StartAt: centred|=4;
  1089.                       t.xx0=read_real640();
  1090.                       t.yy0=read_real640(); break;
  1091.       case k_Text: s=read_string(); break;
  1092.       case k_Matrix:
  1093.         read_matrix(&t.a);
  1094.         had=1;
  1095.         break;
  1096.       case k_Kerned: t.ff|=1; break;
  1097.       case k_RightToLeft: t.ff|=2; break;
  1098.       case k_HCentreIn: centred|=1; /* fall through: */
  1099.       case k_CentreIn:
  1100.         centred|=2;
  1101.         cx=read_real640(); cy=read_real640();
  1102.         cx=(cx+read_real640())>>1;
  1103.         cy=(cy+read_real640())>>1;
  1104.         break;
  1105.       case k_HCentreOn:
  1106.         centred|=1;
  1107.         cx=(read_real640()+read_real640())>>1; /* order doesn't matter :-) */
  1108.         t.yy0=read_real640();
  1109.         break;
  1110.       case k_CentreAt:
  1111.         centred|=2;
  1112.         cx=read_real640(); cy=read_real640();
  1113.         break;
  1114.       case k_Virtual:
  1115.         virt=1;
  1116.         break;
  1117.       default: minor("Illegal keyword in transformed text object");
  1118.     }
  1119.   }
  1120.   memcpy(&text_prev.a,&t.a,12*sizeof(int));
  1121.   if (!s) { minor("No text in transformed text object"); s=""; }
  1122.   l=(strlen(s)>>2)+1;
  1123.   memcpy(t.tx,s,l<<2);
  1124.   t.sz=(l<<2)+80;
  1125.   if (t.st>255 || t.st && !fname[t.st]) {
  1126.     minor("Undeclared font number %d; replacing it with 0",t.st);
  1127.     t.st=0; }
  1128.   if (!bbg)
  1129.     compute_text_bbox(t.st,t.tx,t.xx0,t.yy0,t.xs,t.ys,t.ff,&t.x0,&t.a);
  1130.   if (centred&3) {
  1131.     int *bb=&t.x0;
  1132.     int q[4];
  1133.     int dx,dy;
  1134.     if (centred&4 && !(centred&1)) warn("StartAt is ignored for centred text");
  1135.     if (bbg) compute_text_bbox(t.st,t.tx,t.xx0,t.yy0,t.xs,t.ys,t.ff,bb=q,&t.a);
  1136.     dx=cx-((bb[0]+bb[2]+1)>>1); dy=cy-((bb[1]+bb[3]+1)>>1);
  1137.     t.xx0+=dx; if (!(centred&1)) t.yy0+=dy;
  1138.     if (!bbg) { t.x0+=dx; t.x1+=dx; if (!(centred&1)) { t.y0+=dy; t.y1+=dy; } }
  1139.   }
  1140.   update_gbbox((int*)&t);
  1141.   set_text_bbox_vars(&t.x0);
  1142.   if (!virt) {
  1143.     put_block((int *)&t,curr_addr,l+20);
  1144.     curr_addr+=l+20;
  1145.   }
  1146. }
  1147.  
  1148.  
  1149. /* -------------------------- OBJECT: XfSprite -------------------------- */
  1150.  
  1151.  
  1152. /* |sprite_from_file()| is defined in the "OBJECT: Sprite" section.
  1153.  */
  1154.  
  1155. typedef struct {
  1156.   int ty,sz;
  1157.   int x0,y0,x1,y1;
  1158.   int a,b,c,d,xx,yy;
  1159. } xfs;
  1160.  
  1161. static void do_xfsprite(void) {
  1162.   xfs t={13,12,0,0,1,1,65536,0,0,65536,0,0};
  1163.   uint p=curr_addr;
  1164.   int bbg=0,mxg=0;
  1165.   read_openbr();
  1166.   curr_addr+=12;
  1167.   while (1) {
  1168.     if (get_x_token(0)) {
  1169.       minor("EOF or error in transformed sprite object"); return; }
  1170.     if (curr_token.type==t_real) {
  1171.       put_word_at_end((uint)(curr_token.value.D));
  1172.       continue; }
  1173.     else if (curr_token.type==t_keyword) {
  1174.       if (curr_token.value.I==k_BoundingBox) {
  1175.         read_bbox(&t.x0);
  1176.         bbg=1;
  1177.       }
  1178.       else if (curr_token.value.I==k_Matrix) {
  1179.         read_matrix(&t.a);
  1180.         mxg=1;
  1181.       }
  1182.       else if (curr_token.value.I==k_FromFile) {
  1183.         char *fn,*sn;
  1184.         fn=read_string(); sn=read_string();
  1185.         sprite_from_file(fn,sn);
  1186.       }
  1187.       else minor("Illegal keyword in transformed sprite object");
  1188.     }
  1189.     else {
  1190.       if (curr_token.type!=t_closebr)
  1191.         minor("Illegal token in transformed sprite object");
  1192.       break;
  1193.     }
  1194.   }
  1195.   if (!bbg) warn("Transformed sprite object with no bbox");
  1196.   if (!mxg) warn("Transformed sprite object with no matrix");
  1197.   t.sz=(curr_addr-p)<<2;
  1198.   put_block((int*)&t,p,12);
  1199. }
  1200.  
  1201.  
  1202. /* -------------------------- OBJECT: JPEG -------------------------- */
  1203.  
  1204.  
  1205. /* JPEG object support is optional, because current versions of !Draw
  1206.  * don't understand JPEG objects.
  1207.  */
  1208. #ifndef NO_JPEG
  1209.  
  1210. /* Similar to |sprite_from_file()|, but easier:
  1211.  */
  1212. static int jpeg_from_file(char *name) {
  1213.   int l=file_size(name);
  1214.   void *buf;
  1215.   if (l<0) { minor("JPEG file `%s' not found",name); return 0; }
  1216.   buf=xmalloc(l,"the contents of a JPEG file");
  1217.   if (load_file(name,buf)<0) {
  1218.     minor("Something went wrong reading JPEG file `%s'",name);
  1219.     xfree(buf); return 0; }
  1220.   put_block((int*)buf,curr_addr,(l+3)>>2); curr_addr+=(l+3)>>2;
  1221.   xfree(buf);
  1222.   return l;
  1223. }
  1224.  
  1225. typedef struct {
  1226.   int ty,sz;
  1227.   int x0,y0,x1,y1;
  1228.   int xs,ys;
  1229.   int dx,dy;
  1230.   int a,b,c,d,xx,yy;
  1231.   int len;
  1232. } jpg;
  1233.  
  1234. static void do_jpeg(void) {
  1235.   jpg t={16,17, 0x7FFFFFFF,0x7FFFFFFF,0x80000000,0x80000000, 0,0, 90,90,
  1236.          65536,0,0,65536, 0,0, 0};
  1237.   int bbg=0,szg=0,lng=0;
  1238.   uint p=curr_addr;
  1239.   read_openbr();
  1240.   curr_addr+=17;
  1241.   while (1) {
  1242.     if (get_x_token(0)) { minor("EOF or error in JPEG object"); return;}
  1243.     if (curr_token.type==t_real) {
  1244.       put_word_at_end((unsigned int)(curr_token.value.D));
  1245.       continue; }
  1246.     else if (curr_token.type==t_closebr) break;
  1247.     else if (curr_token.type!=t_keyword) {
  1248.       minor("Illegal token in JPEG object");
  1249.       continue; }
  1250.     else switch(curr_token.value.I) {
  1251.       case k_BoundingBox: read_bbox(&t.x0); bbg=1; break;
  1252.       case k_Size:        t.xs=read_real640(); t.ys=read_real640();
  1253.                           szg=1; break;
  1254.       case k_Matrix:      read_matrix(&t.a); break;
  1255.       case k_FromFile:    t.len=jpeg_from_file(read_string()); lng=1; break;
  1256.       case k_DPI:         t.dx=read_int(); t.dy=read_int(); break;
  1257.       case k_Length:      t.len=read_int(); lng=1; break;
  1258.       default: minor("Illegal keyword in JPEG object");
  1259.     }
  1260.   }
  1261.   if (!bbg) warn("JPEG object with no bbox");
  1262.   if (!szg) {
  1263.     warn("JPEG object with size unspecified");
  1264.     t.xs=t.x1-t.x0;
  1265.     t.ys=t.y1-t.y0; }
  1266.   if (!lng) warn("JPEG object with length unspecified");
  1267.   t.sz=(curr_addr-p)<<2;
  1268.   put_block((int*)&t,p,17);
  1269. }
  1270.  
  1271. #endif
  1272.  
  1273.  
  1274. /* -------------------------- Drawfile header -------------------------- */
  1275.  
  1276.  
  1277. static int hgbb[4];    /* header global bounding box */
  1278. static int hgbbp=0;    /* is there one? */
  1279.  
  1280. static void do_header(void) {
  1281.   int c;
  1282.   char id[13]="            ";
  1283.   char *s;
  1284.   read_openbr();
  1285.   while (1) {
  1286.     c=read_kwd_or_cbr(); if (c<0) break; else switch(c) {
  1287.       case k_Version:
  1288.         put_word(read_int(),1);
  1289.         put_word(read_int(),2);
  1290.         break;
  1291.       case k_Creator:
  1292.         s=read_string(); c=strlen(s);
  1293.         if (c>12) { warn("Over-long Creator string; truncating to 12c");
  1294.                     c=12; }
  1295.         memcpy((int*)id,s,c);
  1296.         put_block((int*)id,3,3);
  1297.         break;
  1298.       case k_BoundingBox:
  1299.         read_bbox(hgbb);
  1300.         hgbbp=1;
  1301.         break;
  1302.       default: minor("Illegal keyword in header pseudo-object");
  1303.     }
  1304.   }
  1305. }
  1306.  
  1307.  
  1308. /* -------------------------- main() -------------------------- */
  1309.  
  1310.  
  1311. /* Well, first:
  1312.  */
  1313. static void do_object(int k) {
  1314.   switch(k) {
  1315.     case k_FontTable: do_ftable();    break;
  1316.     case k_Text:      do_text();      break;
  1317.     case k_Path:      do_path();      break;
  1318.     case k_Sprite:    do_sprite();    break;
  1319.     case k_Group:     do_group();     break;
  1320.     case k_Tagged:    do_tagged();    break;
  1321.     case k_TextArea:  do_textarea();  break;
  1322.     case k_Options:   do_options();   break;
  1323.     case k_XfText:    do_xftext();    break;
  1324.     case k_XfSprite:  do_xfsprite();  break;
  1325. #ifndef NO_JPEG
  1326.     case k_JPEG:      do_jpeg();      break;
  1327. #endif
  1328.     default: minor("Unknown object type");
  1329.   }
  1330. }
  1331.  
  1332. int main(int ac, char *av[]) {
  1333.   int i;
  1334.   char q[300];
  1335.   char *output_file_name=0;
  1336.   int minus_v=0;
  1337.   prog_name=*av;
  1338.  
  1339. #ifdef EXPRO
  1340. exprofle_init("$.tmp.MDFPROF");
  1341. #endif
  1342.  
  1343.   while (++av,--ac) {
  1344.     if (!strcmp(*av,"-v")) {
  1345.       fprintf(stderr,"This is mkdrawf, version " VERSION_STRING ".\n");
  1346.       minus_v=1;
  1347.       continue; }
  1348.     if (!strcmp(*av,"-e")) { no_filenames=1; continue; }
  1349. #ifdef TAGS
  1350.     if (!strcmp(*av,"-t")) {
  1351.       if (!(++av,--ac)) goto bad_syntax;
  1352.       tag_open(*av);
  1353.       continue; }
  1354. #endif
  1355.     if (!input_file_name) { input_file_name=*av; continue; }
  1356.     if (!output_file_name) { output_file_name=*av; continue; }
  1357. bad_syntax:
  1358.     fprintf(stderr,"Usage: %s <infile> <outfile>"
  1359. #ifdef TAGS
  1360.       " [-t <tagfile>]"
  1361. #endif
  1362.       "\n",prog_name);
  1363.     return 0;
  1364.   }
  1365.   if (!output_file_name) {
  1366.     if (minus_v && !input_file_name) return 0;
  1367.     goto bad_syntax; }
  1368.   if (!strcmp(input_file_name,"-")) {
  1369.     input_file_name="<stdin>"; input_file=stdin; }
  1370.   else {
  1371.     input_file=fopen(input_file_name,"r");
  1372.     if (!input_file)
  1373.       error("I couldn't open the input file `%s'",input_file_name);
  1374.   }
  1375.   output_file=fopen(output_file_name,"wb");
  1376.   if (!output_file)
  1377.     error("I couldn't open the output file `%s'",output_file_name);
  1378.   init_global_hash();
  1379.   init_vars();
  1380.   for (i=0;i<256;++i) fname[i]=0;
  1381.   for (i=0;i<max_n_buffers;++i) buffers[i]=0;
  1382.   srand(mono_time());
  1383.   put_word(0x77617244,0);    /* Draw */
  1384.   put_word(201,1); put_word(0,2);    /* version number (of file format) */
  1385.   put_block((int*)"mkdrawf2    ",3,3);
  1386.   curr_addr=10;
  1387.   /* Main loop of program */
  1388.   while (1) {
  1389.     if (get_x_token(0)) break;    /* EOF */
  1390.     if (curr_token.type!=t_keyword) {
  1391.       minor("I expected to find an object type here");
  1392.       continue; }
  1393.     if (curr_token.value.I==k_Header) do_header();
  1394.     else do_object(curr_token.value.I);
  1395.   }
  1396.   if (hgbbp) global_bounding_box=hgbb;
  1397.   put_block(global_bounding_box,6,4);
  1398.   if (had_ftable==2) {
  1399.     /* Implicit font table, not empty */
  1400.     char *ftable=xmalloc(ft_size=(ft_size+11)&~3,"font table");
  1401.     char *ftp=ftable+8;
  1402.     fwrite(buffers[0],4,10,output_file);
  1403.     ((int*)ftable)[0]=0;
  1404.     ((int*)ftable)[1]=ft_size;
  1405.     for (i=1;i<256;++i) if (fname[i]) {
  1406.       char *s=fname[i];
  1407.       *ftp++=i;
  1408.       while((*ftp++=*s++)!=0) ;
  1409.     }
  1410.     while (ftp-ftable<ft_size) *ftp++=0;
  1411.     fwrite(ftable,ft_size,1,output_file);
  1412.     xfree(ftable);
  1413.     /* write out remainder of buffer 0 */
  1414.     if (n_buffers==1)
  1415.       fwrite(buffers[0]+10,4,curr_addr-10,output_file);
  1416.     else
  1417.       fwrite(buffers[0]+10,4,buffer_size-10,output_file);
  1418.   }
  1419.   else {
  1420.     /* write out buffer 0 */
  1421.     if (n_buffers==1)
  1422.       fwrite(buffers[0],4,curr_addr,output_file);
  1423.     else
  1424.       fwrite(buffers[0],4,buffer_size,output_file);
  1425.   }
  1426.   /* now buffer 0 done */
  1427.   if (n_buffers>1) {
  1428.     curr_addr-=buffer_size;
  1429.     for (i=1;i<n_buffers-1;++i) {
  1430.       fwrite(buffers[i],4,buffer_size,output_file);
  1431.       curr_addr-=buffer_size;
  1432.     }
  1433.     if (curr_addr) {
  1434.       if (curr_addr>buffer_size)
  1435.         minor("This can't happen! curr_addr>buffer_sizr");
  1436.       fwrite(buffers[n_buffers-1],4,curr_addr,output_file);
  1437.     }
  1438.   }
  1439.   fclose(output_file);
  1440.   sprintf(q,"SetType %s DrawFile",output_file_name);
  1441.   system(q);
  1442.   return final_report();
  1443. }
  1444.