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