| 1 | |
| 2 | /* Copyright (c) 1999, 2000, 2002, 2005 by Arkkra Enterprises */ |
| 3 | /* All rights reserved */ |
| 4 | |
| 5 | /* This file contains functions related to fonts, |
| 6 | * both user defined and native PostScript fonts. |
| 7 | */ |
| 8 | |
| 9 | #include <string.h> |
| 10 | #include "defines.h" |
| 11 | #include "structs.h" |
| 12 | #include "globals.h" |
| 13 | |
| 14 | |
| 15 | /* The font size info is machine generated, so refer to it here. */ |
| 16 | /* This gives the initialization for FONTINFO structs for all fonts. */ |
| 17 | extern struct FONTINFO Fontinfo[MAXFONTS]; |
| 18 | |
| 19 | /* map font names to numbers. */ |
| 20 | static struct FONTMAP { |
| 21 | char *fontname; /* abbreviated (similar to troff-style) |
| 22 | * or full font name */ |
| 23 | char findex; /* font number */ |
| 24 | } Font_table[] = { |
| 25 | /*====== this table must be sorted alphabetically to allow |
| 26 | *====== binary search!!!!! */ |
| 27 | { "AB", FONT_AB }, |
| 28 | { "AI", FONT_AI }, |
| 29 | { "AR", FONT_AR }, |
| 30 | { "AX", FONT_AX }, |
| 31 | { "BB", FONT_BB }, |
| 32 | { "BI", FONT_BI }, |
| 33 | { "BR", FONT_BR }, |
| 34 | { "BX", FONT_BX }, |
| 35 | { "CB", FONT_CB }, |
| 36 | { "CI", FONT_CI }, |
| 37 | { "CR", FONT_CR }, |
| 38 | { "CX", FONT_CX }, |
| 39 | { "HB", FONT_HB }, |
| 40 | { "HI", FONT_HI }, |
| 41 | { "HR", FONT_HR }, |
| 42 | { "HX", FONT_HX }, |
| 43 | { "NB", FONT_NB }, |
| 44 | { "NI", FONT_NI }, |
| 45 | { "NR", FONT_NR }, |
| 46 | { "NX", FONT_NX }, |
| 47 | { "PB", FONT_PB }, |
| 48 | { "PI", FONT_PI }, |
| 49 | { "PR", FONT_PR }, |
| 50 | { "PX", FONT_PX }, |
| 51 | { "TB", FONT_TB }, |
| 52 | { "TI", FONT_TI }, |
| 53 | { "TR", FONT_TR }, |
| 54 | { "TX", FONT_TX }, |
| 55 | { "avantgarde bold", FONT_AB }, |
| 56 | { "avantgarde boldital", FONT_AX }, |
| 57 | { "avantgarde ital", FONT_AI }, |
| 58 | { "avantgarde rom", FONT_AR }, |
| 59 | { "bookman bold", FONT_BB }, |
| 60 | { "bookman boldital", FONT_BX }, |
| 61 | { "bookman ital", FONT_BI }, |
| 62 | { "bookman rom", FONT_BR }, |
| 63 | { "courier bold", FONT_CB }, |
| 64 | { "courier boldital", FONT_CX }, |
| 65 | { "courier ital", FONT_CI }, |
| 66 | { "courier rom", FONT_CR }, |
| 67 | { "helvetica bold", FONT_HB }, |
| 68 | { "helvetica boldital", FONT_HX }, |
| 69 | { "helvetica ital", FONT_HI }, |
| 70 | { "helvetica rom", FONT_HR }, |
| 71 | { "newcentury bold", FONT_NB }, |
| 72 | { "newcentury boldital", FONT_NX }, |
| 73 | { "newcentury ital", FONT_NI }, |
| 74 | { "newcentury rom", FONT_NR }, |
| 75 | { "palatino bold", FONT_PB }, |
| 76 | { "palatino boldital", FONT_PX }, |
| 77 | { "palatino ital", FONT_PI }, |
| 78 | { "palatino rom", FONT_PR }, |
| 79 | { "times bold", FONT_TB }, |
| 80 | { "times boldital", FONT_TX }, |
| 81 | { "times ital", FONT_TI }, |
| 82 | { "times rom", FONT_TR } |
| 83 | }; |
| 84 | |
| 85 | /* Strings to look for in a fontfile */ |
| 86 | char *Mup_name = "Mup font name:"; |
| 87 | char *PostScript_name = "PostScript font name:"; |
| 88 | char *PS_definition = "PostScript:"; |
| 89 | char *Size_data = "Size data:"; |
| 90 | |
| 91 | /* static functions */ |
| 92 | static int fncmp P((const void *fn1, const void * fn2)); |
| 93 | static char *get_expected P((FILE *fontfile_p, char *filename, char *expected, |
| 94 | int *lineno_p)); |
| 95 | static char *get_noncomment P((FILE *fontfile_p, int *lineno_p)); |
| 96 | \f |
| 97 | |
| 98 | /* given a font number, return its index into Fontinfo table. FONT_TR |
| 99 | * is the first entry in the table. |
| 100 | * If the font number given is out of range, pfatal. |
| 101 | */ |
| 102 | |
| 103 | int |
| 104 | font_index(font) |
| 105 | |
| 106 | int font; /* which font */ |
| 107 | |
| 108 | { |
| 109 | if ((font < 0) || (font >= MAXFONTS)) { |
| 110 | pfatal("font %d out of range", font); |
| 111 | } |
| 112 | /* offset relative to first valid font */ |
| 113 | font = font - FONT_TR; |
| 114 | |
| 115 | return(font); |
| 116 | } |
| 117 | \f |
| 118 | |
| 119 | /* given a fontname, return its number, or FONT_UNKNOWN */ |
| 120 | |
| 121 | int |
| 122 | lookup_font(fontname) |
| 123 | |
| 124 | char *fontname; |
| 125 | |
| 126 | { |
| 127 | struct FONTMAP *finfo_p; |
| 128 | |
| 129 | |
| 130 | if ((finfo_p = (struct FONTMAP *) bsearch(fontname, Font_table, |
| 131 | NUMELEM(Font_table), sizeof(struct FONTMAP), fncmp)) |
| 132 | != (struct FONTMAP *) 0) { |
| 133 | return(finfo_p->findex); |
| 134 | } |
| 135 | else { |
| 136 | return(FONT_UNKNOWN); |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | |
| 141 | |
| 142 | /* font name comparision function for use by bsearch() */ |
| 143 | |
| 144 | static int |
| 145 | fncmp(fn1, fn2) |
| 146 | |
| 147 | #ifdef __STDC__ |
| 148 | const void *fn1; /* font name to check */ |
| 149 | const void *fn2; /* pointer to FONTMAP to compare with. declare as char * |
| 150 | * since that's what bsearch() thinks it gives us, |
| 151 | * then we cast appropriately */ |
| 152 | #else |
| 153 | char *fn1; /* font name to check */ |
| 154 | char *fn2; /* pointer to FONTMAP to compare with. declare as char * |
| 155 | * since that's what bsearch() thinks it gives us, then we |
| 156 | * cast appropriately */ |
| 157 | #endif |
| 158 | |
| 159 | { |
| 160 | return(strcmp(fn1, ((struct FONTMAP *) fn2)->fontname)); |
| 161 | } |
| 162 | \f |
| 163 | |
| 164 | /* Given a font number, return its name. We don't have to do this too often, |
| 165 | * it's a simple int compare, and we only need to look through |
| 166 | * abbreviated names, so just do linear search. */ |
| 167 | |
| 168 | char * |
| 169 | fontnum2name(font) |
| 170 | |
| 171 | int font; |
| 172 | |
| 173 | { |
| 174 | int f; |
| 175 | int elements; |
| 176 | |
| 177 | /* divide by 2 because only need to check abbreviations */ |
| 178 | elements = NUMELEM(Font_table) / 2; |
| 179 | for (f = 0; f < elements; f++) { |
| 180 | if (Font_table[f].findex == font) { |
| 181 | return(Font_table[f].fontname); |
| 182 | } |
| 183 | } |
| 184 | return("unknown"); |
| 185 | } |
| 186 | \f |
| 187 | |
| 188 | /* This handles a fontfile, reading it in, validating its contents, |
| 189 | * and saving the information in the Fontinfo array. |
| 190 | */ |
| 191 | |
| 192 | void |
| 193 | parse_font_file(filename) |
| 194 | |
| 195 | char *filename; |
| 196 | |
| 197 | { |
| 198 | FILE *fontfile_p; |
| 199 | char *name; /* Mup font name */ |
| 200 | char *ps_name; /* PostScript font name */ |
| 201 | int findex; /* which font is being defined */ |
| 202 | int c; /* character index */ |
| 203 | int code; /* "ASCII" code value */ |
| 204 | int width, height, ascent; |
| 205 | char *buffer; /* line read from file */ |
| 206 | int lineno; |
| 207 | int max_height, max_ascent; |
| 208 | |
| 209 | |
| 210 | debug(2, "parse_font_file(%s)", filename); |
| 211 | |
| 212 | if ((fontfile_p = find_file(&filename)) == (FILE *) 0) { |
| 213 | l_yyerror(Curr_filename, yylineno, "can't open '%s'", filename); |
| 214 | return; |
| 215 | } |
| 216 | |
| 217 | /* first line of file is expected to contain the Mup font name */ |
| 218 | lineno = 0; |
| 219 | if ((name = get_expected(fontfile_p, filename, Mup_name, &lineno)) |
| 220 | != (char *) 0) { |
| 221 | if ((findex = lookup_font(name)) == FONT_UNKNOWN) { |
| 222 | l_yyerror(filename, lineno, |
| 223 | "'%s' is not a valid Mup font name", name); |
| 224 | return; |
| 225 | } |
| 226 | findex = font_index(findex); |
| 227 | |
| 228 | if (Fontinfo[findex].fontfile != (FILE *) 0) { |
| 229 | l_yyerror(filename, lineno, |
| 230 | "Font '%s' redefined more than once", name); |
| 231 | return; |
| 232 | } |
| 233 | /* Save the file pointer, since we'll need to read the rest of |
| 234 | * the file to put into the Mup output */ |
| 235 | Fontinfo[findex].fontfile = fontfile_p; |
| 236 | } |
| 237 | else { |
| 238 | return; |
| 239 | } |
| 240 | |
| 241 | /* Next line of file is expected to contain the PostScript font name */ |
| 242 | if ((ps_name = get_expected(fontfile_p, filename, PostScript_name, &lineno)) |
| 243 | != (char *) 0) { |
| 244 | if (strlen(ps_name) == 0 ) { |
| 245 | l_yyerror(filename, lineno, |
| 246 | "No PostScript font name value given"); |
| 247 | return; |
| 248 | } |
| 249 | Fontinfo[findex].ps_name = ps_name; |
| 250 | } |
| 251 | else { |
| 252 | return; |
| 253 | } |
| 254 | |
| 255 | /* Next line of file is expected to contain the Size data line */ |
| 256 | if ((ps_name = get_expected(fontfile_p, filename, Size_data, &lineno)) |
| 257 | == (char *) 0) { |
| 258 | return; |
| 259 | } |
| 260 | |
| 261 | max_height = max_ascent = 0; |
| 262 | for (c = FIRST_CHAR; c < FIRST_CHAR + CHARS_IN_FONT - 1; c++) { |
| 263 | buffer = get_noncomment(fontfile_p, &lineno); |
| 264 | if ( sscanf(buffer, "%d %d %d %d", |
| 265 | &code, &width, &height, &ascent) != 4) { |
| 266 | buffer[strlen(buffer) - 1] = '\0'; |
| 267 | l_yyerror(filename, lineno, |
| 268 | "size data line has incorrect format: '%s'", |
| 269 | buffer); |
| 270 | return; |
| 271 | } |
| 272 | if (c != code) { |
| 273 | l_yyerror(filename, lineno, |
| 274 | "expecting size data for character %d, but got %d instead", |
| 275 | c, code); |
| 276 | return; |
| 277 | } |
| 278 | |
| 279 | /* Because of how backspace works (see comment in defines.h) |
| 280 | * we need to limit width to 0.5 inch for a DEFAULT_SIZE |
| 281 | * character. */ |
| 282 | if (width > 500) { |
| 283 | l_yyerror(filename, lineno, "width must be less than 500"); |
| 284 | return; |
| 285 | } |
| 286 | |
| 287 | /* save size in table */ |
| 288 | code = CHAR_INDEX(code); |
| 289 | Fontinfo[findex].ch_height[code] = height; |
| 290 | Fontinfo[findex].ch_width[code] = width; |
| 291 | Fontinfo[findex].ch_ascent[code] = ascent; |
| 292 | |
| 293 | if (height > max_height) { |
| 294 | max_height = height; |
| 295 | } |
| 296 | if (ascent > max_ascent) { |
| 297 | max_ascent = ascent; |
| 298 | } |
| 299 | } |
| 300 | |
| 301 | Maxfontheight[findex] = (double) max_height / (double) FONTFACTOR; |
| 302 | Maxfontascent[findex] = (double) max_ascent / (double) FONTFACTOR; |
| 303 | |
| 304 | /* Next line of file is expected to contain the PostScript: line */ |
| 305 | if ((ps_name = get_expected(fontfile_p, filename, PS_definition, &lineno)) |
| 306 | == (char *) 0) { |
| 307 | return; |
| 308 | } |
| 309 | } |
| 310 | \f |
| 311 | |
| 312 | /* Read from given file. If next non-comment line starts as expected, |
| 313 | * return a copy of the rest of the line after any white space. |
| 314 | * Otherwise print an error and return 0. |
| 315 | */ |
| 316 | |
| 317 | static char * |
| 318 | get_expected(fontfile_p, filename, expected, lineno_p) |
| 319 | |
| 320 | FILE *fontfile_p; |
| 321 | char *filename; |
| 322 | char *expected; /* line read is expected to start with this */ |
| 323 | int *lineno_p; /* line number where line was found is returned here */ |
| 324 | |
| 325 | { |
| 326 | char *buffer; |
| 327 | char *newstring; |
| 328 | char *p; |
| 329 | |
| 330 | buffer = get_noncomment(fontfile_p, lineno_p); |
| 331 | if (strncmp(buffer, expected, strlen(expected)) != 0) { |
| 332 | l_yyerror(filename, *lineno_p, |
| 333 | "Expecting '%s' in font_file '%s'", expected, filename); |
| 334 | return((char *) 0); |
| 335 | } |
| 336 | |
| 337 | /* skip any leading white space */ |
| 338 | for (buffer += strlen(expected); isspace(*buffer); buffer++) { |
| 339 | ; |
| 340 | } |
| 341 | |
| 342 | /* trim any white space from the end of the string */ |
| 343 | for (p = buffer + strlen(buffer) - 1; p >= buffer; p--) { |
| 344 | if (isspace(*p)) { |
| 345 | *p = '\0'; |
| 346 | } |
| 347 | else { |
| 348 | break; |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | /* make a copy and return it */ |
| 353 | MALLOCA(char, newstring, strlen(buffer) + 1); |
| 354 | strcpy(newstring, buffer); |
| 355 | return(newstring); |
| 356 | } |
| 357 | \f |
| 358 | |
| 359 | /* Read lines from given file until a non-comment line is found, and |
| 360 | * return that line. A comment is a line that has # in its first column. |
| 361 | * Returns a null string on end of file. The line returned is in a static |
| 362 | * buffer overwritten on each call, so caller must save if they need a copy. |
| 363 | */ |
| 364 | |
| 365 | static char * |
| 366 | get_noncomment(file, lineno_p) |
| 367 | |
| 368 | FILE *file; |
| 369 | int *lineno_p; /* line number gets sents in and returned here */ |
| 370 | |
| 371 | { |
| 372 | static char buffer[128]; |
| 373 | |
| 374 | while (fgets(buffer, sizeof(buffer), file) != (char *) 0) { |
| 375 | (*lineno_p)++; |
| 376 | if ( *buffer != '#') { |
| 377 | /* not a comment, so return it */ |
| 378 | return(buffer); |
| 379 | } |
| 380 | } |
| 381 | buffer[0] = '\0'; |
| 382 | return(buffer); |
| 383 | } |
| 384 | \f |
| 385 | |
| 386 | /* return the height for a font in inches for a given size */ |
| 387 | |
| 388 | double |
| 389 | fontheight(font, size) |
| 390 | |
| 391 | int font; |
| 392 | int size; |
| 393 | |
| 394 | { |
| 395 | return( (double) Maxfontheight[font_index(font)] * |
| 396 | ((double) size / (double) DFLT_SIZE) ); |
| 397 | } |
| 398 | |
| 399 | |
| 400 | |
| 401 | /* return the ascent for a font in inches for a given size */ |
| 402 | |
| 403 | double |
| 404 | fontascent(font, size) |
| 405 | |
| 406 | int font; |
| 407 | int size; |
| 408 | |
| 409 | { |
| 410 | return( (double) Maxfontascent[font_index(font)] * |
| 411 | ((double) size / (double) DFLT_SIZE) ); |
| 412 | } |
| 413 | |
| 414 | |
| 415 | |
| 416 | /* return the descent for a font in inches for a given size */ |
| 417 | |
| 418 | double |
| 419 | fontdescent(font, size) |
| 420 | |
| 421 | int font; |
| 422 | int size; |
| 423 | |
| 424 | { |
| 425 | return( fontheight(font, size) - fontascent (font, size) ); |
| 426 | } |