home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / lout2.lzh / LOUT2 / z37.c < prev    next >
Text File  |  1994-01-23  |  35KB  |  831 lines

  1. /*@z37.c:Font Service:Declarations@*******************************************/
  2. /*                                                                           */
  3. /*  LOUT: A HIGH-LEVEL LANGUAGE FOR DOCUMENT FORMATTING (VERSION 2.05)       */
  4. /*  COPYRIGHT (C) 1993 Jeffrey H. Kingston                                   */
  5. /*                                                                           */
  6. /*  Jeffrey H. Kingston (jeff@cs.su.oz.au)                                   */
  7. /*  Basser Department of Computer Science                                    */
  8. /*  The University of Sydney 2006                                            */
  9. /*  AUSTRALIA                                                                */
  10. /*                                                                           */
  11. /*  This program is free software; you can redistribute it and/or modify     */
  12. /*  it under the terms of the GNU General Public License as published by     */
  13. /*  the Free Software Foundation; either version 1, or (at your option)      */
  14. /*  any later version.                                                       */
  15. /*                                                                           */
  16. /*  This program is distributed in the hope that it will be useful,          */
  17. /*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
  18. /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
  19. /*  GNU General Public License for more details.                             */
  20. /*                                                                           */
  21. /*  You should have received a copy of the GNU General Public License        */
  22. /*  along with this program; if not, write to the Free Software              */
  23. /*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                */
  24. /*                                                                           */
  25. /*  FILE:         z37.c                                                      */
  26. /*  MODULE:       Font Service                                               */
  27. /*  EXTERNS:      FontInit(), FontDefine(), FontChange(), FontWordSize(),    */
  28. /*                FontSize(), FontHalfXHeight(), FontEncoding(),             */
  29. /*                FontEncoding(), FontFamilyAndFace(), FontNeeded()          */
  30. /*                                                                           */
  31. /*  This module implements fonts, using encoding vectors and Adobe font      */
  32. /*  metrics files (.AFM files, version 2).                                   */
  33. /*                                                                           */
  34. /*****************************************************************************/
  35. #include "externs"
  36. #define DEFAULT_XHEIGHT 500    /* the default XHeight if font has none      */
  37. #define    NO_FONT          0    /* the not-a-font font number                */
  38. #define    MAX_CHARS    256    /* maximum number of chars in a font         */
  39. #define SZ_DFT           1000    /* default lout size is 50p                  */
  40.  
  41. struct metrics {
  42.   LENGTH up;
  43.   LENGTH down;
  44.   LENGTH left;
  45.   LENGTH right;
  46. };
  47.  
  48. static struct metrics    *size_table[MAX_FONT];    /* metrics of sized fonts    */
  49. static FULL_CHAR    *lig_table[MAX_FONT];    /* ligatures                 */
  50. static OBJECT        font_table[MAX_FONT];    /* record of sized fonts     */
  51. static OBJECT        font_root;        /* root of tree of fonts     */
  52. static FONT_NUM        fontcount;        /* number of sized fonts     */
  53. static int        font_seqnum;        /* unique number for a font  */
  54.  
  55.  
  56. /*@::FontInit(), FontDebug()@*************************************************/
  57. /*                                                                           */
  58. /*  FontInit()                                                               */
  59. /*                                                                           */
  60. /*  Initialise this module.                                                  */
  61. /*                                                                           */
  62. /*****************************************************************************/
  63.  
  64. FontInit()
  65. { debug0(DFT, D, "FontInit()");
  66.   fontcount    = 0;
  67.   font_root    = New(ACAT);
  68.   font_seqnum    = 0;
  69.   debug0(DFT, D, "FontInit returning.");
  70. }
  71.  
  72.  
  73. /*****************************************************************************/
  74. /*                                                                           */
  75. /*  FontDebug()                                                                 */
  76. /*                                                                           */
  77. /*  Print out font tree.                                                     */
  78. /*                                                                           */
  79. /*****************************************************************************/
  80.  
  81. #if DEBUG_ON
  82. static FontDebug()
  83. { OBJECT family, face, filename, link, flink;  int i;
  84.   assert( font_root != nil && type(font_root)==ACAT, "FontDebug: font_root!" );
  85.   for( link = Down(font_root);  link != font_root;  link = NextDown(link) )
  86.   { Child(family, link);
  87.     assert( is_word(type(family)), "FontDebug: family!" );
  88.     fprintf(stderr, "family %s:\n", string(family));
  89.     for( flink = Down(family);  flink != family;  flink = NextDown(flink) )
  90.     { Child(face, flink);
  91.       assert( is_word(type(face)), "FontDebug: face!" );
  92.       fprintf(stderr, "   face %s in file ", string(face));
  93.       assert( Down(face) != face, "FontDebug: Down(face)!");
  94.       Child(filename, Down(face));
  95.       assert( is_word(type(filename)), "FontDebug: filename!" );
  96.       fprintf(stderr, "%s\n", string(filename));
  97.     }
  98.   }
  99.   for( i = 1;  i <= fontcount;  i++ )
  100.     fprintf(stderr, "  font_table[%d] = %s\n", i, EchoObject(font_table[i]));
  101. } /* end FontDebug */
  102. #endif
  103.  
  104.  
  105. /*@::FontDefine()@************************************************************/
  106. /*                                                                           */
  107. /*  FontDefine(family, face, inside)                                         */
  108. /*                                                                           */
  109. /*  Insert a font defined by fontdef <family> <face> { <inside> } into the   */
  110. /*  font tree; <inside> ::= <fontname> <AFMfilename> <CEVfilename> <recode>  */
  111. /*                                                                           */
  112. /*****************************************************************************/
  113.  
  114. FontDefine(family, face, inside)
  115. OBJECT family, face, inside;
  116. { OBJECT font_name, AFMfilename, CEVfilename, recode;
  117.   OBJECT short_name, link, y, val[4]; int i;
  118.   debug3(DFT, D, "FontDefine( %s, %s, %s )", string(family),
  119.     string(face), EchoObject(inside));
  120.  
  121.   /* extract font_name, AFMfilename, CEVfilename, and recode */
  122.   if( type(inside) != ACAT )
  123.   { Error(WARN, &fpos(inside), "fontdef is not a sequence of words");
  124.     DisposeObject(inside);  return;
  125.   }
  126.   for( i = 0;  Down(inside) != inside && i != 4;  i++ )
  127.   { Child(val[i], Down(inside));
  128.     DeleteLink(Up(val[i]));
  129.     if( type(val[i]) == GAP_OBJ )  DisposeObject(val[i--]);
  130.     else if( !is_word(type(val[i])) )
  131.     { Error(WARN, &fpos(val[i]), "fontdef contains a non-word");
  132.       DisposeObject(inside);  return;
  133.     }
  134.   }
  135.   if( Down(inside) != inside || i != 4 )
  136.   { Error(WARN, &fpos(inside), "fontdef does not contain exactly four words");
  137.     DisposeObject(inside);  return;
  138.   }
  139.   font_name = val[0];    AFMfilename = val[1];
  140.   CEVfilename = val[2];  recode = val[3];
  141.  
  142.   /* insert family into font tree if not already present */
  143.   for( link = Down(font_root);  link != font_root;  link = NextDown(link) )
  144.   { Child(y, link);
  145.     if( StringEqual(string(y), string(family)) )
  146.     { Dispose(family);  family = y; break; }
  147.   }
  148.   if( link == font_root )  Link(font_root, family);
  149.  
  150.   /* insert face into family, or error if already present */
  151.   for( link = Down(family);  link != family;  link = NextDown(link) )
  152.   { Child(y, link);
  153.     if( StringEqual(string(y), string(face)) )
  154.     { Error(WARN, &fpos(face), "font %s %s already defined at%s",
  155.     string(family), string(face), EchoFilePos(&fpos(y)));
  156.       debug0(DFT, D, "FontDefine returning: font already defined");
  157.       Dispose(face);
  158.       return;
  159.     }
  160.   }
  161.   Link(family, face);
  162.  
  163.   /* add AFMfilename as first size of font, and PostScript name as its child */
  164.   Link(face, AFMfilename);
  165.   short_name = MakeWordTwo(WORD, AsciiToFull("fnt"), StringInt(++font_seqnum),
  166.     no_fpos);
  167.   Link(AFMfilename, short_name);  Link(AFMfilename, font_name);
  168.  
  169.   /* load encoding vector */
  170.   if( StringEqual(string(recode), STR_FONT_RECODE) )
  171.   { font_recoded(face) = TRUE;
  172.     font_encoding(AFMfilename) = EvLoad(CEVfilename, TRUE);
  173.   }
  174.   else if( StringEqual(string(recode), STR_FONT_NORECODE) )
  175.   { font_recoded(face) = FALSE;
  176.     font_encoding(AFMfilename) = EvLoad(CEVfilename, FALSE);
  177.   }
  178.   else Error(FATAL, &fpos(recode), "expecting either Recode or NoRecode here");
  179.   debug0(DFT, D, "FontDefine returning.");
  180. } /* end FontDefine */
  181.  
  182.  
  183. /*@::ReadFont()@**************************************************************/
  184. /*                                                                           */
  185. /*  static ReadFont(face, err)                                               */
  186. /*                                                                           */
  187. /*  Read in a font file.  Object err is used only for error reporting.       */
  188. /*                                                                           */
  189. /*****************************************************************************/
  190.  
  191. static ReadFont(face, err)
  192. OBJECT face, err;
  193. { OBJECT filename, fontname;
  194.   FULL_CHAR buff[MAX_LINE], command[MAX_LINE], ch;
  195.   int wx, llx, lly, urx, ury, xheight2, i, lnum, ligtop;
  196.   BOOLEAN xhfound, wxfound, bfound;
  197.   FILE_NUM fnum;  FILE *fp;
  198.   struct metrics *fnt;
  199.   FULL_CHAR *lig, ligchar;
  200.   OBJECT x;
  201.   char *malloc();
  202.   assert( is_word(type(face)), "ReadFont: !is_word(type(face))!" );
  203.   debug1(DFT, DD, "ReadFont( %s, err )", string(face));
  204.  
  205.   /* get a new font number for this font */
  206.   if( ++fontcount >= MAX_FONT )  Error(FATAL, &fpos(err),
  207.     "too many different fonts and sizes (max is %d)", MAX_FONT - 1);
  208.  
  209.   /* open the Adobe font metrics (AFM) file of the font */
  210.   assert( Down(face) != face, "ReadFont: filename missing!" );
  211.   Child(filename, Down(face));
  212.   assert( Down(filename) != filename, "ReadFont: filename child missing!" );
  213.   fnum = DefineFile(string(filename), STR_EMPTY, &fpos(filename),
  214.     FONT_FILE, FONT_PATH);
  215.   fp = OpenFile(fnum, FALSE, FALSE);
  216.   if( fp == NULL )
  217.     Error(FATAL, &fpos(filename), "cannot open font file %s", FileName(fnum));
  218.  
  219.   /* check that the AFM file begins, as it should, with "StartFontMetrics" */
  220.   if( StringFGets(buff, MAX_LINE, fp) == NULL ||
  221.     sscanf( (char *) buff, "%s", command) != 1 ||
  222.     !StringEqual(command, "StartFontMetrics")  )
  223.   { debug1(DFT, D, "first line of AFM file:%s", buff);
  224.     debug1(DFT, D, "command:%s", command);
  225.     Error(FATAL, &fpos(filename),
  226.       "font file %s does not begin with StartFontMetrics", FileName(fnum));
  227.   }
  228.  
  229.   /* initialise font metrics and ligature tables for the new font */
  230.   fnt = (struct metrics *) malloc(MAX_CHARS * sizeof(struct metrics));
  231.   if( fnt == (struct metrics *) NULL )  Error(FATAL, &fpos(err),
  232.     "run out of memory reading font file %s", FileName(fnum));
  233.   lig = (FULL_CHAR *) malloc(2*MAX_CHARS*sizeof(FULL_CHAR));
  234.   if( lig == (FULL_CHAR *) NULL )  Error(FATAL, &fpos(err),
  235.     "run out of memory reading font file %s", FileName(fnum));
  236.   for( i = 0;  i < MAX_CHARS;  i++ )  lig[i] = 1;    /* i.e. char unknown */
  237.   ligtop = MAX_CHARS+2;        /* must avoid ligtop - MAX_CHARS == 0 or 1 */
  238.  
  239.   /* read font metrics file */
  240.   xhfound = FALSE;  fontname = nil;  lnum = 1;
  241.   while ( ( StringFGets(buff, MAX_LINE, fp) ) != NULL )
  242.   {
  243.     lnum++;
  244.     sscanf( (char *) buff, "%s", command);
  245.     switch( command[0] )
  246.     {
  247.  
  248.       case 'X':
  249.  
  250.     if( StringEqual(command, AsciiToFull("XHeight")) ) 
  251.     { if( xhfound )
  252.       { Error(FATAL, &fpos(filename),
  253.           "XHeight found twice in font file (line %d)", lnum);
  254.       }
  255.       sscanf( (char *) buff, "XHeight %d", &xheight2);
  256.       xheight2 = xheight2 / 2;
  257.       xhfound = TRUE;
  258.     }
  259.     break;
  260.  
  261.  
  262.       case 'F':
  263.  
  264.     if( StringEqual(command, AsciiToFull("FontName")) )
  265.     { if( fontname != nil )
  266.       { Error(FATAL, &fpos(filename),
  267.           "FontName found twice in font file %s (line %d)",
  268.           FileName(fnum), lnum);
  269.       }
  270.       sscanf( (char *) buff, "FontName %s", command);
  271.       if( StringEqual(command, STR_EMPTY) )
  272.       { Error(FATAL, &fpos(filename),
  273.           "FontName empty in font file %s (line %d)",
  274.           FileName(fnum), lnum);
  275.       }
  276.       Child(x, LastDown(filename));
  277.       if( !StringEqual(command, string(x)) )
  278.       Error(FATAL, &fpos(filename),
  279.         "FontName in AFM file (%s) and in fontdef (%s) disagree",
  280.         command, string(x));
  281.       fontname = MakeWord(WORD, command, &fpos(filename));
  282.     }
  283.     break;
  284.  
  285.  
  286.       case 'S':
  287.  
  288.     if( !StringEqual(command, AsciiToFull("StartCharMetrics")) )
  289.       continue;
  290.     if( fontname == nil )  Error(FATAL, &fpos(filename),
  291.       "FontName missing in file %s", FileName(fnum));
  292.     if( !xhfound )  xheight2 = DEFAULT_XHEIGHT / 2;
  293.     while( StringFGets(buff, MAX_LINE, fp) != NULL &&
  294.            !StringBeginsWith(buff, AsciiToFull("EndCharMetrics")) )
  295.     {
  296.       /* read one line containing metric info for one character */
  297.       debug1(DFT, DD, "ReadFont reading %s", buff);
  298.       lnum++;  ch = '\0';  
  299.       wxfound = bfound = FALSE;
  300.       i = 0;  while( buff[i] == ' ' )  i++;
  301.       while( buff[i] != '\n' )
  302.       {
  303.           debug2(DFT, DD, "  ch = %d, &buff[i] = %s", ch, &buff[i]);
  304.           sscanf( (char *) &buff[i], "%s", command);
  305.           if( StringEqual(command, "N") )
  306.           { sscanf( (char *) &buff[i], "N %s", command);
  307.         ch = EvRetrieve(command, font_encoding(filename));
  308.           }
  309.           else if( StringEqual(command, "WX") )
  310.           {    sscanf( (char *) &buff[i], "WX %d", &wx);
  311.         wxfound = TRUE;
  312.           }
  313.           else if( StringEqual(command, "B") )
  314.           { sscanf( (char *) &buff[i], "B %d %d %d %d",
  315.           &llx, &lly, &urx, &ury);
  316.         bfound = TRUE;
  317.           }
  318.           else if( StringEqual(command, "L") && ch != '\0' )
  319.           { if( lig[ch] == 1 )  lig[ch] = ligtop - MAX_CHARS;
  320.         lig[ligtop++] = ch;
  321.         i++;  /* skip L */
  322.         while( buff[i] == ' ' )  i++;
  323.         while( buff[i] != ';' && buff[i] != '\n' )
  324.         { sscanf( (char *) &buff[i], "%s", command);
  325.           ligchar = EvRetrieve(command, font_encoding(filename));
  326.           if( ligchar != '\0' )  lig[ligtop++] = ligchar;
  327.           else
  328.           { Error(WARN, &fpos(filename),
  329.             "ignoring ligature character %s in font file %s (line %d%s",
  330.             command, FileName(fnum), lnum, ") as it is not encoded");
  331.             lig[ch] = 1;
  332.           }
  333.           if( ligtop > 2*MAX_CHARS - 5 )  Error(FATAL, &fpos(filename),
  334.             "too many ligature characters in font file %s (line %d)",
  335.             FileName(fnum), lnum);
  336.           while( buff[i] != ' ' && buff[i] != ';' )  i++;
  337.           while( buff[i] == ' ' ) i++;
  338.         }
  339.         lig[ligtop++] = '\0';
  340.           }
  341.           while( buff[i] != ';' && buff[i] != '\n' )  i++;
  342.           if( buff[i] == ';' )
  343.           { i++;  while( buff[i] == ' ' ) i++;
  344.           }
  345.       }
  346.       if( ch > '\0' )
  347.       { 
  348.         if( !wxfound )
  349.         { Error(FATAL, &fpos(filename),
  350.             "WX missing in font file %s (line %d)", FileName(fnum), lnum);
  351.         }
  352.         if( !bfound )
  353.         { Error(FATAL, &fpos(filename),
  354.             "B missing in font file %s (line %d)", FileName(fnum), lnum);
  355.         }
  356.         if( lig[ch] == 1 )  lig[ch] = 0;    /* set to known if unknown */
  357.         else if( lig[ch] > 1 )        /* add '\0' to end of ligs */
  358.           lig[ligtop++] = '\0';
  359.         fnt[ch].left  = llx;
  360.         fnt[ch].down  = lly - xheight2;
  361.         fnt[ch].right = wx;
  362.         fnt[ch].up    = ury - xheight2;
  363.         debug5(DFT, DD, "  fnt[%c] = (%d,%d,%d,%d)", ch, fnt[ch].left,
  364.           fnt[ch].down, fnt[ch].right, fnt[ch].up);
  365.       }
  366.     }
  367.  
  368.     /* make a new font record and insert into font tree */
  369.     font_num(fontname) = fontcount;
  370.     font_size(fontname) = SZ_DFT;
  371.     font_xheight2(fontname) = xheight2;
  372.     font_encoding(fontname) = font_encoding(filename);
  373.     ch = EvRetrieve(STR_PS_SPACENAME, font_encoding(fontname));
  374.     font_spacewidth(fontname) = ch == '\0' ? 0 : fnt[ch].right;
  375.     font_table[fontcount] = fontname;
  376.     size_table[fontcount] = fnt;
  377.     lig_table[fontcount] = lig;
  378.     Link(face, fontname);
  379.  
  380.     /* close file, debug and exit */
  381.     fclose(fp);
  382.     debug4(DFT, D, "ReadFont returning: %d, name %s, fs %d, xh2 %d",
  383.         fontcount, string(fontname), font_size(fontname), xheight2);
  384.     return;
  385.     break;
  386.  
  387.  
  388.       default:
  389.  
  390.     break;
  391.  
  392.     }
  393.   }
  394.   Error(FATAL, &fpos(filename),
  395.     "StartCharMetrics missing from font file %s", FileName(fnum));
  396. } /* end ReadFont */
  397.  
  398.  
  399. /*@::FontChange()@************************************************************/
  400. /*                                                                           */
  401. /*  FontChange(style, x)                                                     */
  402. /*                                                                           */
  403. /*  Returns an internal font number which is the current font changed        */
  404. /*  according to word object x.  e.g. if current font is Roman 12p and x is  */
  405. /*  "-3p", then FontChange returns the internal font number of Roman 9p.     */
  406. /*                                                                           */
  407. /*****************************************************************************/
  408.  
  409. FontChange(style, x)
  410. STYLE *style;  OBJECT x;
  411. { /* register */ int i;
  412.   OBJECT par[3], family, face, fsize, y, link, new, old, tmpf;
  413.   GAP gp;  LENGTH flen;  int num, c;  unsigned inc;
  414.   struct metrics *newfnt, *oldfnt;  FULL_CHAR *lig;  char *malloc();
  415.   debug2(DFT, D, "FontChange( %s, %s )", EchoStyle(style), EchoObject(x));
  416.   assert( font(*style) <= fontcount, "FontChange: fontcount!");
  417.   /* ifdebug(DFT, DD, FontDebug()); */
  418.  
  419.   /* set par[0..num-1] to the 1, 2 or 3 parameters of the font operator */
  420.   num = 0;
  421.   if( is_word(type(x)) )  par[num++] = x; 
  422.   else if( type(x) == ACAT )
  423.   { for( link = Down(x);  link != x;  link = NextDown(link) )
  424.     { Child(y, link);
  425.       debug1(DFT, DD, "  pars examining y = %s", EchoObject(y));
  426.       if( type(y) == GAP_OBJ )  continue;
  427.       if( !is_word(type(y)) || num >= 3 )
  428.       {    Error(WARN, &fpos(x), "error in left parameter of %s", KW_FONT);
  429.     debug0(DFT, D, "FontChange returning: ACAT children");
  430.     return;
  431.       }
  432.       par[num++] = y;
  433.     }
  434.   }
  435.   else
  436.   { Error(WARN, &fpos(x), "error in left parameter of %s", KW_FONT);
  437.     debug0(DFT, D, "FontChange returning: wrong type");
  438.     return;
  439.   }
  440.   debug1(DFT, DD, " found pars, num = %d", num);
  441.  
  442.   /* extract fsize parameter, if any */
  443.   assert( num >= 1 && num <= 3, "FontChange: num!" );
  444.   c = string(par[num-1])[0];
  445.   if( c == CH_INCGAP || c == CH_DECGAP || decimaldigit(c) )
  446.   { fsize = par[num-1];  num--;
  447.   }
  448.   else fsize = nil;
  449.  
  450.   /* check for initial font case: must have family, face, and size */
  451.   if( font(*style) == NO_FONT && (fsize == nil || num < 2) )
  452.     Error(FATAL, &fpos(x), "initial font must have family, face and size");
  453.  
  454.   /* get font family */
  455.   if( num == 2 )
  456.   {
  457.     /* par[0] contains a new family name */
  458.     for( link = Down(font_root);  link != font_root;  link = NextDown(link) )
  459.     { Child(family, link);
  460.       if( StringEqual(string(family), string(par[0])) )  break;
  461.     }
  462.     if( link == font_root )
  463.     { Error(WARN,&fpos(par[0]), "font family %s not defined", string(par[0]));
  464.       return;
  465.     }
  466.   }
  467.   else
  468.   { /* preserve current family */
  469.     assert( Up(font_table[font(*style)]) != font_table[font(*style)],
  470.       "FontChange: Up(font_table[font(*style)]) !" );
  471.     Parent(face, Up(font_table[font(*style)]));
  472.     assert( is_word(type(face)), "FontChange: type(face)!" );
  473.     assert( Up(face) != face, "FontChange: Up(face)!" );
  474.     Parent(family, Up(face));
  475.     assert( is_word(type(family)), "FontChange: type(family)!" );
  476.   }
  477.  
  478.   /* get font face */
  479.   if( num != 0 )
  480.   {
  481.     /* par[num-1] contains a new face name */
  482.     for( link = Down(family);  link != family;  link = NextDown(link) )
  483.     { Child(face, link);
  484.       if( StringEqual(string(face), string(par[num-1])) )  break;
  485.     }
  486.     if( link == family )
  487.     {
  488.       /* missing face name; first check whether a family name was intended */
  489.       for( link = Down(font_root);  link != font_root;  link = NextDown(link) )
  490.       {    Child(tmpf, link);
  491.     if( StringEqual(string(tmpf), string(par[num-1])) )  break;
  492.       }
  493.       if( font_root == Down(font_root) )
  494.       {    Error(FATAL, &fpos(par[num-1]), "there are no fonts");
  495.       }
  496.       else if( link != font_root )
  497.       {    Error(WARN, &fpos(par[num-1]),
  498.         "font family name %s must be accompanied by a face name",
  499.         string(par[num-1]));
  500.       }
  501.       else Error(WARN, &fpos(par[num-1]),
  502.         "font face name %s not defined in font family %s",
  503.         string(par[num-1]), string(family));
  504.       return;
  505.     }
  506.   }
  507.   else
  508.   {
  509.     /* preserve current face name */
  510.     Parent(face, Up(font_table[font(*style)]));
  511.     assert( is_word(type(face)), "FontChange: type(face)!" );
  512.     assert( Up(face) != face, "FontChange: Up(face)!" );
  513.   }
  514.  
  515.   /* get font size */
  516.   if( fsize == nil )  flen = font_size(font_table[font(*style)]);
  517.   else 
  518.   { GetGap(fsize, style, &gp, &inc);
  519.     if( inc == GAP_ABS )  flen = width(gp);
  520.     else if( font(*style) == NO_FONT )
  521.       Error(FATAL, &fpos(fsize), "no font encloses this %s", string(fsize));
  522.     else if( inc==GAP_INC )  flen = font_size(font_table[font(*style)])+width(gp);
  523.     else if( inc==GAP_DEC )  flen = font_size(font_table[font(*style)])-width(gp);
  524.     else Error(INTERN, &fpos(x), "GetGap returned inc = %d!", inc);
  525.   }
  526.  
  527.   if( flen <= 0 )
  528.   { Error(WARN, &fpos(fsize), "%s %s ignored: result is not positive",
  529.       string(fsize), KW_FONT);
  530.     return;
  531.   }
  532.  
  533.   /* if the font file has not been read before, read it now */
  534.   assert( Down(face) != face && type(Down(face)) == LINK, "FontChange: dn!" );
  535.   if( Down(face) == LastDown(face) )  ReadFont(face, x);
  536.   assert( Down(face) != LastDown(face), "FontChange: after ReadFont!" );
  537.  
  538.   /* search fonts of face for desired size; return if already present */
  539.   for( link = NextDown(Down(face));  link != face;  link = NextDown(link) )
  540.   { Child(fsize, link);
  541.     if( font_size(fsize) == flen )
  542.     { font(*style) = font_num(fsize);
  543.       SetGap(space_gap(*style), FALSE, TRUE, FIXED_UNIT, EDGE_MODE,
  544.             font_spacewidth(fsize));
  545.       debug2(DFT, D,"FontChange returning (old) %d (XHeight2 = %d)",
  546.             font(*style), font_xheight2(font_table[font(*style)]));
  547.       return;
  548.     }
  549.   }
  550.  
  551.   /* now need to rescale the font; first create a sized font record */
  552.   if( ++fontcount >= MAX_FONT )  Error(FATAL, &fpos(x),
  553.     "too many different fonts and sizes (max is %d)", MAX_FONT - 1);
  554.   assert( Down(face) != face && NextDown(Down(face)) != face, "FontChange!!" );
  555.   Child(old, NextDown(Down(face)));
  556.   assert( is_word(type(old)), "FontChange: old!" );
  557.   new = MakeWord(WORD, string(old), no_fpos);
  558.   Link(face, new);
  559.   font_size(new)        = flen;
  560.   font_xheight2(new)    = font_xheight2(old) * font_size(new) / font_size(old);
  561.   font_encoding(new)    = font_encoding(old);
  562.   font_spacewidth(new)    = font_spacewidth(old) * font_size(new)/font_size(old);
  563.   font_num(new)         = fontcount;
  564.   font_table[fontcount] = new;
  565.   size_table[fontcount] =
  566.     (struct metrics *) malloc(MAX_CHARS * sizeof(struct metrics));
  567.   if( size_table[fontcount] == (struct metrics *) NULL )
  568.     Error(FATAL, &fpos(x), "run out of memory when changing font or font size");
  569.   lig_table[fontcount]  = lig = lig_table[font_num(old)];
  570.  
  571.   /* scale old font to new size */
  572.   newfnt = size_table[font_num(new)];
  573.   oldfnt = size_table[font_num(old)];
  574.   for( i = 0;  i < MAX_CHARS;  i++ )  if( lig[i] != 1 )
  575.   { newfnt[i].left  = (oldfnt[i].left  * font_size(new)) / font_size(old);
  576.     newfnt[i].right = (oldfnt[i].right * font_size(new)) / font_size(old);
  577.     newfnt[i].down  = (oldfnt[i].down  * font_size(new)) / font_size(old);
  578.     newfnt[i].up    = (oldfnt[i].up    * font_size(new)) / font_size(old);
  579.   }
  580.  
  581.   /* return new font number and exit */
  582.   font(*style) = fontcount;
  583.   SetGap(space_gap(*style), FALSE, TRUE, FIXED_UNIT, EDGE_MODE,
  584.     font_spacewidth(new));
  585.   debug2(DFT, D,"FontChange returning (scaled) %d (XHeight2 = %d)",
  586.     font(*style), font_xheight2(font_table[font(*style)]));
  587.   /* FontDebug(); */
  588. } /* end FontChange */
  589.  
  590.  
  591. /*@::FontWordSize()@**********************************************************/
  592. /*                                                                           */
  593. /*  FontWordSize(x)                                                          */
  594. /*                                                                           */
  595. /*  Calculate the horizontal and vertical size of WORD or QWORD x, replacing */
  596. /*  ligature sequences by ligature characters wherever they occur.           */
  597. /*                                                                           */
  598. /*****************************************************************************/
  599.  
  600. FontWordSize(x)
  601. OBJECT x;
  602. { FULL_CHAR *p, *q, *a, *b, *lig;  OBJECT tmp;
  603.   int r, u, d; struct metrics *fnt;
  604.   debug2(DFT, D, "FontWordSize( %s ), font = %d", string(x), word_font(x));
  605.   assert( is_word(type(x)), "FontWordSize: !is_word(type(x))!" );
  606.  
  607.   p = q = string(x);
  608.   if( *p )
  609.   { if ( word_font(x) < 1 || word_font(x) > fontcount )
  610.       Error(FATAL, &fpos(x), "no current font at word %s", string(x));
  611.     fnt = size_table[word_font(x)];
  612.     lig = lig_table[word_font(x)];
  613.     d = u = r = 0;
  614.     do
  615.     { 
  616.       /* check for missing glyph (lig[] == 1) or ligatures (lig[] > 1) */
  617.       if( lig[*q = *p++] )
  618.       {
  619.     if( lig[*q] == 1 )
  620.     { tmp = MakeWord(QWORD, STR_SPACE, &fpos(x));
  621.       string(tmp)[0] = *q;
  622.       Error(WARN, &fpos(x),
  623.         "character %s left out (it has no glyph in font %s)",
  624.          StringQuotedWord(tmp), FontFamilyAndFace(word_font(x)));
  625.       Dispose(tmp);
  626.       continue;
  627.     }
  628.     else
  629.     { a = &lig[ lig[*(p-1)] + MAX_CHARS ];
  630.       while( *a++ == *(p-1) )
  631.       { b = p;
  632.         while( *a == *b && *(a+1) != '\0' && *b != '\0' )  a++, b++;
  633.         if( *(a+1) == '\0' )
  634.         { *q = *a;
  635.           p = b;
  636.           break;
  637.         }
  638.         else
  639.         { while( *++a );
  640.           a++;
  641.         }
  642.       }
  643.     }
  644.       }
  645.  
  646.       /* accumulate size of *q */
  647.       if( fnt[*q].up   > u )  u = fnt[*q].up;
  648.       if( fnt[*q].down < d )  d = fnt[*q].down;
  649.       r += fnt[*q++].right;
  650.     } while( *p );
  651.     *q++ = '\0';
  652.     back(x, COL) = 0; fwd(x, COL)  = r;
  653.     back(x, ROW) = u; fwd(x, ROW)  = -d;
  654.   } 
  655.   else back(x, COL) = fwd(x, COL) = back(x, ROW) = fwd(x, ROW) = 0;
  656.   debug4(DFT, D, "FontWordSize returning %hd %hd %hd %hd",
  657.       back(x, COL), fwd(x, COL), back(x, ROW), fwd(x, ROW));
  658. } /* end FontWordSize */
  659.  
  660.  
  661. /*@::FontSize(), FontHalfXHeight(), FontEncoding(), FontName()@***************/
  662. /*                                                                           */
  663. /*  LENGTH FontSize(fnum, x)                                                 */
  664. /*                                                                           */
  665. /*  Return the size of this font.  x is for error messages only.             */
  666. /*                                                                           */
  667. /*****************************************************************************/
  668.  
  669. LENGTH FontSize(fnum, x)
  670. FONT_NUM fnum;  OBJECT x;
  671. { debug1(DFT, D, "FontSize( %d )", fnum);
  672.   assert( fnum <= fontcount, "FontSize!" );
  673.   if( fnum <= 0 )  Error(FATAL, &fpos(x), "no current font at this point");
  674.   debug1(DFT, D, "FontSize returning %d", font_size(font_table[fnum]));
  675.   return font_size(font_table[fnum]);
  676. } /* end FontSize */
  677.  
  678.  
  679. /*****************************************************************************/
  680. /*                                                                           */
  681. /*  LENGTH FontHalfXHeight(fnum)                                             */
  682. /*                                                                           */
  683. /*  Return the xheight2 value of this font.                                  */
  684. /*                                                                           */
  685. /*****************************************************************************/
  686.  
  687. LENGTH FontHalfXHeight(fnum)
  688. FONT_NUM fnum;
  689. { debug1(DFT, D, "FontHalfXHeight( %d )", fnum);
  690.   assert( fnum <= fontcount, "FontHalfXHeight!" );
  691.   debug1(DFT,D,"FontHalfXHeight returning %d", font_xheight2(font_table[fnum]));
  692.   return font_xheight2(font_table[fnum]);
  693. } /* end FontSize */
  694.  
  695.  
  696. /*****************************************************************************/
  697. /*                                                                           */
  698. /*  ENCODING FontEncoding(fnum)                                              */
  699. /*                                                                           */
  700. /*  Return the encoding of this font.                                        */
  701. /*                                                                           */
  702. /*****************************************************************************/
  703.  
  704. ENCODING FontEncoding(fnum)
  705. FONT_NUM fnum;
  706. { debug1(DFT, D, "FontEncoding( %d )", fnum);
  707.   assert( fnum <= fontcount, "FontSize!" );
  708.   debug1(DFT, D, "FontEncoding returning %d", font_encoding(font_table[fnum]));
  709.   return font_encoding(font_table[fnum]);
  710. } /* end FontSize */
  711.  
  712.  
  713. /*****************************************************************************/
  714. /*                                                                           */
  715. /*  FULL_CHAR *FontName(fnum)                                                */
  716. /*                                                                           */
  717. /*  Return the short PostScript name of this font.                           */
  718. /*                                                                           */
  719. /*****************************************************************************/
  720.  
  721. FULL_CHAR *FontName(fnum)
  722. FONT_NUM fnum;
  723. { OBJECT face, AFMfilename, short_name;
  724.   debug1(DFT, D, "FontName( %d )", fnum);
  725.   assert( fnum <= fontcount, "FontName!" );
  726.   Parent(face, Up(font_table[fnum]));
  727.   Child(AFMfilename, Down(face));
  728.   Child(short_name, Down(AFMfilename));
  729.   assert( is_word(type(short_name)), "FontName: short_name!" );
  730.   debug1(DFT, D, "FontName returning %s", string(short_name));
  731.   return string(short_name);
  732. } /* end FontSize */
  733.  
  734.  
  735. /*@::FontFamilyAndFace(), FontPrintAll()@*************************************/
  736. /*                                                                           */
  737. /*  FULL_CHAR *FontFamilyAndFace(fnum)                                       */
  738. /*                                                                           */
  739. /*  Return a static string of the current font family and face.              */
  740. /*                                                                           */
  741. /*****************************************************************************/
  742.  
  743. FULL_CHAR *FontFamilyAndFace(fnum)
  744. FONT_NUM fnum;
  745. { OBJECT face, family; static FULL_CHAR buff[80];
  746.   debug1(DFT, D, "FontFamilyAndFace( %d )", fnum);
  747.   assert( fnum <= fontcount, "FontName!" );
  748.   Parent(face, Up(font_table[fnum]));
  749.   Parent(family, Up(face));
  750.   if( StringLength(string(family)) + StringLength(string(face)) + 1 > 80 )
  751.     Error(FATAL, no_fpos, "family and face names %s %s are too long",
  752.       string(family), string(face));
  753.   StringCopy(buff, string(family));
  754.   StringCat(buff, STR_SPACE);
  755.   StringCat(buff, string(face));
  756.   debug1(DFT, D, "FontName returning %s", buff);
  757.   return buff;
  758. } /* end FontFamilyAndFace */
  759.  
  760.  
  761. /*****************************************************************************/
  762. /*                                                                           */
  763. /*  FontPrintAll(fp)                                                            */
  764. /*                                                                           */
  765. /*  Print all font encoding commands on output file fp                       */
  766. /*                                                                           */
  767. /*****************************************************************************/
  768.  
  769. FontPrintAll(fp)
  770. FILE *fp;
  771. { OBJECT family, face, AFMfilename, short_name, ps_name, link, flink;
  772.   assert( font_root != nil && type(font_root)==ACAT, "FontDebug: font_root!" );
  773.   debug0(DFT, DD, "FontPrintAll(fp)");
  774.   for( link = Down(font_root);  link != font_root;  link = NextDown(link) )
  775.   { Child(family, link);
  776.     assert( is_word(type(family)), "FontPrintAll: family!" );
  777.     for( flink = Down(family);  flink != family;  flink = NextDown(flink) )
  778.     { Child(face, flink);
  779.       assert( is_word(type(face)), "FontPrintAll: face!" );
  780.       assert( Down(face) != face, "FontDebug: Down(face)!");
  781.       Child(AFMfilename, Down(face));
  782.       assert( is_word(type(AFMfilename)), "FontPrintAll: filename!" );
  783.       assert( Down(AFMfilename) != AFMfilename, "FontPrintAll: 1!" );
  784.       assert( LastDown(AFMfilename) != Down(AFMfilename), "FontPrintAll: 2!" );
  785.       Child(short_name, Down(AFMfilename));
  786.       assert( is_word(type(short_name)), "FontPrintAll: short_name!" );
  787.       Child(ps_name, LastDown(AFMfilename));
  788.       assert( is_word(type(ps_name)), "FontPrintAll: ps_name!" );
  789.       if( font_recoded(face) )
  790.       { fprintf(fp, "/%s%s %s /%s LoutRecode\n",
  791.       string(ps_name), string(short_name),
  792.       EvName(font_encoding(AFMfilename)), string(ps_name));
  793.         fprintf(fp, "/%s { /%s%s LoutFont } def\n", string(short_name),
  794.       string(ps_name), string(short_name));
  795.       }
  796.       else fprintf(fp, "/%s { /%s LoutFont } def\n", string(short_name),
  797.       string(ps_name));
  798.     }
  799.   }
  800.   fputs("\n", fp);
  801.   debug0(DFT, DD, "FontPrintAll returning.");
  802. } /* end FontPrintAll */
  803.  
  804.  
  805. /*@::FontNeeded()@************************************************************/
  806. /*                                                                           */
  807. /*  OBJECT FontNeeded(out_fp);                                               */
  808. /*                                                                           */
  809. /*  Writes font needed resources onto file out_fp.  Returns TRUE if none.    */
  810. /*                                                                           */
  811. /*****************************************************************************/
  812.  
  813. BOOLEAN FontNeeded(out_fp)
  814. FILE *out_fp;
  815. { BOOLEAN first_need = TRUE;
  816.   OBJECT link, flink, family, face, x;
  817.   for( link = Down(font_root); link != font_root; link = NextDown(link) )
  818.   { Child(family, link);
  819.     for( flink = Down(family);  flink != family;  flink = NextDown(flink) )
  820.     { Child(face, flink);
  821.       if( LastDown(face) != Down(face) )
  822.       { Child(x, LastDown(face));
  823.         fprintf(out_fp, "%s font %s\n",
  824.             first_need ? "%%DocumentNeededResources:" : "%%+", string(x));
  825.         first_need = FALSE;
  826.       }
  827.     }
  828.   }
  829.   return first_need;
  830. } /* end FontNeeded */
  831.