1 /*@z37.c:Font Service:Declarations@*******************************************/
2 /*                                                                           */
3 /*  THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.39)                       */
4 /*  COPYRIGHT (C) 1991, 2008 Jeffrey H. Kingston                             */
5 /*                                                                           */
6 /*  Jeffrey H. Kingston (jeff@it.usyd.edu.au)                                */
7 /*  School of Information Technologies                                       */
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 3, 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., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA   */
24 /*                                                                           */
25 /*  FILE:         z37.c                                                      */
26 /*  MODULE:       Font Service                                               */
27 /*  EXTERNS:      FontInit(), FontDefine(), FontChange(), FontWordSize(),    */
28 /*                FontSize(), FontHalfXHeight(), FontEncoding(),             */
29 /*                FontMapping(), 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.h"
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 SZ_DFT	       1000	/* default lout size is 50p                  */
39 #define	INIT_FINFO_SIZE	100	/* initial number of sized fonts set aside   */
40 
41 /*****************************************************************************/
42 /*                                                                           */
43 /* These definitions have been moved to "externs.h" since z24.c needs them:  */
44 /*                                                                           */
45 /*  struct metrics {							     */
46 /*    FULL_LENGTH up;							     */
47 /*    FULL_LENGTH down;							     */
48 /*    FULL_LENGTH left;							     */
49 /*    FULL_LENGTH right;						     */
50 /*    FULL_LENGTH last_adjust;						     */
51 /*  };							     		     */
52 /*                                                                           */
53 /*  typedef struc composite_rec {                                            */
54 /*    FULL_CHAR char_code;                                                   */
55 /*    FULL_LENGTH x_offset;                                                  */
56 /*    FULL_LENGTH y_offset;                                                  */
57 /*  } COMPOSITE;                                                             */
58 /*                                                                           */
59 /*  typedef struct font_rec {						     */
60 /*    struct metrics	*size_table;		   metrics of sized fonts    */
61 /*    FULL_CHAR		*lig_table;		   ligatures                 */
62 /*    unsigned short	*composite;		   non-zero means composite  */
63 /*    COMPOSITE		*cmp_table;		   composites to build       */
64 /*    int               cmp_top;                   length of cmp_table       */
65 /*    OBJECT		font_table;		   record of sized fonts     */
66 /*    OBJECT            original_face;             face object of font       */
67 /*    FULL_LENGTH	underline_pos;             position of underline     */
68 /*    FULL_LENGTH	underline_thick;           thickness of underline    */
69 /*    unsigned short	*kern_table;		   first kerning chars       */
70 /*    FULL_CHAR		*kern_chars;		   second kerning chars      */
71 /*    unsigned char	*kern_value;		   points into kern_lengths  */
72 /*    FULL_LENGTH	*kern_sizes;		   sizes of kernings         */
73 /*    FULL_LENGTH	bbox_lly;		   lly of font bbox          */
74 /*    FULL_LENGTH	bbox_ury;		   ury of font bbox          */
75 /*  } FONT_INFO;							     */
76 /*                                                                           */
77 /*****************************************************************************/
78 
79 /*****************************************************************************/
80 /*                                                                           */
81 /*  Private data structures of this module                                   */
82 /*                                                                           */
83 /*            +++++++++++++++++++++++++++                                    */
84 /*            +                         +                                    */
85 /*  root ->   +  ACAT                   +                                    */
86 /*            +                         +                                    */
87 /*            +                         +                                    */
88 /*            +++++++++++++++++++++++++++                                    */
89 /*                    |                                 font families...     */
90 /*                    |                                                      */
91 /*              +-----+-----------------------------------------------+ ...  */
92 /*              |                                                     |      */
93 /*              |                                                     |      */
94 /*            +++++++++++++++++++++++++++                                    */
95 /*            +                         +                                    */
96 /*  family -> + WORD                    +                                    */
97 /*            +   string (family name)  +                                    */
98 /*            +                         +                                    */
99 /*            +++++++++++++++++++++++++++                                    */
100 /*                    |                           faces of this family...    */
101 /*                    |                                                      */
102 /*              +-----+-----------------------------------------------+ ...  */
103 /*              |                                                     |      */
104 /*              |                                                     |      */
105 /*            +++++++++++++++++++++++++++++++++                              */
106 /*            +                               +                              */
107 /*  face ->   + WORD                          +                              */
108 /*            +   string (face name)          +                              */
109 /*            +   font_recoded                +                              */
110 /*            +   font_mapping                +                              */
111 /*            +   font_page                   +                              */
112 /*            +                               +                              */
113 /*            +++++++++++++++++++++++++++++++++                              */
114 /*                |                                 size records...          */
115 /*                |                                                          */
116 /*     +----------+---------+--------------------+-----------------------+   */
117 /*     |                    |                    |                       |   */
118 /*     |                    |                    |                       |   */
119 /*   +++++++++++++++++++  +++++++++++++++++++  +++++++++++++++++++++         */
120 /*   +                 +  +                 +  +                   +         */
121 /*   + WORD            +  + WORD            +  + WORD              +         */
122 /*   +   string (font  +  +   string (AFM   +  +   string (short   +         */
123 /*   +     name)       +  +     file name)  +  +     font name)    +         */
124 /*   +                 +  +                 +  +   font_num        +         */
125 /*   +++++++++++++++++++  +++++++++++++++++++  +   font_size       +         */
126 /*                          |                  +   font_xheight2   +         */
127 /*                          |                  +   font_recoded    +         */
128 /*                  ++++++++++++++++++++       +   font_mapping    +         */
129 /*                  +                  +       +   font_spacewidth +         */
130 /*       (optional) + WORD             +       +   font_bbox_lly   +         */
131 /*                  +   string (extra  +       +   font_bbox_ury   +         */
132 /*                  +   AFM file name) +       +                   +         */
133 /*                  +                  +       +++++++++++++++++++++         */
134 /*                  ++++++++++++++++++++                                     */
135 /*                                                                           */
136 /*****************************************************************************/
137 
138 	OBJECT		FontDefSym;		/* symtab entry for @FontDef */
139 	int		font_curr_page;		/* current page number       */
140 	FONT_INFO	*finfo;			/* all the font table info   */
141 static	int		finfo_size;		/* current finfo array size  */
142 static	OBJECT		font_root;		/* root of tree of fonts     */
143 static	OBJECT		font_used;		/* fonts used on this page   */
144 static	FONT_NUM	font_count;		/* number of sized fonts     */
145 static	int		font_seqnum;		/* unique number for a font  */
146 static	OBJECT		fd_tag;			/* @FontDef @Tag entry       */
147 static	OBJECT		fd_family;		/* @FontDef @Family entry    */
148 static	OBJECT		fd_face;		/* @FontDef @Face entry      */
149 static	OBJECT		fd_name;		/* @FontDef @Name entry      */
150 static	OBJECT		fd_metrics;		/* @FontDef @Metrics entry   */
151 static	OBJECT		fd_extra_metrics;	/* @FontDef @ExtraMetrics    */
152 static	OBJECT		fd_mapping;		/* @FontDef @Mapping entry   */
153 static	OBJECT		fd_recode;		/* @FontDef @Recode entry    */
154 
155 
156 /*@::FontInit(), FontDebug()@*************************************************/
157 /*                                                                           */
158 /*  FontInit()                                                               */
159 /*                                                                           */
160 /*  Initialise this module.                                                  */
161 /*                                                                           */
162 /*****************************************************************************/
163 
load(FULL_CHAR * name,unsigned dtype,OBJECT encl,BOOLEAN compulsory)164 static OBJECT load(FULL_CHAR *name, unsigned dtype, OBJECT encl, BOOLEAN compulsory)
165 { OBJECT res;
166   res = InsertSym(name, dtype, no_fpos, DEFAULT_PREC, FALSE, FALSE, 0, encl,
167     MakeWord(WORD, STR_EMPTY, no_fpos));
168   if( dtype == NPAR ) visible(res) = TRUE;
169   if( compulsory )
170   { has_compulsory(encl)++;
171     is_compulsory(res) = TRUE;
172   }
173   return res;
174 }
175 
FontInit(void)176 void FontInit(void)
177 {
178   debug0(DFT, D, "FontInit()");
179   font_curr_page = 1;
180   font_count	= 0;
181   New(font_root, ACAT);
182   New(font_used, ACAT);
183   font_seqnum	= 0;
184   finfo         = (FONT_INFO *) malloc(INIT_FINFO_SIZE * sizeof(FONT_INFO));
185   finfo_size    = INIT_FINFO_SIZE;
186   ifdebug(DMA, D,
187     DebugRegisterUsage(MEM_FONTS, 1, INIT_FINFO_SIZE * sizeof(FONT_INFO)));
188 
189   /* set up FontDefSym */
190   FontDefSym	   = load(KW_FONTDEF,       LOCAL, StartSym,   FALSE);
191   fd_tag	   = load(KW_TAG,           NPAR,  FontDefSym, TRUE);
192   fd_family	   = load(KW_FAMILY,        NPAR,  FontDefSym, TRUE);
193   fd_face	   = load(KW_FACE,          NPAR,  FontDefSym, TRUE);
194   fd_name	   = load(KW_NAME,          NPAR,  FontDefSym, TRUE);
195   fd_metrics	   = load(KW_METRICS,       NPAR,  FontDefSym, TRUE);
196   fd_extra_metrics = load(KW_EXTRA_METRICS, NPAR,  FontDefSym, FALSE);
197   fd_mapping	   = load(KW_MAPPING,       NPAR,  FontDefSym, TRUE);
198   fd_recode	   = load(KW_RECODE,        NPAR,  FontDefSym, FALSE);
199 
200   debug0(DFT, D, "FontInit returning.");
201 }
202 
203 
204 /*****************************************************************************/
205 /*                                                                           */
206 /*  FontDebug()                        	                                     */
207 /*                                                                           */
208 /*  Print out font tree (not currectly used).                                */
209 /*                                                                           */
210 /*****************************************************************************/
211 
212 #if DEBUG_ON
FontDebug(void)213 static void FontDebug(void)
214 { OBJECT family, face, link, flink, zlink, z;  int i;
215   assert(font_root!=nilobj && type(font_root)==ACAT, "FontDebug: font_root!");
216   for( link = Down(font_root);  link != font_root;  link = NextDown(link) )
217   { Child(family, link);
218     assert( is_word(type(family)), "FontDebug: family!" );
219     debug1(DFS, D, "family %s:", string(family));
220     for( flink = Down(family);  flink != family;  flink = NextDown(flink) )
221     { Child(face, flink);
222       assert( is_word(type(face)), "FontDebug: face!" );
223       debug1(DFS, D, "   face %s:", string(face));
224       for( zlink = Down(face);  zlink != face;  zlink = NextDown(zlink) )
225       { Child(z, zlink);
226 	if( is_word(type(z)) )
227 	{ debug2(DFS, D, "      %s%s", string(z), Down(z) != z ? " child" : "");
228 	}
229 	else
230 	{ debug1(DFS, D, "      %s", Image(type(z)));
231 	}
232       }
233     }
234   }
235   for( i = 1;  i <= font_count;  i++ )
236     fprintf(stderr, "  finfo[%d].font_table = %s%s", i,
237       EchoObject(finfo[i].font_table), STR_NEWLINE);
238 } /* end FontDebug */
239 
240 
241 /*****************************************************************************/
242 /*                                                                           */
243 /*  DebugKernTable(fnum)                                                     */
244 /*                                                                           */
245 /*  Print debug output of kern table for font fnum.                          */
246 /*                                                                           */
247 /*****************************************************************************/
248 
DebugKernTable(FONT_NUM fnum)249 static void DebugKernTable(FONT_NUM fnum)
250 { int i, j;
251   unsigned short *kt = finfo[fnum].kern_table;
252   FULL_CHAR      *kc = finfo[fnum].kern_chars;
253   unsigned char  *kv = finfo[fnum].kern_value;
254   FULL_LENGTH    *ks = finfo[fnum].kern_sizes;
255   debug1(DFT, DD, "DebugKernTable(%d)", fnum);
256   for( i = 0;  i < MAX_CHARS;  i++ )
257   { if( kt[i] != 0 )
258     { debug1(DFT, DD, "kt[%d]:", i);
259       for( j = kt[i];  kc[j] != '\0';  j++ )
260       { debug3(DFT, DD, "KPX %c %c %d", i, kc[j], ks[kv[j]]);
261       }
262     }
263   }
264   debug1(DFT, DD, "DebugKernTable(%d) returning", fnum);
265 } /* DebugKernTable */
266 #endif
267 
268 
269 /*****************************************************************************/
270 /*                                                                           */
271 /*  ReadCharMetrics(face, fixed_pitch, xheight2,lig,ligtop,fnum,fnt,lnum,fp) */
272 /*                                                                           */
273 /*  Read a sequence of character metrics lines.  The font record is          */
274 /*  face, its ligatures are lig[0..ligtop], font number fnum, metrics fnt.   */
275 /*  The line number is lnum; input is to be read from file fp.               */
276 /*                                                                           */
277 /*****************************************************************************/
278 
ReadCharMetrics(OBJECT face,BOOLEAN fixed_pitch,int xheight2,FULL_CHAR * lig,int * ligtop,FILE_NUM fnum,struct metrics * fnt,int * lnum,FILE * fp)279 static void ReadCharMetrics(OBJECT face, BOOLEAN fixed_pitch, int xheight2,
280   FULL_CHAR *lig, int *ligtop, FILE_NUM fnum, struct metrics *fnt,
281   int *lnum, FILE *fp)
282 { FULL_CHAR buff[MAX_BUFF], command[MAX_BUFF], ch, ligchar;
283   int prev_ligtop, prev_lig, i, wx = 0, llx = 0, lly = 0, urx = 0, ury = 0;
284   float fl_wx, fl_llx, fl_lly, fl_urx, fl_ury;
285   BOOLEAN wxfound, bfound;
286   OBJECT AFMfilename;
287 
288   Child(AFMfilename, NextDown(Down(face)));
289   while( ReadOneLine(fp, buff, MAX_BUFF) != 0 &&
290 	 !StringBeginsWith(buff, AsciiToFull("EndCharMetrics")) &&
291 	 !StringBeginsWith(buff, AsciiToFull("EndExtraCharMetrics")) )
292   {
293     /* read one line containing metric info for one character */
294     debug1(DFT, DD, "  ReadCharMetrics: %s", buff);
295     (*lnum)++;  ch = '\0';
296     wxfound = bfound = FALSE;
297     i = 0;  while( buff[i] == ' ' )  i++;
298     while( buff[i] != '\0' )
299     {
300       debug2(DFT, DDD, "  ch = %d, &buff[i] = %s", ch, &buff[i]);
301       sscanf( (char *) &buff[i], "%s", command);
302       if( StringEqual(command, "N") )
303       { sscanf( (char *) &buff[i], "N %s", command);
304 	ch = MapCharEncoding(command, font_mapping(face));
305       }
306       else if( StringEqual(command, "WX") )
307       {	sscanf( (char *) &buff[i], "WX %f", &fl_wx);
308 	wx = fl_wx;
309 	wxfound = TRUE;
310       }
311       else if( StringEqual(command, "B") )
312       { sscanf( (char *) &buff[i], "B %f %f %f %f",
313 	  &fl_llx, &fl_lly, &fl_urx, &fl_ury);
314 	llx = fl_llx;
315 	lly = fl_lly;
316 	urx = fl_urx;
317 	ury = fl_ury;
318 	bfound = TRUE;
319       }
320       else if( StringEqual(command, "L") &&
321 	BackEnd->uses_font_metrics && ch != '\0' )
322       {
323 	prev_ligtop = *ligtop;
324 	prev_lig = lig[ch];
325 	if( lig[ch] == 1 )  lig[ch] = (*ligtop) - MAX_CHARS;
326 	lig[(*ligtop)++] = ch;
327 	i++;  /* skip L */
328 	while( buff[i] == ' ' )  i++;
329 	while( buff[i] != ';' && buff[i] != '\0' )
330 	{ sscanf( (char *) &buff[i], "%s", command);
331 	  ligchar = MapCharEncoding(command, font_mapping(face));
332 	  if( ligchar != '\0' )  lig[(*ligtop)++] = ligchar;
333 	  else
334 	  { Error(37, 1, "ignoring unencoded ligature character %s in font file %s (line %d)",
335 	      WARN, &fpos(AFMfilename), command, FileName(fnum), *lnum);
336 	    lig[ch] = prev_lig;  /* patch by Ludovic Courtes, added v. 3.31 */
337 	    *ligtop = prev_ligtop;
338 	  }
339 	  if( *ligtop > 2*MAX_CHARS - 5 )
340 	    Error(37, 2, "too many ligature characters in font file %s (line %d)",
341 	    FATAL, &fpos(AFMfilename), FileName(fnum), *lnum);
342 	  while( buff[i] != ' ' && buff[i] != ';' )  i++;
343 	  while( buff[i] == ' ' ) i++;
344 	}
345 	if( *ligtop != prev_ligtop )  /* this test by Ludovic Courtes, 3.31 */
346 	  lig[(*ligtop)++] = '\0';
347       }
348       while( buff[i] != ';' && buff[i] != '\0' )  i++;
349       if( buff[i] == ';' )
350       { i++;  while( buff[i] == ' ' ) i++;
351       }
352     }
353     if( ch > '\0' )
354     {
355       if( !wxfound )
356       { Error(37, 3, "WX missing in font file %s (line %d)",
357 	  FATAL, &fpos(AFMfilename), FileName(fnum), *lnum);
358       }
359       if( !bfound )
360       { Error(37, 4, "B missing in font file %s (line %d)",
361 	  FATAL, &fpos(AFMfilename), FileName(fnum), *lnum);
362       }
363       if( lig[ch] == 1 )  lig[ch] = 0;	/* set to known if unknown */
364       else if( lig[ch] > 1 )		/* add '\0' to end of ligs */
365 	lig[(*ligtop)++] = '\0';
366       if( BackEnd->uses_font_metrics )
367       {
368 	fnt[ch].left  = llx;
369 	fnt[ch].down  = lly - xheight2;
370 	fnt[ch].right = wx;
371 	fnt[ch].up    = ury - xheight2;
372 	fnt[ch].last_adjust = (urx==0 || wx==0 || fixed_pitch) ? 0 : urx - wx;
373       }
374       else
375       {
376 	fnt[ch].left  = 0;
377 	fnt[ch].down  = - PlainCharHeight / 2;
378 	fnt[ch].right = PlainCharWidth;
379 	fnt[ch].up    = PlainCharHeight / 2;
380 	fnt[ch].last_adjust = 0;
381       }
382       debug6(DFT, DDD, "  fnt[%c] = (%d,%d,%d,%d,%d)",ch, fnt[ch].left,
383 	fnt[ch].down, fnt[ch].right, fnt[ch].up, fnt[ch].last_adjust);
384     }
385   }
386 } /* end ReadCharMetrics */
387 
388 
389 /*****************************************************************************/
390 /*                                                                           */
391 /*  ReadCompositeMetrics(face, Extrafilename, extra_fnum, lnum, composite,   */
392 /*    cmp, cmptop, fp)                                                       */
393 /*                                                                           */
394 /*  Read a sequence of composite metrics lines.  The font record is face.    */
395 /*  The line number is lnum; input is to be read from file fp.               */
396 /*                                                                           */
397 /*****************************************************************************/
398 
ReadCompositeMetrics(OBJECT face,OBJECT Extrafilename,FILE_NUM extra_fnum,int * lnum,unsigned short composite[],COMPOSITE cmp[],int * cmptop,FILE * fp)399 static void ReadCompositeMetrics(OBJECT face, OBJECT Extrafilename,
400   FILE_NUM extra_fnum, int *lnum, unsigned short composite[],
401   COMPOSITE cmp[], int *cmptop, FILE *fp)
402 { int status;
403   FULL_CHAR buff[MAX_BUFF], composite_name[100], name[100];
404   int composite_num, x_offset, y_offset, i, count;
405   FULL_CHAR composite_code, code;
406 
407   /* build composites */
408   while( (status = ReadOneLine(fp, buff, MAX_BUFF)) != 0
409 		&& StringBeginsWith(buff, AsciiToFull("CC")) )
410   {
411     (*lnum)++;
412     debug1(DFT, DD, "  composite: %s", buff);
413 
414     /* read CC <charname> <number_of_pieces> ; and move i to after it */
415     if( sscanf((char *)buff, "CC %s %d ", composite_name, &composite_num) != 2 )
416       Error(37, 5, "syntax error in extra font file %s (line %d)",
417 	FATAL, &fpos(Extrafilename), FileName(extra_fnum), *lnum);
418     for( i = 0;  buff[i] != ';' && buff[i] != '\0'; i++ );
419     if( buff[i] != ';' )
420       Error(37, 5, "syntax error in extra font file %s (line %d)",
421 	FATAL, &fpos(Extrafilename), FileName(extra_fnum), *lnum);
422     i++;
423 
424     /* add entry for this character to composite */
425     composite_code = MapCharEncoding(composite_name,font_mapping(face));
426     if( composite_code == (FULL_CHAR) '\0' )
427       Error(37, 6, "unknown character name %s in font file %s (line %d)",
428 	FATAL, &fpos(Extrafilename), composite_name, FileName(extra_fnum),
429 	*lnum);
430     composite[composite_code] = *cmptop;
431 
432     for( count = 0; count < composite_num; count++ )
433     {
434       /* read one PCC <charname> <xoffset> <yoffset> ; and move i to after it */
435       if( sscanf((char *)&buff[i]," PCC %s %d %d",name,&x_offset,&y_offset)!=3 )
436 	Error(37, 5, "syntax error in extra font file %s (line %d)",
437 	  FATAL, &fpos(Extrafilename), FileName(extra_fnum), *lnum);
438       for( ; buff[i] != ';' && buff[i] != '\0'; i++ );
439       if( buff[i] != ';' )
440 	Error(37, 5, "syntax error in extra font file %s (line %d)",
441 	  FATAL, &fpos(Extrafilename), FileName(extra_fnum), *lnum);
442       i++;
443 
444       /* load this piece into cmp */
445       if( *cmptop >= MAX_CHARS )
446 	Error(37, 7, "too many composites in file %s (at line %d)",
447 	  FATAL, &fpos(Extrafilename), FileName(extra_fnum), *lnum);
448       code = MapCharEncoding(name, font_mapping(face));
449       cmp[*cmptop].char_code = code;
450       cmp[*cmptop].x_offset = x_offset;
451       cmp[*cmptop].y_offset = y_offset;
452       (*cmptop)++;
453     }
454 
455     /* add null terminating component */
456     if( *cmptop >= MAX_CHARS )
457       Error(37, 8, "too many composites in file %s (at line %d)",
458 	FATAL, &fpos(Extrafilename), FileName(extra_fnum), *lnum);
459     cmp[*cmptop].char_code = (FULL_CHAR) '\0';
460     (*cmptop)++;
461   }
462   if( status == 0 ||
463 	!StringBeginsWith(buff, AsciiToFull("EndBuildComposites")) )
464     Error(37, 9, "missing EndBuildComposites in extra font file %s (line %d)",
465       FATAL, &fpos(Extrafilename), FileName(extra_fnum), *lnum);
466 } /* end ReadCompositeMetrics */
467 
468 
469 /*@::FontRead()@**************************************************************/
470 /*                                                                           */
471 /*  static OBJECT FontRead(FULL_CHAR *family_name, *face_name, OBJECT err)   */
472 /*                                                                           */
473 /*  Search the font databases for a font with this family and face name.     */
474 /*  If found, read the font and update this module's data structures, then   */
475 /*  return the face object.                                                  */
476 /*                                                                           */
477 /*  If an error occurs, use fpos(err) for reporting its location if nothing  */
478 /*  better suggests itself.                                                  */
479 /*                                                                           */
480 /*****************************************************************************/
481 
FontRead(FULL_CHAR * family_name,FULL_CHAR * face_name,OBJECT err)482 static OBJECT FontRead(FULL_CHAR *family_name, FULL_CHAR *face_name, OBJECT err)
483 {
484   OBJECT cs, link, db, fontdef_obj, y, ylink;
485   FULL_CHAR tag[100], seq[100];
486   FILE_NUM dfnum; long dfpos, cont; int dlnum;
487   BOOLEAN font_name_found, font_bbox_found;
488   OBJECT family, face, font_name, AFMfilename, Extrafilename, LCMfilename;
489   OBJECT recode, first_size;
490   FULL_CHAR buff[MAX_BUFF], command[MAX_BUFF], ch;
491   int status;
492   int xheight2, i, lnum, ligtop, cmptop;
493   float fl_xheight2, fl_under_pos, fl_under_thick;
494   int under_pos, under_thick;
495   BOOLEAN upfound, utfound, xhfound;
496   BOOLEAN fixed_pitch = FALSE;
497   FILE_NUM fnum, extra_fnum;  FILE *fp, *extra_fp;
498   struct metrics *fnt;
499   FULL_CHAR *lig;  unsigned short *composite;  COMPOSITE *cmp;
500   unsigned short *kt;  FULL_CHAR *kc;  unsigned char *kv;  FULL_LENGTH *ks;
501   FULL_LENGTH bbox_llx, bbox_lly, bbox_urx, bbox_ury;
502   debug2(DFT, D, "FontRead(%s, %s)", family_name, face_name);
503 
504 
505   /***************************************************************************/
506   /*                                                                         */
507   /*  Get the @FontDef object with tag family_name-face_name from databases  */
508   /*                                                                         */
509   /***************************************************************************/
510 
511   /* if no databases available, fatal error */
512   cs = cross_sym(FontDefSym);
513   if( cs == nilobj )
514   { Error(37, 10, "unable to set font %s %s (no font databases loaded)",
515       FATAL, no_fpos, family_name, face_name);
516   }
517 
518   /* search the databases for @FontDef @Tag { family-face } */
519   sprintf( (char *) tag, "%s-%s", family_name, face_name);
520   for( link = NextUp(Up(cs));  link != cs;  link = NextUp(link) )
521   { Parent(db, link);
522     if( DbRetrieve(db, FALSE, FontDefSym,tag,seq,&dfnum,&dfpos,&dlnum,&cont) )
523       break;
524   }
525 
526   /* if not found, return nilobj */
527   if( link == cs )
528   { debug0(DFT, D, "FontRead returning nilobj (not in any database)");
529     return nilobj;
530   }
531 
532   /* found it; read @FontDef object from database file */
533   SwitchScope(nilobj);
534   fontdef_obj = ReadFromFile(dfnum, dfpos, dlnum);
535   UnSwitchScope(nilobj);
536   if( fontdef_obj == nilobj )
537     Error(37, 11, "cannot read %s for %s", INTERN, no_fpos, KW_FONTDEF, tag);
538 
539 
540   /***************************************************************************/
541   /*                                                                         */
542   /*  Extract the attributes of fontdef_obj, and check that they are OK.     */
543   /*                                                                         */
544   /***************************************************************************/
545 
546   /* extract the various attributes */
547   family = face = font_name = AFMfilename = nilobj;
548   Extrafilename = LCMfilename = recode = nilobj;
549   for( ylink=Down(fontdef_obj);  ylink != fontdef_obj;  ylink=NextDown(ylink) )
550   { Child(y, ylink);
551     if( type(y) == PAR )
552     {
553       assert( type(y) == PAR, "FontRead: type(y) != PAR!" );
554       if( actual(y) == fd_tag )
555       {
556 	/* do nothing with this one */
557       }
558       else if( actual(y) == fd_family )
559       { Child(family, Down(y));
560 	if( !is_word(type(family)) || !StringEqual(string(family), family_name) )
561 	  Error(37, 12, "font family name %s incompatible with %s value %s",
562 	    FATAL, &fpos(fontdef_obj), string(family), KW_TAG, tag);
563       }
564       else if( actual(y) == fd_face )
565       { Child(face, Down(y));
566 	if( !is_word(type(face)) || !StringEqual(string(face), face_name) )
567 	  Error(37, 13, "font face name %s incompatible with %s value %s",
568 	    FATAL, &fpos(fontdef_obj), string(face), KW_TAG, tag);
569       }
570       else if( actual(y) == fd_name )
571       { Child(font_name, Down(y));
572 	font_name = ReplaceWithTidy(font_name, WORD_TIDY);
573 	if( !is_word(type(font_name)) )
574 	  Error(37, 14, "illegal font name (quotes needed?)",
575 	      FATAL, &fpos(font_name));
576       }
577       else if( actual(y) == fd_metrics )
578       { Child(AFMfilename, Down(y));
579 	AFMfilename = ReplaceWithTidy(AFMfilename, WORD_TIDY);
580 	if( !is_word(type(AFMfilename)) )
581 	  Error(37, 15, "illegal font metrics file name (quotes needed?)",
582 	    FATAL, &fpos(AFMfilename));
583       }
584       else if( actual(y) == fd_extra_metrics )
585       { Child(Extrafilename, Down(y));
586 	Extrafilename = ReplaceWithTidy(Extrafilename, WORD_TIDY);
587 	if( !is_word(type(Extrafilename)) )
588 	  Error(37, 16, "illegal font extra metrics file name (quotes needed?)",
589 	    FATAL, &fpos(Extrafilename));
590       }
591       else if( actual(y) == fd_mapping )
592       { Child(LCMfilename, Down(y));
593 	LCMfilename = ReplaceWithTidy(LCMfilename, WORD_TIDY);
594 	if( !is_word(type(LCMfilename)) )
595 	  Error(37, 17, "illegal mapping file name (quotes needed?)",
596 	    FATAL, &fpos(LCMfilename));
597       }
598       else if( actual(y) == fd_recode )
599       { Child(recode, Down(y));
600 	recode = ReplaceWithTidy(recode, WORD_TIDY);
601 	if( !is_word(type(recode)) )
602 	  Error(37, 18, "illegal value of %s", FATAL, &fpos(recode),
603 	    SymName(fd_recode));
604       }
605       else
606       { assert(FALSE, "FontRead: cannot identify component of FontDef")
607       }
608     }
609   }
610 
611   /* check that all the compulsory ones were found */
612   /* a warning message will have already been given if not */
613   if( family == nilobj || face == nilobj || font_name == nilobj ||
614       AFMfilename  == nilobj || LCMfilename == nilobj )
615   {
616     debug0(DFT, D, "FontRead returning nilobj (missing compulsory)");
617     return nilobj;
618   }
619 
620 
621   /***************************************************************************/
622   /*                                                                         */
623   /*  Update font tree to have this family, face and first_size.             */
624   /*                                                                         */
625   /***************************************************************************/
626 
627   /* insert family into font tree if not already present */
628   for( link = Down(font_root);  link != font_root;  link = NextDown(link) )
629   { Child(y, link);
630     if( StringEqual(string(y), string(family)) )
631     { family = y;
632       break;
633     }
634   }
635   if( link == font_root )
636     MoveLink(Up(family), font_root, PARENT);
637 
638   /* insert face into family, or error if already present */
639   for( link = Down(family);  link != family;  link = NextDown(link) )
640   { Child(y, link);
641     if( StringEqual(string(y), string(face)) )
642     { Error(37, 19, "font %s %s already defined, at%s", WARN, &fpos(face),
643 	string(family), string(face), EchoFilePos(&fpos(y)));
644       debug0(DFT, D, "FontRead returning: font already defined");
645       DisposeObject(fontdef_obj);
646       return y;
647     }
648   }
649   MoveLink(Up(face), family, PARENT);
650 
651   /* PostScript name and AFM file name are first two children of face */
652   Link(face, font_name);
653   Link(face, AFMfilename);
654 
655   /* AFM file name has extra file name as optional child */
656   if( Extrafilename != nilobj )
657     Link(AFMfilename, Extrafilename);
658 
659   /* load character mapping file */
660   if( recode != nilobj && StringEqual(string(recode), AsciiToFull("No")) )
661   { font_recoded(face) = FALSE;
662     font_mapping(face) = MapLoad(LCMfilename, FALSE);
663   }
664   else if( recode == nilobj || StringEqual(string(recode), AsciiToFull("Yes")) )
665   { font_recoded(face) = TRUE;
666     font_mapping(face) = MapLoad(LCMfilename, TRUE);
667   }
668   else Error(37, 20, "expecting either Yes or No here", FATAL, &fpos(recode));
669 
670   /* say that this font is currently unused on any page */
671   font_page(face) = 0;
672 
673   /* get a new number for this (default) font size */
674   if( ++font_count >= finfo_size )
675   { if( font_count > MAX_FONT )
676       Error(37, 21, "too many different fonts and sizes (maximum is %d)",
677 	FATAL, &fpos(err),MAX_FONT);
678     ifdebug(DMA, D,
679       DebugRegisterUsage(MEM_FONTS, -1, -finfo_size * sizeof(FONT_INFO)));
680     finfo_size *= 2;
681     ifdebug(DMA, D,
682       DebugRegisterUsage(MEM_FONTS, 1, finfo_size * sizeof(FONT_INFO)));
683     finfo = (FONT_INFO *) realloc(finfo, finfo_size * sizeof(FONT_INFO));
684     if( finfo == (FONT_INFO *) NULL )
685       Error(37, 22, "run out of memory when increasing font table size",
686 	FATAL, &fpos(err));
687   }
688 
689   /* build the first size record, and initialize it with what we know now */
690   first_size = MakeWordTwo(WORD, AsciiToFull("fnt"), StringInt(++font_seqnum),
691     no_fpos);
692   Link(face, first_size);
693   font_num(first_size) = font_count;
694   font_size(first_size) = BackEnd->uses_font_metrics ? SZ_DFT : PlainCharHeight;
695   font_recoded(first_size) = font_recoded(face);
696   font_mapping(first_size) = font_mapping(face);
697   font_num(face) = font_num(first_size); /* Uwe's suggestion, helps PDF */
698   /* font_xheight2, font_bbox_lly, font_bbox_ury, font_spacewidth still to do */
699 
700 
701   /***************************************************************************/
702   /*                                                                         */
703   /*  Read the Adobe font metrics file, and record what's in it.             */
704   /*                                                                         */
705   /***************************************************************************/
706 
707   /* open the Adobe font metrics (AFM) file of the font */
708   debug0(DFS, D, "  calling DefineFile from FontRead");
709   fnum = DefineFile(string(AFMfilename), STR_EMPTY, &fpos(AFMfilename),
710     FONT_FILE, FONT_PATH);
711   fp = OpenFile(fnum, FALSE, FALSE);
712   if( fp == NULL )
713     Error(37, 23, "cannot open font file %s", FATAL, &fpos(AFMfilename),
714       FileName(fnum));
715 
716   /* check that the AFM file begins, as it should, with "StartFontMetrics" */
717   if( ReadOneLine(fp, buff, MAX_BUFF) == 0 ||
718 	sscanf( (char *) buff, "%s", command) != 1 ||
719 	!StringEqual(command, "StartFontMetrics")  )
720   { debug1(DFT, DD, "first line of AFM file:%s", buff);
721     debug1(DFT, DD, "command:%s", command);
722     Error(37, 24, "font file %s does not begin with StartFontMetrics",
723       FATAL, &fpos(AFMfilename), FileName(fnum));
724   }
725 
726   /* initialise font metrics table for the new font */
727   ifdebug(DMA, D,
728       DebugRegisterUsage(MEM_FONTS, 1, MAX_CHARS * sizeof(struct metrics)));
729   fnt = (struct metrics *) malloc(MAX_CHARS * sizeof(struct metrics));
730   if( fnt == (struct metrics *) NULL )
731     Error(37, 25, "run out of memory while reading font file %s",
732       FATAL, &fpos(err), FileName(fnum));
733   ifdebug(DMA, D,
734       DebugRegisterUsage(MEM_FONTS, 0, 2*MAX_CHARS*sizeof(FULL_CHAR)));
735 
736   /* initialise ligature table for the new font */
737   lig = (FULL_CHAR *) malloc(2*MAX_CHARS*sizeof(FULL_CHAR));
738   if( lig == (FULL_CHAR *) NULL )
739     Error(37, 25, "run out of memory while reading font file %s",
740       FATAL, &fpos(err), FileName(fnum));
741   for( i = 0;  i < MAX_CHARS;  i++ )  lig[i] = 1;	/* i.e. char unknown */
742   ligtop = MAX_CHARS+2;		/* must avoid ligtop - MAX_CHARS == 0 or 1 */
743 
744   /* initialise composites table for the new font */
745   composite = (unsigned short *) malloc(MAX_CHARS * sizeof(unsigned short));
746   if( composite == (unsigned short *) NULL )
747     Error(37, 25, "run out of memory while reading font file %s",
748       FATAL, &fpos(err), FileName(fnum));
749   cmp = (COMPOSITE *) malloc(MAX_CHARS * sizeof(COMPOSITE));
750   if( cmp == (COMPOSITE *) NULL )
751     Error(37, 25, "run out of memory while reading font file %s",
752       FATAL, &fpos(err), FileName(fnum));
753   for( i = 0;  i < MAX_CHARS;  i++ )  composite[i] = 0;	/* i.e. not composite */
754   cmptop = 1;   /* must avoid cmptop == 0 */
755 
756   /* initialise kerning table for the new font */
757   ifdebug(DMA, D,
758       DebugRegisterUsage(MEM_FONTS, 0, MAX_CHARS * sizeof(unsigned short)));
759   kt = (unsigned short *) malloc(MAX_CHARS * sizeof(unsigned short));
760   if( kt == (unsigned short *) NULL )
761     Error(37, 25, "run out of memory while reading font file %s",
762       FATAL, &fpos(err), FileName(fnum));
763   for( i = 0;  i < MAX_CHARS;  i++ )  kt[i] = 0;  /* i.e. no kerns */
764   ks = (FULL_LENGTH *) NULL;			  /* i.e. no kern sizes */
765 
766   /* read font metrics file fp */
767   xhfound = upfound = utfound = FALSE;
768   xheight2 = under_thick = under_pos = 0;
769   kc = (FULL_CHAR *) NULL;
770   kv = (unsigned char *) NULL;
771   ks = (FULL_LENGTH *) NULL;
772   font_name_found = font_bbox_found = FALSE;  lnum = 1;
773   bbox_llx = bbox_lly = bbox_urx = bbox_ury = 0;
774   while( (status = ReadOneLine(fp, buff, MAX_BUFF)) != 0 &&
775     !(buff[0] == 'E' && StringEqual(buff, AsciiToFull("EndFontMetrics"))) )
776   {
777     lnum++;
778     if( sscanf( (char *) buff, "%s", command) != EOF ) switch( command[0] )
779     {
780 
781       case 'U':
782 
783 	if( StringEqual(command, AsciiToFull("UnderlinePosition")) )
784 	{ if( upfound )
785 	  { Error(37, 26, "UnderlinePosition found twice in font file (line %d)",
786 	      FATAL, &fpos(AFMfilename), lnum);
787 	  }
788 	  sscanf( (char *) buff, "UnderlinePosition %f", &fl_under_pos);
789 	  under_pos = fl_under_pos;
790 	  upfound = TRUE;
791 	}
792 	else if( StringEqual(command, AsciiToFull("UnderlineThickness")) )
793 	{ if( utfound )
794 	  { Error(37, 27, "UnderlineThickness found twice in font file (line %d)",
795 	      FATAL, &fpos(AFMfilename), lnum);
796 	  }
797 	  sscanf( (char *) buff, "UnderlineThickness %f", &fl_under_thick);
798 	  under_thick = fl_under_thick;
799 	  utfound = TRUE;
800 	}
801 	break;
802 
803 
804       case 'X':
805 
806 	if( StringEqual(command, AsciiToFull("XHeight")) )
807 	{ if( xhfound )
808 	  { Error(37, 28, "XHeight found twice in font file (line %d)",
809 	      FATAL, &fpos(AFMfilename), lnum);
810 	  }
811 	  sscanf( (char *) buff, "XHeight %f", &fl_xheight2);
812 	  xheight2 = fl_xheight2 / 2;
813 	  xhfound = TRUE;
814 	}
815 	break;
816 
817 
818       case 'F':
819 
820 	if( StringEqual(command, AsciiToFull("FontName")) )
821 	{ if( font_name_found )
822 	  { Error(37, 29, "FontName found twice in font file %s (line %d)",
823 	      FATAL, &fpos(AFMfilename), FileName(fnum), lnum);
824 	  }
825 	  sscanf( (char *) buff, "FontName %s", command);
826 	  if( StringEqual(command, STR_EMPTY) )
827 	  { Error(37, 30, "FontName empty in font file %s (line %d)",
828 	      FATAL, &fpos(AFMfilename), FileName(fnum), lnum);
829 	  }
830 	  Child(y, Down(face));
831 	  if( !StringEqual(command, string(y)) )
832 	  Error(37, 31, "FontName in font file (%s) and %s (%s) disagree",
833 	    WARN, &fpos(AFMfilename), command, KW_FONTDEF, string(y));
834 	  font_name_found = TRUE;
835 	}
836 	else if( StringEqual(command, AsciiToFull("FontBBox")) )
837 	{
838 	  if( font_bbox_found )
839 	  { Error(37, 69, "FontBBox found twice in font file %s (line %d)",
840 	      FATAL, &fpos(AFMfilename), FileName(fnum), lnum);
841 	  }
842 	  if( sscanf( (char *) buff, "FontBBox %d %d %d %d",
843 		&bbox_llx, &bbox_lly, &bbox_urx, &bbox_ury) != 4 )
844 	  { Error(37, 70, "FontBBox format error in font file %s (line %d)",
845 	      FATAL, &fpos(AFMfilename), FileName(fnum), lnum);
846 	  }
847 	  font_bbox_found = TRUE;
848 	}
849 	break;
850 
851 
852       case 'I':
853 
854 	if( StringEqual(command, AsciiToFull("IsFixedPitch")) )
855 	{
856 	  sscanf( (char *) buff, "IsFixedPitch %s", command);
857 	  if( StringEqual(command, AsciiToFull("true")) )
858 	  { fixed_pitch = TRUE;
859 	  }
860 	}
861 	break;
862 
863 
864       case 'S':
865 
866 	if( StringEqual(command, AsciiToFull("StartCharMetrics")) )
867 	{
868 	  if( !font_name_found )
869 	    Error(37, 32, "FontName missing in file %s",
870 	      FATAL, &fpos(AFMfilename), FileName(fnum));
871 	  if( !xhfound )  xheight2 = DEFAULT_XHEIGHT / 2;
872 	  ReadCharMetrics(face, fixed_pitch, xheight2, lig, &ligtop,
873 	    fnum, fnt, &lnum, fp);
874 	}
875 	else if( BackEnd->uses_font_metrics && Kern &&
876 	  StringEqual(command, AsciiToFull("StartKernPairs")) )
877 	{ FULL_CHAR ch1, ch2, last_ch1;
878 	  FULL_CHAR name1[30], name2[30];
879 	  int kc_top, ks_top, pos, num_pairs, ksize;  float fl_ksize;
880 
881 	  if( sscanf( (char *) buff, "StartKernPairs %d", &num_pairs) != 1 )
882 	    Error(37, 33, "syntax error on StartKernPairs line in font file %s (line %d)",
883 	      FATAL, &fpos(AFMfilename), FileName(fnum), lnum);
884 	  kc_top = 1;  ks_top = 1;
885 	  ifdebug(DMA, D,
886 	    DebugRegisterUsage(MEM_FONTS, 0, 2*num_pairs * sizeof(FULL_CHAR)));
887 	  kc = (FULL_CHAR *) malloc(2 * num_pairs * sizeof(FULL_CHAR));
888 	  ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 0,
889 	    2 * num_pairs * sizeof(unsigned char)));
890 	  kv = (unsigned char *) malloc(2 * num_pairs * sizeof(unsigned char));
891 	  ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 0,
892 	    num_pairs * sizeof(FULL_LENGTH)));
893 	  ks = (FULL_LENGTH *) malloc(num_pairs * sizeof(FULL_LENGTH));
894 	  last_ch1 = '\0';
895 	  while( ReadOneLine(fp, buff, MAX_BUFF) != 0 &&
896 	    !StringBeginsWith(buff, AsciiToFull("EndKernPairs")) )
897 	  {
898 	    debug1(DFT, DD, "FontRead reading %s", buff);
899 	    lnum++;
900 	    if( StringBeginsWith(buff, AsciiToFull("KPX")) )
901 	    {
902 	      /* get the two character names and kern size from buff */
903 	      if( sscanf((char *)buff, "KPX %s %s %f",name1,name2,&fl_ksize)!=3 )
904 		Error(37, 34, "syntax error in font file %s (line %d): %s",
905 		  FATAL, &fpos(AFMfilename), FileName(fnum), lnum, buff);
906 
907 	      /* ignore size 0 kern pairs (they are frequent, why?) */
908 	      ksize = fl_ksize;
909 	      if( ksize == 0 )  continue;
910 
911 	      /* check that both characters are encoded */
912 	      ch1 = MapCharEncoding(name1, font_mapping(face));
913 	      if( ch1 == '\0' )
914 	      {
915 		continue;
916 	      }
917 	      ch2 = MapCharEncoding(name2, font_mapping(face));
918 	      if( ch2 == '\0' )
919 	      {
920 		continue;
921 	      }
922 
923 	      /* check that ch1 is contiguous with previous occurrences */
924 	      if( ch1 != last_ch1 && kt[ch1] != 0 )
925 	      { Error(37, 35, "ignoring out-of-order kerning pair %s %s in font file %s (line %d)",
926 		  WARN, &fpos(AFMfilename), name1, name2, FileName(fnum), lnum);
927 		continue;
928 	      }
929 	      last_ch1 = ch1;
930 
931 	      /* if ch1 never seen before, make new entry in kt[] and kc[] */
932 	      if( kt[ch1] == 0 )
933 	      { debug2(DFT, DD, "  kt[%d] = %d", ch1, kc_top);
934 		kt[ch1] = kc_top;
935 		kc[kc_top] = (FULL_CHAR) '\0';
936 		kv[kc_top] = 0;
937 		kc_top++;
938 	      }
939 
940 	      /* find kerning size in ks[] or else add it to the end */
941 	      for( pos = 1;  pos < ks_top;  pos++ )
942 	      { if( ks[pos] == ksize )  break;
943 	      }
944 	      if( pos == ks_top )
945 	      { if( ks_top == num_pairs )
946 		  Error(37, 36, "too many kerning pairs in font file %s (line %d)",
947 		    FATAL, &fpos(AFMfilename), FileName(fnum), lnum);
948 		debug2(DFT, DD, "  ks[%d] = %d", pos, ksize);
949 		ks[pos] = ksize;
950 		ks_top++;
951 	      }
952 
953 	      /* insert ch2 into the kc entries (sorted decreasing) for ch1 */
954 	      for( i = kc_top-1; i >= kt[ch1] && kc[i] < ch2;  i-- )
955 	      { kc[i+1] = kc[i];
956 		kv[i+1] = kv[i];
957 	      }
958 	      if( i >= kt[ch1] && kc[i] == ch2 )
959 		Error(37, 37, "kerning pair %s %s appears twice in font file %s (line %d)",
960 		  FATAL, &fpos(AFMfilename), name1, name2, FileName(fnum), lnum);
961 	      kc[i+1] = ch2;
962 	      kv[i+1] = pos;
963 	      kc_top++;
964 	    }
965 	  }
966 	  ks[0] = ks_top;
967 	}
968 	break;
969 
970 
971       default:
972 
973 	break;
974 
975     }
976   }
977 
978   /* make sure we terminated the font metrics file gracefully */
979   if( status == 0 )
980     Error(37, 38, "EndFontMetrics missing from font file %s",
981       FATAL, &fpos(AFMfilename), FileName(fnum));
982   fclose(fp);
983   fp = (FILE *) NULL;
984 
985   /* complete the initialization of first_size */
986   if( BackEnd->uses_font_metrics )
987   {
988     font_xheight2(first_size) = xheight2;
989     font_bbox_lly(first_size) = bbox_lly - xheight2;
990     font_bbox_ury(first_size) = bbox_ury - xheight2;
991   }
992   else
993   {
994     font_xheight2(first_size) = PlainCharHeight / 4;
995     font_bbox_lly(first_size) = - PlainCharHeight / 2;
996     font_bbox_ury(first_size) = PlainCharHeight / 2;
997   }
998   ch = MapCharEncoding(STR_PS_SPACENAME, font_mapping(first_size));
999   font_spacewidth(first_size) = ch == '\0' ? 0 : fnt[ch].right;
1000 
1001 
1002   /***************************************************************************/
1003   /*                                                                         */
1004   /*  Read the optional Extra font metrics file, and record what's in it.    */
1005   /*                                                                         */
1006   /***************************************************************************/
1007 
1008   if( Extrafilename != nilobj )
1009   { debug0(DFS, D, "  calling DefineFile from FontRead (extra_filename)");
1010     extra_fnum = DefineFile(string(Extrafilename), STR_EMPTY,
1011       &fpos(Extrafilename), FONT_FILE, FONT_PATH);
1012     extra_fp = OpenFile(extra_fnum, FALSE, FALSE);
1013     if( extra_fp == NULL )
1014       Error(37, 39, "cannot open extra font file %s", FATAL,
1015 	&fpos(Extrafilename), FileName(extra_fnum));
1016     lnum = 0;
1017 
1018     while( ReadOneLine(extra_fp, buff, MAX_BUFF) != 0 )
1019     {
1020       debug1(DFT, DD, "  Extra: %s", buff);
1021       lnum++;
1022       sscanf( (char *) buff, "%s", command);
1023       if( command[0] == 'S' )
1024       {
1025 	if( StringEqual(command, AsciiToFull("StartExtraCharMetrics")) )
1026 	{
1027 	  /* get extra character metrics, just like the others */
1028 	  debug0(DFT, DD, "  StartExtraCharMetrics calling ReadCharMetrics");
1029 	  ReadCharMetrics(face, fixed_pitch, xheight2, lig, &ligtop,
1030 	    extra_fnum, fnt, &lnum, extra_fp);
1031 	}
1032 	else if( StringEqual(command, AsciiToFull("StartBuildComposites")) )
1033 	{
1034 	  /* build composites */
1035 	  debug0(DFT, DD, "  StartBuildComposites");
1036 	  ReadCompositeMetrics(face, Extrafilename, extra_fnum, &lnum,
1037 	    composite, cmp, &cmptop, extra_fp);
1038 	}
1039       }
1040     }
1041 
1042     fclose(extra_fp);
1043     extra_fp = (FILE *) NULL;
1044   }
1045 
1046 
1047   /***************************************************************************/
1048   /*                                                                         */
1049   /*  Set finfo[fontcount] and exit.                                         */
1050   /*                                                                         */
1051   /***************************************************************************/
1052 
1053   finfo[font_count].font_table = first_size;
1054   finfo[font_count].original_face = face;
1055   finfo[font_count].underline_pos = xheight2 - under_pos;
1056   finfo[font_count].underline_thick = under_thick;
1057   finfo[font_count].size_table = fnt;
1058   finfo[font_count].lig_table = lig;
1059   finfo[font_count].composite = composite;
1060   finfo[font_count].cmp_table = cmp;
1061   finfo[font_count].cmp_top = cmptop;
1062   finfo[font_count].kern_table = kt;
1063   finfo[font_count].kern_chars = kc;
1064   finfo[font_count].kern_value = kv;
1065   finfo[font_count].kern_sizes = ks;
1066 
1067   ifdebug(DFT, DD, DebugKernTable(font_count));
1068   debug4(DFT, DD, "FontRead returning: %d, name %s, fs %d, xh2 %d",
1069     font_count, string(first_size), font_size(first_size), xheight2);
1070   return face;
1071 
1072 } /* end FontRead */
1073 
1074 
1075 /*@::FontChange()@************************************************************/
1076 /*                                                                           */
1077 /*  FontChange(style, x)                                                     */
1078 /*                                                                           */
1079 /*  Returns an internal font number which is the current font changed        */
1080 /*  according to word object x.  e.g. if current font is Roman 12p and x is  */
1081 /*  "-3p", then FontChange returns the internal font number of Roman 9p.     */
1082 /*                                                                           */
1083 /*  FontChange permits empty and null objects within x; these have no        */
1084 /*  effect.                                                                  */
1085 /*                                                                           */
1086 /*****************************************************************************/
1087 
FontChange(STYLE * style,OBJECT x)1088 void FontChange(STYLE *style, OBJECT x)
1089 { /* register */ int i;
1090   OBJECT requested_family, requested_face, requested_size;
1091   OBJECT par[3], family, face, fsize, y = nilobj, link, new, old, tmpf;
1092   GAP gp;  FULL_LENGTH flen = 0;  int num, c;  unsigned inc;
1093   struct metrics *newfnt, *oldfnt;
1094   FULL_CHAR *lig, *old_lig;
1095   int cmptop;
1096   COMPOSITE *oldcmp, *newcmp;
1097   FULL_LENGTH *oldks, *newks;  int klen;
1098   debug2(DFT, D, "FontChange( %s, %s )", EchoStyle(style), EchoObject(x));
1099   assert( font(*style) <= font_count, "FontChange: font_count!");
1100   ifdebug(DFT, DD, FontDebug());
1101 
1102   /***************************************************************************/
1103   /*                                                                         */
1104   /*  Analyse x, doing any small-caps, baselinemark, strut, and ligatures    */
1105   /*  immediately, and putting all the other words of x into par[0 .. num-1] */
1106   /*  for further analysis.                                                  */
1107   /*                                                                         */
1108   /***************************************************************************/
1109 
1110   num = 0;
1111   switch( type(x) )
1112   {
1113     case NULL_CLOS:
1114 
1115       /* acceptable, but do nothing */
1116       break;
1117 
1118 
1119     case WORD:
1120     case QWORD:
1121 
1122       if( StringEqual(string(x), STR_SMALL_CAPS_ON) )
1123         small_caps(*style) = SMALL_CAPS_ON;
1124       else if( StringEqual(string(x), STR_SMALL_CAPS_OFF) )
1125         small_caps(*style) = SMALL_CAPS_OFF;
1126       else if( StringEqual(string(x), STR_BASELINE_MARK) )
1127         baselinemark(*style) = TRUE;
1128       else if( StringEqual(string(x), STR_XHEIGHT2_MARK) )
1129         baselinemark(*style) = FALSE;
1130       else if( StringEqual(string(x), STR_NOSTRUT) )
1131         strut(*style) = FALSE;
1132       else if( StringEqual(string(x), STR_STRUT) )
1133         strut(*style) = TRUE;
1134       else if( StringEqual(string(x), STR_LIG) )
1135         ligatures(*style) = TRUE;
1136       else if( StringEqual(string(x), STR_NOLIG) )
1137         ligatures(*style) = FALSE;
1138       else if( StringEqual(string(x), STR_SMALL_CAPS_SET) )
1139         Error(37, 65, "%s in left parameter of %s must be followed by a value",
1140           WARN, &fpos(x), STR_SMALL_CAPS_SET, KW_FONT);
1141       else if( !StringEqual(string(x), STR_EMPTY) )
1142         par[num++] = x;
1143       break;
1144 
1145 
1146     case ACAT:
1147 
1148       for( link = Down(x);  link != x;  link = NextDown(link) )
1149       { Child(y, link);
1150         debug1(DFT, DDD, "  pars examining y = %s", EchoObject(y));
1151         if( type(y) == GAP_OBJ || type(y)  == NULL_CLOS )  continue;
1152         if( is_word(type(y)) )
1153         {
1154 	  if( StringEqual(string(y), STR_SMALL_CAPS_ON) )
1155 	    small_caps(*style) = SMALL_CAPS_ON;
1156 	  else if( StringEqual(string(y), STR_SMALL_CAPS_OFF) )
1157 	    small_caps(*style) = SMALL_CAPS_OFF;
1158 	  else if( StringEqual(string(y), STR_BASELINE_MARK) )
1159 	    baselinemark(*style) = TRUE;
1160 	  else if( StringEqual(string(y), STR_XHEIGHT2_MARK) )
1161 	    baselinemark(*style) = FALSE;
1162 	  else if( StringEqual(string(y), STR_NOSTRUT) )
1163 	    strut(*style) = FALSE;
1164 	  else if( StringEqual(string(y), STR_STRUT) )
1165 	    strut(*style) = TRUE;
1166 	  else if( StringEqual(string(y), STR_LIG) )
1167 	    ligatures(*style) = TRUE;
1168 	  else if( StringEqual(string(y), STR_NOLIG) )
1169 	    ligatures(*style) = FALSE;
1170 	  else if( StringEqual(string(y), STR_SMALL_CAPS_SET) )
1171 	  {
1172 	    if( NextDown(link) == x || NextDown(NextDown(link)) == x )
1173 	      Error(37, 65, "%s in %s must be followed by a value",
1174 	        WARN, &fpos(x), STR_SMALL_CAPS_SET, KW_FONT);
1175 	    else
1176 	    { float tmpf;
1177 	      Child(y, NextDown(NextDown(link)));
1178 	      if( !is_word(type(y)) )
1179 	        Error(37, 66, "%s in %s must be followed by a word",
1180 		  WARN, &fpos(x), STR_SMALL_CAPS_SET, KW_FONT);
1181 	      else if( sscanf( (char *) string(y), "%f", &tmpf) != 1 )
1182 	        Error(37, 67, "%s in %s followed by \"%s\" (should be number)",
1183 		  WARN, &fpos(x), STR_SMALL_CAPS_SET, KW_FONT, string(y));
1184 	      else if( tmpf <= 0 || tmpf >= 10 )
1185 	        Error(37, 68, "%s in %s followed by unreasonable number \"%s\"",
1186 		  WARN, &fpos(x), STR_SMALL_CAPS_SET, KW_FONT, string(y));
1187 	      else
1188 	        smallcaps_len(*style) = tmpf * FR;
1189 	      link = NextDown(NextDown(link));
1190 	    }
1191 	  }
1192 	  else if( !StringEqual(string(y), STR_EMPTY) )
1193 	  {
1194 	    if( num >= 3 )
1195 	    { Error(37, 40, "error in left parameter of %s",
1196 	        WARN, &fpos(x), KW_FONT);
1197 	      debug0(DFT, D, "FontChange returning: ACAT children");
1198 	      return;
1199 	    }
1200 	    debug2(DFT, DD, "  par[%d]++ = %s", num, string(y));
1201 	    par[num++] = y;
1202 	  }
1203         }
1204         else
1205         { Error(37, 41, "error in left parameter of %s",
1206 	    WARN, &fpos(x), KW_FONT);
1207 	  debug0(DFT, D, "FontChange returning: ACAT children");
1208 	  return;
1209         }
1210       }
1211       break;
1212 
1213 
1214     default:
1215 
1216       Error(37, 42, "error in left parameter of %s", WARN, &fpos(x), KW_FONT);
1217       debug0(DFT, D, "FontChange returning: wrong type");
1218       return;
1219 
1220   }
1221   debug1(DFT, DDD, " found pars, num = %d", num);
1222   if( num == 0 )
1223   { debug1(DFT, D, "FontChange returning %s", EchoStyle(style));
1224     return;
1225   }
1226 
1227   /***************************************************************************/
1228   /*                                                                         */
1229   /* Extract size, family, and face changes (if any) from par[0 .. num-1].   */
1230   /*                                                                         */
1231   /***************************************************************************/
1232 
1233   /* extract fsize parameter, if any */
1234   assert( num >= 1 && num <= 3, "FontChange: num!" );
1235   requested_size = nilobj;
1236   for( i = 0;  i < num;  i++ )
1237   {
1238     c = string(par[i])[0];
1239     if( c == CH_INCGAP || c == CH_DECGAP || decimaldigit(c) )
1240     {
1241       /* extract fsize, shuffle the rest down */
1242       requested_size = par[i];
1243       for( i = i + 1;  i < num;  i++ )
1244 	par[i-1] = par[i];
1245       num--;
1246     }
1247   }
1248 
1249   /* what remains must be family and face */
1250   switch( num )
1251   {
1252     case 0:
1253 
1254       requested_family = requested_face = nilobj;
1255       break;
1256 
1257 
1258     case 1:
1259 
1260       requested_family = nilobj;
1261       requested_face = par[0];
1262       break;
1263 
1264 
1265     case 2:
1266 
1267       requested_family = par[0];
1268       requested_face = par[1];
1269       break;
1270 
1271 
1272     default:
1273 
1274       Error(37, 43, "error in left parameter of %s", WARN, &fpos(x), KW_FONT);
1275       debug0(DFT, D, "FontChange returning: too many parameters");
1276       return;
1277       break;
1278   }
1279 
1280   /* check for initial font case: must have family, face, and size */
1281   if( font(*style) == NO_FONT && (requested_size == nilobj ||
1282 	requested_family == nilobj || requested_face == nilobj) )
1283     Error(37, 44, "initial font must have family, face and size",
1284       FATAL, &fpos(x));
1285 
1286 
1287   /***************************************************************************/
1288   /*                                                                         */
1289   /*  Either find the family and face already existing, or load them.        */
1290   /*                                                                         */
1291   /***************************************************************************/
1292 
1293   /* get font family */
1294   family = nilobj;
1295   if( requested_family != nilobj )
1296   {
1297     /* search for this family */
1298     for( link = Down(font_root);  link != font_root;  link = NextDown(link) )
1299     { Child(y, link);
1300       if( StringEqual(string(requested_family), string(y)) )  break;
1301     }
1302     if( link != font_root )
1303       family = y;
1304   }
1305   else
1306   {
1307     /* preserve current family */
1308     assert( Up(finfo[font(*style)].font_table)!=finfo[font(*style)].font_table,
1309       "FontChange: Up(finfo[font(*style)].font_table) !" );
1310     Parent(tmpf, Up(finfo[font(*style)].font_table));
1311     assert( is_word(type(tmpf)), "FontChange: type(tmpf)!" );
1312     assert( Up(tmpf) != tmpf, "FontChange: Up(tmpf)!" );
1313     Parent(family, Up(tmpf));
1314     assert( is_word(type(family)), "FontChange: type(family)!" );
1315   }
1316 
1317   /* get font face, if have family */
1318   face = nilobj;
1319   if( family != nilobj )
1320   {
1321     if( requested_face != nilobj )
1322     {
1323       /* search for this face in family */
1324       for( link = Down(family);  link != family;  link = NextDown(link) )
1325       {	Child(y, link);
1326 	if( StringEqual(string(requested_face), string(y)) )  break;
1327       }
1328       if( link != family )
1329 	face = y;
1330     }
1331     else
1332     {
1333       /* preserve current face */
1334       Parent(face, Up(finfo[font(*style)].font_table));
1335       assert( is_word(type(face)), "FontChange: type(face)!" );
1336       assert( Up(face) != face, "FontChange: Up(face)!" );
1337     }
1338   }
1339 
1340   if( face == nilobj )
1341   {
1342     /* face not loaded, try the font databases */
1343     assert( family != nilobj || requested_family != nilobj, "FontChange fr!" );
1344     assert( requested_face != nilobj, "FontChange requested_face!");
1345     if( family != nilobj )
1346       requested_family = family;
1347     face = FontRead(string(requested_family), string(requested_face), x);
1348 
1349     if( face == nilobj )
1350     {
1351       /* missing face name error; check whether a family name was intended */
1352       for( link = Down(font_root); link != font_root; link = NextDown(link) )
1353       { Child(y, link);
1354 	if( StringEqual(string(y), string(requested_face)) )  break;
1355       }
1356       if( link != font_root )
1357 	Error(37, 45, "font family name %s must be followed by a face name",
1358 	  WARN, &fpos(requested_face), string(requested_face));
1359       else
1360 	Error(37, 46, "no database contains a font with family name %s and face name %s",
1361 	  WARN, &fpos(requested_face), string(requested_family),
1362 	  string(requested_face));
1363       debug0(DFT, D, "FontChange returning (unable to set face)");
1364       return;
1365     }
1366   }
1367 
1368   assert( Down(face) != face, "FontChange: no children!" );
1369   assert( NextDown(Down(face)) != face, "FontChange: 1 child!" );
1370   assert( NextDown(NextDown(Down(face))) != face, "FontChange: 2 children!" );
1371 
1372   /***************************************************************************/
1373   /*                                                                         */
1374   /*  Now have family and face; search for size and return it if found.      */
1375   /*                                                                         */
1376   /***************************************************************************/
1377 
1378   /* get font size as integer flen */
1379   if( requested_size == nilobj )
1380     flen = font_size(finfo[font(*style)].font_table);
1381   else
1382   { GetGap(requested_size, style, &gp, &inc);
1383     if( mode(gp) != EDGE_MODE || units(gp) != FIXED_UNIT )
1384     { Error(37, 47, "syntax error in font size %s; ignoring it",
1385 	WARN, &fpos(requested_size), string(requested_size));
1386       flen = font_size(finfo[font(*style)].font_table);
1387     }
1388     else if( inc == GAP_ABS )
1389       flen = width(gp);
1390     else if( font(*style) == NO_FONT )
1391     { Error(37, 48, "no current font on which to base size change %s",
1392 	FATAL, &fpos(requested_size), string(requested_size));
1393     }
1394     else if( inc == GAP_INC )
1395       flen = font_size(finfo[font(*style)].font_table) + width(gp);
1396     else if( inc == GAP_DEC )
1397       flen = font_size(finfo[font(*style)].font_table) - width(gp);
1398     else Error(37, 49, "FontChange: %d", INTERN, &fpos(x), inc);
1399   }
1400 
1401   if( flen <= 0 )
1402   { Error(37, 50, "%s %s ignored (result is not positive)",
1403       WARN, &fpos(requested_size), string(requested_size), KW_FONT);
1404     debug0(DFT, D,"FontChange returning (non-positive size)");
1405     return;
1406   }
1407 
1408   /* search fonts of face for desired size; return if already present */
1409   if( !(BackEnd->uses_font_metrics) )  flen = PlainCharHeight;
1410   for( link=NextDown(NextDown(Down(face))); link!=face; link = NextDown(link) )
1411   { Child(fsize, link);
1412     if( font_size(fsize) == flen )
1413     { font(*style) = font_num(fsize);
1414       SetGap(space_gap(*style), nobreak(space_gap(*style)), FALSE, TRUE,
1415 	FIXED_UNIT, EDGE_MODE, font_spacewidth(fsize));
1416       debug2(DFT, D,"FontChange returning (old) %d (XHeight2 = %d)",
1417 	font(*style), font_xheight2(finfo[font(*style)].font_table));
1418       return;
1419     }
1420   }
1421 
1422   /***************************************************************************/
1423   /*                                                                         */
1424   /*  No suitable size right now, so scale the original size and exit.       */
1425   /*                                                                         */
1426   /***************************************************************************/
1427 
1428   /* get a new number for this new size */
1429   if( ++font_count >= finfo_size )
1430   { if( font_count > MAX_FONT )
1431       Error(37, 51, "too many different fonts and sizes (max is %d)",
1432 	FATAL, &fpos(x), MAX_FONT);
1433     ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, -1,
1434       -finfo_size * sizeof(FONT_INFO)));
1435     finfo_size *= 2;
1436     ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 1,
1437       finfo_size * sizeof(FONT_INFO)));
1438     finfo = (FONT_INFO *) realloc(finfo, finfo_size * sizeof(FONT_INFO));
1439     if( finfo == (FONT_INFO *) NULL )
1440       Error(37, 52, "run out of memory when increasing font table size",
1441 	FATAL, &fpos(x));
1442   }
1443 
1444   /* create a new sized font record */
1445   Child(old, NextDown(NextDown(Down(face))));
1446   assert( is_word(type(old)), "FontChange: old!" );
1447   new = MakeWord(WORD, string(old), no_fpos);
1448   Link(face, new);
1449   font_num(new)         = font_count;
1450   font_size(new)        = BackEnd->uses_font_metrics ? flen : font_size(old);
1451   font_xheight2(new)    = font_xheight2(old) * font_size(new) / font_size(old);
1452   font_bbox_lly(new)    = font_bbox_lly(old) * font_size(new) / font_size(old);
1453   font_bbox_ury(new)    = font_bbox_ury(old) * font_size(new) / font_size(old);
1454   font_recoded(new)	= font_recoded(old);
1455   font_mapping(new)	= font_mapping(old);
1456   font_spacewidth(new)	= font_spacewidth(old) * font_size(new)/font_size(old);
1457   finfo[font_count].font_table = new;
1458   finfo[font_count].original_face = face;
1459   finfo[font_count].underline_pos =
1460     (finfo[font_num(old)].underline_pos * font_size(new)) / font_size(old);
1461   finfo[font_count].underline_thick =
1462     (finfo[font_num(old)].underline_thick * font_size(new)) / font_size(old);
1463   ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 1,
1464       MAX_CHARS * sizeof(struct metrics)));
1465   finfo[font_count].size_table =
1466     (struct metrics *) malloc(MAX_CHARS * sizeof(struct metrics));
1467   if( finfo[font_count].size_table == (struct metrics *) NULL )
1468     Error(37, 53, "run out of memory when changing font or font size",
1469       FATAL, &fpos(x));
1470 
1471   /* copy lig_table (sharing it is not safe) bug fix JeffK 25/06/13 */
1472   lig = (FULL_CHAR *) malloc(2*MAX_CHARS*sizeof(FULL_CHAR));
1473   old_lig = finfo[font_num(old)].lig_table;
1474   if( lig == (FULL_CHAR *) NULL )
1475     Error(37, 54, "run out of memory when changing font or font size",
1476       FATAL, &fpos(x));
1477   for( i = 0;  i < 2*MAX_CHARS;  i++ )
1478     lig[i] = old_lig[i];
1479   finfo[font_count].lig_table = lig;
1480 
1481   /* scale old font to new size */
1482   newfnt = finfo[font_num(new)].size_table;
1483   oldfnt = finfo[font_num(old)].size_table;
1484   for( i = 0;  i < MAX_CHARS;  i++ )  if( lig[i] != 1 )
1485   { newfnt[i].left  = (oldfnt[i].left  * font_size(new)) / font_size(old);
1486     newfnt[i].right = (oldfnt[i].right * font_size(new)) / font_size(old);
1487     newfnt[i].down  = (oldfnt[i].down  * font_size(new)) / font_size(old);
1488     newfnt[i].up    = (oldfnt[i].up    * font_size(new)) / font_size(old);
1489     newfnt[i].last_adjust = (oldfnt[i].last_adjust * font_size(new)) / font_size(old);
1490   }
1491 
1492   /* copy and scale composite table */
1493   finfo[font_count].composite = finfo[font_num(old)].composite;
1494   finfo[font_count].cmp_top = cmptop = finfo[font_num(old)].cmp_top;
1495   oldcmp = finfo[font_num(old)].cmp_table;
1496   newcmp = (COMPOSITE *) malloc(cmptop*sizeof(COMPOSITE));
1497   if( newcmp == (COMPOSITE *) NULL )
1498     Error(37, 54, "run out of memory when changing font or font size",
1499       FATAL, &fpos(x));
1500   for( i = 1;  i < cmptop;  i++ )  /* NB position 0 is unused */
1501   { newcmp[i].char_code = oldcmp[i].char_code;
1502     if( newcmp[i].char_code != (FULL_CHAR) '\0' )
1503     { newcmp[i].x_offset = (oldcmp[i].x_offset*font_size(new)) / font_size(old);
1504       newcmp[i].y_offset = (oldcmp[i].y_offset*font_size(new)) / font_size(old);
1505       debug5(DFT, DD, "FontChange scales composite %d from (%d, %d) to (%d, %d)",
1506 	(int) newcmp[i].char_code, oldcmp[i].x_offset, oldcmp[i].y_offset,
1507 	newcmp[i].x_offset, newcmp[i].y_offset);
1508     }
1509   }
1510   finfo[font_count].cmp_table = newcmp;
1511 
1512   /* copy and scale kerning tables */
1513   finfo[font_count].kern_table = finfo[font_num(old)].kern_table;
1514   finfo[font_count].kern_chars = finfo[font_num(old)].kern_chars;
1515   finfo[font_count].kern_value = finfo[font_num(old)].kern_value;
1516   oldks = finfo[font_num(old)].kern_sizes;
1517   if( oldks != (FULL_LENGTH *) NULL )
1518   { klen = oldks[0];
1519     ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 0, klen * sizeof(FULL_LENGTH)));
1520     finfo[font_count].kern_sizes = newks =
1521       (FULL_LENGTH *) malloc(klen * sizeof(FULL_LENGTH));
1522     if( newks == (FULL_LENGTH *) NULL )
1523       Error(37, 55, "run out of memory when changing font or font size",
1524 	FATAL, &fpos(x));
1525     newks[0] = klen;
1526     for( i = 1;  i < klen;  i++ )
1527       newks[i] = (oldks[i] * font_size(new)) / font_size(old);
1528   }
1529   else finfo[font_count].kern_sizes = (FULL_LENGTH *) NULL;
1530 
1531   /* return new font number and exit */
1532   font(*style) = font_count;
1533   SetGap(space_gap(*style), nobreak(space_gap(*style)), FALSE, TRUE,
1534     FIXED_UNIT, EDGE_MODE, font_spacewidth(new));
1535   debug2(DFT, D,"FontChange returning (scaled) %d (XHeight2 = %d)",
1536     font(*style), font_xheight2(finfo[font(*style)].font_table));
1537   /* FontDebug(); */
1538 } /* end FontChange */
1539 
1540 
1541 /*****************************************************************************/
1542 /*                                                                           */
1543 /*  FULL_LENGTH FontKernLength(FONT_NUM fnum, FULL_CHAR *unacc_map,          */
1544 /*    FULL_CHAR ch1, FULL_CHAR ch2)                                          */
1545 /*                                                                           */
1546 /*  Set res to the kern length between ch1 and ch2 in font fnum, or 0 if     */
1547 /*  none.                                                                    */
1548 /*                                                                           */
1549 /*  Parameter unacc_map is the mapping from characters to their unaccented   */
1550 /*  versions.  If no kerning data is available for ch1 and ch2, then their   */
1551 /*  unaccented versions are used instead.                                    */
1552 /*                                                                           */
1553 /*****************************************************************************/
1554 
1555 /* *** old version which just used the unaccented characters
1556 FULL_LENGTH FontKernLength(FONT_NUM fnum, FULL_CHAR *unacc_map,
1557   FULL_CHAR ch1, FULL_CHAR ch2)
1558 {
1559   FULL_LENGTH res;  int ua_ch1, ua_ch2, i, j;
1560   ua_ch1 = unacc_map[ch1];
1561   ua_ch2 = unacc_map[ch2];
1562   i = finfo[fnum].kern_table[ua_ch1];
1563   if( i == 0 )  res = 0;
1564   else
1565   { FULL_CHAR *kc = finfo[fnum].kern_chars;
1566     for( j = i;  kc[j] > ua_ch2;  j++ );
1567     res = (kc[j] == ua_ch2) ?
1568       finfo[fnum].kern_sizes[finfo[fnum].kern_value[j]] : 0;
1569   }
1570   return res;
1571 }
1572 *** */
1573 
FontKernLength(FONT_NUM fnum,FULL_CHAR * unacc_map,FULL_CHAR ch1,FULL_CHAR ch2)1574 FULL_LENGTH FontKernLength(FONT_NUM fnum, FULL_CHAR *unacc_map,
1575   FULL_CHAR ch1, FULL_CHAR ch2)
1576 {
1577   int ua_ch1, ua_ch2, i, j;
1578   FULL_CHAR *kc = finfo[fnum].kern_chars;
1579 
1580   /* search for a kern pair of the original characters */
1581   i = finfo[fnum].kern_table[ch1];
1582   if( i > 0 )
1583   {
1584     for( j = i;  kc[j] > ch2;  j++ );
1585     if( kc[j] == ch2 )
1586       return finfo[fnum].kern_sizes[finfo[fnum].kern_value[j]];
1587   }
1588 
1589   /* no luck, so search for a kern pair of their unaccented versions */
1590   ua_ch1 = unacc_map[ch1];
1591   ua_ch2 = unacc_map[ch2];
1592   if( ua_ch1 != ch1 || ua_ch2 != ch2 )
1593   {
1594     i = finfo[fnum].kern_table[ua_ch1];
1595     if( i > 0 )
1596     {
1597       for( j = i;  kc[j] > ua_ch2;  j++ );
1598       if( kc[j] == ua_ch2 )
1599 	return finfo[fnum].kern_sizes[finfo[fnum].kern_value[j]];
1600     }
1601   }
1602 
1603   /* no luck again, so return 0 */
1604   return 0;
1605 } /* end FontKernLength */
1606 
1607 
1608 /*@::FontWordSize()@**********************************************************/
1609 /*                                                                           */
1610 /*  FontWordSize(x)                                                          */
1611 /*                                                                           */
1612 /*  Calculate the horizontal and vertical size of WORD or QWORD x, including */
1613 /*  the effect of ligature sequences but not replacing them with ligatures.  */
1614 /*                                                                           */
1615 /*****************************************************************************/
1616 
FontWordSize(OBJECT x)1617 void FontWordSize(OBJECT x)
1618 { FULL_CHAR *p, *q, *a, *b, *lig, *unacc, *acc;  OBJECT tmp;
1619   FULL_CHAR buff[MAX_BUFF];  MAPPING m;
1620   int r, u, d, ksize; struct metrics *fnt;
1621   debug2(DFT, DD, "FontWordSize( %s ), font = %d", string(x), word_font(x));
1622   assert( is_word(type(x)), "FontWordSize: !is_word(type(x))!" );
1623 
1624   p = string(x);
1625   q = buff;
1626   if( *p )
1627   { if( word_font(x) < 1 || word_font(x) > font_count )
1628       Error(37, 56, "no current font at word %s", FATAL, &fpos(x), string(x));
1629     if( word_colour(x) == 0 && BackEnd->colour_avail )
1630       Error(37, 57, "no current colour at word %s", FATAL, &fpos(x), string(x));
1631     if( word_language(x) == 0 )
1632       Error(37, 58, "no current language at word %s", FATAL,&fpos(x),string(x));
1633     fnt = finfo[word_font(x)].size_table;
1634     lig = finfo[word_font(x)].lig_table;
1635     m = font_mapping(finfo[word_font(x)].font_table);
1636     unacc = MapTable[m]->map[MAP_UNACCENTED];
1637     acc   = MapTable[m]->map[MAP_ACCENT];
1638     d = u = r = 0;
1639     do
1640     {
1641       /* check for missing glyph (lig[] == 1) or ligatures (lig[] > 1) */
1642       debug2(DFT, DD, "  examining `%c' lig = %d", *p, lig[*p]);
1643       if( lig[*q = *p++] )
1644       {
1645 	if( lig[*q] == 1 )
1646 	{ tmp = MakeWord(QWORD, STR_SPACE, &fpos(x));
1647 	  string(tmp)[0] = *q;
1648 	  /* bug fix: unaccented version exists if unacc differs from self */
1649 	  if( unacc[*q] != *q )
1650 	  {
1651 	    debug2(DFT, DD, "  unacc[%c] = `%c'", *q, unacc[*q]);
1652 	    fnt[*q].up = fnt[unacc[*q]].up;
1653 	    fnt[*q].down = fnt[unacc[*q]].down;
1654 	    fnt[*q].left = fnt[unacc[*q]].left;
1655 	    fnt[*q].right = fnt[unacc[*q]].right;
1656 	    fnt[*q].last_adjust = fnt[unacc[*q]].last_adjust;
1657 	    lig[*q] = 0;
1658 	  }
1659 	  else
1660 	  {
1661 	    debug1(DFT, DD, "  unacc[%c] = 0, replacing by space", *q);
1662 	    Error(37, 60, "character %s replaced by space (it has no glyph in font %s)",
1663 	      WARN, &fpos(x),
1664 	      StringQuotedWord(tmp), FontFamilyAndFace(word_font(x)));
1665 	    *(p-1) = *q = CH_SPACE;
1666 	  }
1667 	  Dispose(tmp);
1668 	}
1669 	else if( word_ligatures(x) )
1670 	{
1671 	  debug1(DFT, DD, "  processing ligature beginning at %c", *q);
1672 	  a = &lig[ lig[*(p-1)] + MAX_CHARS ];
1673 	  while( *a++ == *(p-1) )
1674 	  { b = p;
1675 	    while( *a == *b && *(a+1) != '\0' && *b != '\0' )  a++, b++;
1676 	    if( *(a+1) == '\0' )
1677 	    { *q = *a;
1678 	      p = b;
1679 	      break;
1680 	    }
1681 	    else
1682 	    { while( *++a );
1683 	      a++;
1684 	    }
1685 	  }
1686 	}
1687 	else
1688 	  debug1(DFT, DD, "  ignoring ligature beginning at %c", *q);
1689       }
1690 
1691       /* accumulate size of *q */
1692       if( fnt[*q].up   > u )  u = fnt[*q].up;
1693       if( fnt[*q].down < d )  d = fnt[*q].down;
1694       r += fnt[*q++].right;
1695     } while( *p );
1696     *q = '\0';
1697 
1698     /* adjust for last character */
1699     r += fnt[*(q-1)].last_adjust;
1700 
1701     /* add kern lengths to r */
1702     for( p = buff, q = p+1;  *q;  p++, q++ )
1703     { ksize = FontKernLength(word_font(x), unacc, *p, *q);
1704       debugcond3(DFT, DD, ksize != 0, "  FontKernLength(fnum, %c, %c) = %d",
1705 	*p, *q, ksize);
1706       r += ksize;
1707     }
1708 
1709     /* set sizes of x */
1710     back(x, COLM) = 0;
1711     fwd(x, COLM)  = r;
1712     if( word_strut(x) )
1713     {
1714       int vadjust;
1715       vadjust = font_bbox_ury(finfo[word_font(x)].font_table);
1716       u = find_max(u, vadjust);
1717       vadjust = font_bbox_lly(finfo[word_font(x)].font_table);
1718       d = find_min(d, vadjust);
1719     }
1720     if( word_baselinemark(x) )
1721     { int vadjust = font_xheight2(finfo[word_font(x)].font_table);
1722       back(x, ROWM) = u + vadjust;
1723       fwd(x, ROWM)  = -d - vadjust;
1724     }
1725     else
1726     {
1727       back(x, ROWM) = u;
1728       fwd(x, ROWM)  = -d;
1729     }
1730   }
1731   else back(x, COLM) = fwd(x, COLM) = back(x, ROWM) = fwd(x, ROWM) = 0;
1732   debug4(DFT, DD, "FontWordSize returning %hd %hd %hd %hd",
1733 	  back(x, COLM), fwd(x, COLM), back(x, ROWM), fwd(x, ROWM));
1734 } /* end FontWordSize */
1735 
1736 
1737 /*@::FontSize(), FontHalfXHeight(), FontEncoding(), FontName()@***************/
1738 /*                                                                           */
1739 /*  FULL_LENGTH FontSize(fnum, x)                                            */
1740 /*                                                                           */
1741 /*  Return the size of this font.  x is for error messages only, and may be  */
1742 /*  nilobj if fnum is certain not to be NO_FONT.                             */
1743 /*                                                                           */
1744 /*****************************************************************************/
1745 
FontSize(FONT_NUM fnum,OBJECT x)1746 FULL_LENGTH FontSize(FONT_NUM fnum, OBJECT x)
1747 { debug1(DFT, D, "FontSize( %d )", fnum);
1748   assert( fnum <= font_count, "FontSize!" );
1749   if( fnum <= 0 )
1750     Error(37, 61, "no current font at this point", FATAL, &fpos(x));
1751   debug1(DFT, D, "FontSize returning %d", font_size(finfo[fnum].font_table));
1752   return font_size(finfo[fnum].font_table);
1753 } /* end FontSize */
1754 
1755 
1756 /*****************************************************************************/
1757 /*                                                                           */
1758 /*  FULL_LENGTH FontHalfXHeight(fnum)                                        */
1759 /*                                                                           */
1760 /*  Return the xheight2 value of this font.                                  */
1761 /*                                                                           */
1762 /*****************************************************************************/
1763 
FontHalfXHeight(FONT_NUM fnum)1764 FULL_LENGTH FontHalfXHeight(FONT_NUM fnum)
1765 { debug1(DFT, DD, "FontHalfXHeight( %d )", fnum);
1766   assert( fnum <= font_count, "FontHalfXHeight!" );
1767   debug1(DFT, DD, "FontHalfXHeight returning %d",
1768     font_xheight2(finfo[fnum].font_table));
1769   return font_xheight2(finfo[fnum].font_table);
1770 } /* end FontHalfXHeight */
1771 
1772 
1773 /*****************************************************************************/
1774 /*                                                                           */
1775 /*  MAPPING FontMapping(fnum, xfpos)                                         */
1776 /*                                                                           */
1777 /*  Return the character mapping of this font, to use for small caps, etc.   */
1778 /*  xfpos is the file position for error messages.                           */
1779 /*                                                                           */
1780 /*****************************************************************************/
1781 
FontMapping(FONT_NUM fnum,FILE_POS * xfpos)1782 MAPPING FontMapping(FONT_NUM fnum, FILE_POS *xfpos)
1783 { debug1(DFT, DD, "FontMapping( %d )", fnum);
1784   assert( fnum <= font_count, "FontMapping!" );
1785   if( fnum <= 0 )
1786     Error(37, 62, "no current font at this point", FATAL, xfpos);
1787   debug1(DFT, DD, "FontMapping returning %d",
1788     font_mapping(finfo[fnum].font_table));
1789   return font_mapping(finfo[fnum].font_table);
1790 } /* end FontMapping */
1791 
1792 
1793 /*****************************************************************************/
1794 /*                                                                           */
1795 /*  FULL_CHAR *FontName(fnum)                                                */
1796 /*                                                                           */
1797 /*  Return the short PostScript name of this font.                           */
1798 /*                                                                           */
1799 /*****************************************************************************/
1800 
FontName(FONT_NUM fnum)1801 FULL_CHAR *FontName(FONT_NUM fnum)
1802 { debug1(DFT, D, "FontName( %d )", fnum);
1803   assert( fnum <= font_count, "FontName!" );
1804   debug1(DFT, D, "FontName returning %s", string(finfo[fnum].font_table));
1805   return string(finfo[fnum].font_table);
1806 } /* end FontName */
1807 
1808 
1809 /*@::FontFamily(), FontFace@**************************************************/
1810 /*                                                                           */
1811 /*  FULL_CHAR *FontFamilyAndFace(fnum)                                       */
1812 /*                                                                           */
1813 /*  Return a static string of the current font family and face.              */
1814 /*                                                                           */
1815 /*****************************************************************************/
1816 
FontFamily(FONT_NUM fnum)1817 FULL_CHAR *FontFamily(FONT_NUM fnum)
1818 { OBJECT face, family;
1819   debug1(DFT, D, "FontFamily( %d )", fnum);
1820   assert( fnum <= font_count, "FontFamiliy!" );
1821   Parent(face, Up(finfo[fnum].font_table));
1822   Parent(family, Up(face));
1823   debug1(DFT, D, "FontFamily returning %s", string(family));
1824   return string(family);
1825 } /* end FontFamilyAndFace */
1826 
1827 
FontFace(FONT_NUM fnum)1828 FULL_CHAR *FontFace(FONT_NUM fnum)
1829 { OBJECT face, family;
1830   debug1(DFT, D, "FontFacec( %d )", fnum);
1831   assert( fnum <= font_count, "FontFamiliy!" );
1832   Parent(face, Up(finfo[fnum].font_table));
1833   Parent(family, Up(face));
1834   debug1(DFT, D, "FontFace returning %s", string(face));
1835   return string(face);
1836 } /* end FontFamilyAndFace */
1837 
1838 
1839 /*@::FontFamilyAndFace(), FontPrintAll()@*************************************/
1840 /*                                                                           */
1841 /*  FULL_CHAR *FontFamilyAndFace(fnum)                                       */
1842 /*                                                                           */
1843 /*  Return a static string of the current font family and face.              */
1844 /*                                                                           */
1845 /*****************************************************************************/
1846 
FontFamilyAndFace(FONT_NUM fnum)1847 FULL_CHAR *FontFamilyAndFace(FONT_NUM fnum)
1848 { OBJECT face, family; static FULL_CHAR buff[80];
1849   debug1(DFT, D, "FontFamilyAndFace( %d )", fnum);
1850   assert( fnum <= font_count, "FontName!" );
1851   Parent(face, Up(finfo[fnum].font_table));
1852   Parent(family, Up(face));
1853   if( StringLength(string(family)) + StringLength(string(face)) + 1 > 80 )
1854     Error(37, 63, "family and face names %s %s are too long",
1855       FATAL, no_fpos, string(family), string(face));
1856   StringCopy(buff, string(family));
1857   StringCat(buff, STR_SPACE);
1858   StringCat(buff, string(face));
1859   debug1(DFT, D, "FontName returning %s", buff);
1860   return buff;
1861 } /* end FontFamilyAndFace */
1862 
1863 
1864 /*****************************************************************************/
1865 /*                                                                           */
1866 /*  FontPrintAll(fp)                   	                                     */
1867 /*                                                                           */
1868 /*  Print all font encoding commands on output file fp                       */
1869 /*                                                                           */
1870 /*****************************************************************************/
1871 
FontPrintAll(FILE * fp)1872 void FontPrintAll(FILE *fp)
1873 { OBJECT family, face, first_size, ps_name, link, flink;
1874   assert(font_root!=nilobj && type(font_root)==ACAT, "FontDebug: font_root!");
1875   debug0(DFT, DD, "FontPrintAll(fp)");
1876   for( link = Down(font_root);  link != font_root;  link = NextDown(link) )
1877   { Child(family, link);
1878     assert( is_word(type(family)), "FontPrintAll: family!" );
1879     for( flink = Down(family);  flink != family;  flink = NextDown(flink) )
1880     { Child(face, flink);
1881       assert( is_word(type(face)), "FontPrintAll: face!" );
1882       assert( Down(face) != face && NextDown(Down(face)) != face &&
1883 	NextDown(NextDown(Down(face))) != face, "FontDebug: Down(face)!");
1884       Child(ps_name, Down(face));
1885       assert( is_word(type(ps_name)), "FontPrintAll: ps_name!" );
1886       Child(first_size, NextDown(NextDown(Down(face))));
1887       assert( is_word(type(first_size)), "FontPrintAll: first_size!" );
1888       if( font_recoded(face) )
1889       { fprintf(fp, "/%s%s %s /%s LoutRecode%s",
1890 	  string(ps_name), string(first_size),
1891 	  MapEncodingName(font_mapping(face)), string(ps_name),
1892 	  (char *) STR_NEWLINE);
1893 	fprintf(fp, "/%s { /%s%s LoutFont } def%s", string(first_size),
1894 	  string(ps_name), string(first_size), (char *) STR_NEWLINE);
1895       }
1896       else fprintf(fp, "/%s { /%s LoutFont } def%s", string(first_size),
1897 	  string(ps_name), (char *) STR_NEWLINE);
1898     }
1899   }
1900   fputs((char *) STR_NEWLINE, fp);
1901   debug0(DFT, DD, "FontPrintAll returning.");
1902 } /* end FontPrintAll */
1903 
1904 
1905 /*@@**************************************************************************/
1906 /*                                                                           */
1907 /*  FontPrintPageSetup(fp)             	                                     */
1908 /*                                                                           */
1909 /*  Print all font encoding commands needed for the current page onto fp.    */
1910 /*                                                                           */
1911 /*****************************************************************************/
1912 
FontPrintPageSetup(FILE * fp)1913 void FontPrintPageSetup(FILE *fp)
1914 { OBJECT face, first_size, ps_name, link;
1915   assert(font_root!=nilobj && type(font_root)==ACAT, "FontDebug: font_root!");
1916   assert(font_used!=nilobj && type(font_used)==ACAT, "FontDebug: font_used!");
1917   debug0(DFT, DD, "FontPrintPageSetup(fp)");
1918   for( link = Down(font_used);  link != font_used;  link = NextDown(link) )
1919   {
1920     Child(face, link);
1921     assert( is_word(type(face)), "FontPrintPageSetup: face!" );
1922     assert( Down(face) != face, "FontDebug: Down(face)!");
1923 
1924     /* print font encoding command */
1925     Child(first_size, NextDown(NextDown(Down(face))));
1926     assert( is_word(type(first_size)), "FontPrintPageSetup: first_size!" );
1927     Child(ps_name, Down(face));
1928     assert( is_word(type(ps_name)), "FontPrintPageSetup: ps_name!" );
1929     BackEnd->PrintPageSetupForFont(face, font_curr_page,
1930       string(ps_name), string(first_size));
1931   }
1932   debug0(DFT, DD, "FontPrintPageSetup returning.");
1933 } /* end FontPrintPageSetup */
1934 
1935 
1936 /*@@**************************************************************************/
1937 /*                                                                           */
1938 /*  FontPrintPageResources(fp)        	                                     */
1939 /*                                                                           */
1940 /*  Print all page resources (i.e. fonts needed or supplied) onto fp.        */
1941 /*                                                                           */
1942 /*****************************************************************************/
1943 
FontPrintPageResources(FILE * fp)1944 void FontPrintPageResources(FILE *fp)
1945 { OBJECT face, ps_name, link, pface, pname, plink;
1946   BOOLEAN first;
1947   assert(font_root!=nilobj && type(font_root)==ACAT, "FontDebug: font_root!");
1948   assert(font_used!=nilobj && type(font_used)==ACAT, "FontDebug: font_used!");
1949   debug0(DFT, DD, "FontPrintPageResources(fp)");
1950   first = TRUE;
1951   for( link = Down(font_used);  link != font_used;  link = NextDown(link) )
1952   {
1953     Child(face, link);
1954     assert( is_word(type(face)), "FontPrintPageResources: face!" );
1955     assert( Down(face) != face, "FontDebug: Down(face)!");
1956     Child(ps_name, Down(face));
1957     assert( is_word(type(ps_name)), "FontPrintPageResources: ps_name!" );
1958 
1959     /* make sure this ps_name has not been printed before (ugly, I know). */
1960     /* Repeats arise when the font appears twice in the database under    */
1961     /* different family-face names, perhaps because of sysnonyms like     */
1962     /* Italic and Slope, or perhaps because of different encoding vectors */
1963     for( plink = Down(font_used);  plink != link;  plink = NextDown(plink) )
1964     {
1965       Child(pface, plink);
1966       Child(pname, Down(pface));
1967       if( StringEqual(string(pname), string(ps_name)) )
1968 	break;
1969     }
1970     if( plink == link )
1971     {
1972       /* not seen before, so print it */
1973       BackEnd->PrintPageResourceForFont(string(ps_name), first);
1974       first = FALSE;
1975     }
1976   }
1977   debug0(DFT, DD, "FontPrintPageResources returning.");
1978 } /* end FontPrintPageResources */
1979 
1980 
1981 /*@@**************************************************************************/
1982 /*                                                                           */
1983 /*  FontAdvanceCurrentPage()        	                                     */
1984 /*                                                                           */
1985 /*  Advance the current page.                                                */
1986 /*                                                                           */
1987 /*****************************************************************************/
1988 
FontAdvanceCurrentPage(void)1989 void FontAdvanceCurrentPage(void)
1990 { debug0(DFT, DD, "FontAdvanceCurrentPage()");
1991   while( Down(font_used) != font_used )  DeleteLink(Down(font_used));
1992   font_curr_page++;
1993   debug0(DFT, DD, "FontAdvanceCurrentPage() returning.");
1994 } /* end FontAdvanceCurrentPage */
1995 
1996 
1997 /*@::FontPageUsed()@**********************************************************/
1998 /*                                                                           */
1999 /*  OBJECT FontPageUsed(face)                                                */
2000 /*                                                                           */
2001 /*  Declares that font face is used on the current page.                     */
2002 /*                                                                           */
2003 /*****************************************************************************/
2004 
FontPageUsed(OBJECT face)2005 void FontPageUsed(OBJECT face)
2006 { debug1(DFT, DD, "FontPageUsed(%d)", font_num(face));
2007   assert( font_page(face) < font_curr_page, "FontPageUsed!" );
2008   Link(font_used, face);
2009   font_page(face) = font_curr_page;
2010   debug0(DFT, DD, "FontPageUsed returning");
2011 } /* end FontPageUsed */
2012 
2013 
2014 /*@::FontNeeded()@************************************************************/
2015 /*                                                                           */
2016 /*  OBJECT FontNeeded(fp)                                                    */
2017 /*                                                                           */
2018 /*  Writes font needed resources onto file out_fp.  Returns TRUE if none.    */
2019 /*  Now that we are using a database, every font that is actually loaded     */
2020 /*  is really needed.                                                        */
2021 /*                                                                           */
2022 /*****************************************************************************/
2023 
FontNeeded(FILE * fp)2024 BOOLEAN FontNeeded(FILE *fp)
2025 { BOOLEAN first_need = TRUE;
2026   OBJECT link, flink, family, face, ps_name;
2027   for( link = Down(font_root); link != font_root; link = NextDown(link) )
2028   { Child(family, link);
2029     for( flink = Down(family);  flink != family;  flink = NextDown(flink) )
2030     { Child(face, flink);
2031       Child(ps_name, Down(face));
2032       assert( is_word(type(ps_name)), "FontPrintPageResources: ps_name!" );
2033       fprintf(fp, "%s font %s%s",
2034 	first_need ? "%%DocumentNeededResources:" : "%%+", string(ps_name),
2035 	(char *) STR_NEWLINE);
2036       first_need = FALSE;
2037     }
2038   }
2039   return first_need;
2040 } /* end FontNeeded */
2041 
2042 
2043 /*@::FontGlyphHeight()@*******************************************************/
2044 /*                                                                           */
2045 /*  FULL_LENGTH FontGlyphHeight(fnum, chr)                                   */
2046 /*                                                                           */
2047 /*  Contributed as part of margin kerning by Ludovic Courtes.                */
2048 /*                                                                           */
2049 /*  Return the height of the glyph that corresponds to character chr in      */
2050 /*  font fnum.                                                               */
2051 /*                                                                           */
2052 /*****************************************************************************/
2053 
FontGlyphHeight(FONT_NUM fnum,FULL_CHAR chr)2054 FULL_LENGTH FontGlyphHeight(FONT_NUM fnum, FULL_CHAR chr)
2055 {
2056   struct metrics *fnt;
2057 
2058   assert ((fnum >= 1) && (fnum <= font_count), "FontGlyphHeight");
2059 
2060   fnt = finfo[fnum].size_table;
2061   return (fnt ? fnt[chr].up - fnt[chr].down : 0);
2062 }
2063 
2064 
2065 /*****************************************************************************/
2066 /*                                                                           */
2067 /*  FULL_LENGTH FontGlyphWidth(fnum, chr)                                    */
2068 /*                                                                           */
2069 /*  Contributed as part of margin kerning by Ludovic Courtes.                */
2070 /*                                                                           */
2071 /*  Return the width of the glyph that corresponds to character chr in       */
2072 /*  font fnum.                                                               */
2073 /*                                                                           */
2074 /*****************************************************************************/
2075 
FontGlyphWidth(FONT_NUM fnum,FULL_CHAR chr)2076 FULL_LENGTH FontGlyphWidth(FONT_NUM fnum, FULL_CHAR chr)
2077 {
2078   struct metrics *fnt;
2079 
2080   assert ((fnum >= 1) && (fnum <= font_count), "FontGlyphWidth");
2081 
2082   fnt = finfo[fnum].size_table;
2083   return (fnt ? fnt[chr].right - fnt[chr].left : 0);
2084 }
2085