/*@z37.c:Font Service:Declarations@*******************************************/ /* */ /* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.18) */ /* COPYRIGHT (C) 1991, 2000 Jeffrey H. Kingston */ /* */ /* Jeffrey H. Kingston (jeff@cs.usyd.edu.au) */ /* Basser Department of Computer Science */ /* The University of Sydney 2006 */ /* AUSTRALIA */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation; either Version 2, or (at your option) */ /* any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA */ /* */ /* FILE: z37.c */ /* MODULE: Font Service */ /* EXTERNS: FontInit(), FontDefine(), FontChange(), FontWordSize(), */ /* FontSize(), FontHalfXHeight(), FontEncoding(), */ /* FontMapping(), FontFamilyAndFace(), FontNeeded() */ /* */ /* This module implements fonts, using encoding vectors and Adobe font */ /* metrics files (.AFM files, version 2). */ /* */ /*****************************************************************************/ #include "externs.h" #define DEFAULT_XHEIGHT 500 /* the default XHeight if font has none */ #define NO_FONT 0 /* the not-a-font font number */ #define SZ_DFT 1000 /* default lout size is 50p */ #define INIT_FINFO_SIZE 100 /* initial number of sized fonts set aside */ /*****************************************************************************/ /* */ /* these definitions have been placed in "externs" because z24.c needs them */ /* */ /* struct metrics { */ /* SHORT_LENGTH up; */ /* SHORT_LENGTH down; */ /* SHORT_LENGTH left; */ /* SHORT_LENGTH right; */ /* SHORT_LENGTH last_adjust; */ /* }; */ /* */ /* typedef struct font_rec { */ /* struct metrics *size_table; metrics of sized fonts */ /* FULL_CHAR *lig_table; ligatures */ /* OBJECT font_table; record of sized fonts */ /* OBJECT original_font; font rec before resizing */ /* SHORT_LENGTH underline_pos; position of underline */ /* SHORT_LENGTH underline_thick; thickness of underline */ /* unsigned short *kern_table; first kerning chars */ /* FULL_CHAR *kern_chars; second kerning chars */ /* unsigned char *kern_value; points into kern_lengths */ /* SHORT_LENGTH *kern_sizes; sizes of kernings */ /* } FONT_INFO; */ /* */ /*****************************************************************************/ int font_curr_page; /* current page number */ FONT_INFO *finfo; /* all the font table info */ static int finfo_size; /* current finfo array size */ static OBJECT font_root; /* root of tree of fonts */ static OBJECT font_used; /* fonts used on this page */ static FONT_NUM font_count; /* number of sized fonts */ static int font_seqnum; /* unique number for a font */ /*@::FontInit(), FontDebug()@*************************************************/ /* */ /* FontInit() */ /* */ /* Initialise this module. */ /* */ /*****************************************************************************/ void FontInit(void) { debug0(DFT, D, "FontInit()"); font_curr_page = 1; font_count = 0; New(font_root, ACAT); New(font_used, ACAT); font_seqnum = 0; finfo = (FONT_INFO *) malloc(INIT_FINFO_SIZE * sizeof(FONT_INFO)); finfo_size = INIT_FINFO_SIZE; ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 1, INIT_FINFO_SIZE * sizeof(FONT_INFO))); debug0(DFT, D, "FontInit returning."); } /*****************************************************************************/ /* */ /* FontDebug() */ /* */ /* Print out font tree (not currectly used). */ /* */ /*****************************************************************************/ #if DEBUG_ON static void FontDebug(void) { OBJECT family, face, filename, link, flink; int i; assert(font_root!=nilobj && type(font_root)==ACAT, "FontDebug: font_root!"); for( link = Down(font_root); link != font_root; link = NextDown(link) ) { Child(family, link); assert( is_word(type(family)), "FontDebug: family!" ); fprintf(stderr, "family %s:\n", string(family)); for( flink = Down(family); flink != family; flink = NextDown(flink) ) { Child(face, flink); assert( is_word(type(face)), "FontDebug: face!" ); fprintf(stderr, " face %s in file ", string(face)); assert( Down(face) != face, "FontDebug: Down(face)!"); Child(filename, Down(face)); assert( is_word(type(filename)), "FontDebug: filename!" ); fprintf(stderr, "%s\n", string(filename)); } } for( i = 1; i <= font_count; i++ ) fprintf(stderr, " finfo[%d].font_table = %s\n", i, EchoObject(finfo[i].font_table)); } /* end FontDebug */ #endif /*@::FontDefine()@************************************************************/ /* */ /* FontDefine(family, face, inside) */ /* */ /* Insert a font defined by fontdef { }, where */ /* ::= fontname AFMfilename LCMfilename recode */ /* */ /*****************************************************************************/ static void ReadFont(OBJECT face, OBJECT err); void FontDefine(OBJECT family, OBJECT face, OBJECT inside) { OBJECT font_name, AFMfilename, LCMfilename, recode; OBJECT short_name, link, y, val[5]; int i; debug3(DFT, D, "FontDefine( %s, %s, %s )", string(family), string(face), EchoObject(inside)); /* extract font_name, AFMfilename, LCMfilename, and recode */ if( type(inside) != ACAT ) { Error(37, 1, "font definition does not contain a sequence of words", WARN, &fpos(inside)); DisposeObject(inside); return; } for( i = 0; Down(inside) != inside && i != 5; i++ ) { Child(val[i], Down(inside)); DeleteLink(Up(val[i])); if( type(val[i]) == GAP_OBJ ) DisposeObject(val[i--]); else if( !is_word(type(val[i])) ) { Error(37, 2, "font definition contains a non-word", WARN, &fpos(val[i])); DisposeObject(inside); return; } } if( Down(inside) != inside || i != 4 ) { Error(37, 3, "font definition does not contain exactly four words", WARN, &fpos(inside)); DisposeObject(inside); return; } font_name = val[0]; AFMfilename = val[1]; LCMfilename = val[2]; recode = val[3]; /* insert family into font tree if not already present */ for( link = Down(font_root); link != font_root; link = NextDown(link) ) { Child(y, link); if( StringEqual(string(y), string(family)) ) { Dispose(family); family = y; break; } } if( link == font_root ) Link(font_root, family); /* insert face into family, or error if already present and different */ for( link = Down(family); link != family; link = NextDown(link) ) { Child(y, link); if( StringEqual(string(y), string(face)) ) { OBJECT other_name, other_AFMname; Child(other_AFMname, Down(y)); Child(other_name, NextDown(Down(other_AFMname))); if( StringEqual(string(other_name), string(font_name)) && StringEqual(string(other_AFMname), string(AFMfilename)) ) { debug0(DFT, D, "FontDefine returning: font redefined"); Dispose(face); return; } Error(37, 4, "font %s %s already defined at%s", WARN, &fpos(face), string(family), string(face), EchoFilePos(&fpos(y))); debug0(DFT, D, "FontDefine returning: font already defined"); Dispose(face); return; } } Link(family, face); /* add AFMfilename as first size of font, and PostScript name as its child */ Link(face, AFMfilename); short_name = MakeWordTwo(WORD, AsciiToFull("fnt"), StringInt(++font_seqnum), no_fpos); Link(AFMfilename, short_name); Link(AFMfilename, font_name); /* load character mapping file */ if( StringEqual(string(recode), STR_FONT_RECODE) ) { font_recoded(face) = TRUE; font_mapping(AFMfilename) = MapLoad(LCMfilename, TRUE); } else if( StringEqual(string(recode), STR_FONT_NORECODE) ) { font_recoded(face) = FALSE; font_mapping(AFMfilename) = MapLoad(LCMfilename, FALSE); } else Error(37, 5, "expecting either Recode or NoRecode here", FATAL, &fpos(recode)); /* say that this font is currently unused on any page */ font_page(face) = 0; font_firstpage(face) = FALSE; /* if initializing run, read the font just to make sure */ if( InitializeAll ) { ReadFont(face, face); } debug0(DFT, D, "FontDefine returning."); } /* end FontDefine */ /*****************************************************************************/ /* */ /* DebugKernTable(fnum) */ /* */ /* Print debug output of kern table for font fnum. */ /* */ /*****************************************************************************/ #if DEBUG_ON static void DebugKernTable(FONT_NUM fnum) { int i, j; unsigned short *kt = finfo[fnum].kern_table; FULL_CHAR *kc = finfo[fnum].kern_chars; unsigned char *kv = finfo[fnum].kern_value; SHORT_LENGTH *ks = finfo[fnum].kern_sizes; debug1(DFT, DD, "DebugKernTable(%d)", fnum); for( i = 0; i < MAX_CHARS; i++ ) { if( kt[i] != 0 ) { debug1(DFT, DD, "kt[%d]:", i); for( j = kt[i]; kc[j] != '\0'; j++ ) { debug3(DFT, DD, "KPX %c %c %d", i, kc[j], ks[kv[j]]); } } } debug1(DFT, DD, "DebugKernTable(%d) returning", fnum); } /* DebugKernTable */ #endif /*@::ReadFont()@**************************************************************/ /* */ /* static ReadFont(face, err) */ /* */ /* Read in a font file. Object err is used only for error reporting. */ /* */ /*****************************************************************************/ static void ReadFont(OBJECT face, OBJECT err) { OBJECT filename, fontname; FULL_CHAR buff[MAX_BUFF], command[MAX_BUFF], ch; int wx, llx, lly, urx, ury, xheight2, i, lnum, ligtop; float fl_wx, fl_llx, fl_lly, fl_urx, fl_ury, fl_xheight2, fl_under_pos, fl_under_thick; int under_pos, under_thick; BOOLEAN upfound, utfound, xhfound, wxfound, bfound; BOOLEAN fixed_pitch = FALSE; FILE_NUM fnum; FILE *fp; struct metrics *fnt; FULL_CHAR *lig, ligchar; unsigned short *kt; FULL_CHAR *kc; unsigned char *kv; SHORT_LENGTH *ks; OBJECT x; assert( is_word(type(face)), "ReadFont: !is_word(type(face))!" ); debug1(DFT, DD, "ReadFont( %s, err )", string(face)); /* get a new font number for this font, possibly requiring realloc */ if( ++font_count >= finfo_size ) { if( font_count > MAX_FONT ) Error(37, 6, "too many different fonts and sizes (maximum is %d)", FATAL, &fpos(err),MAX_FONT); ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, -1, -finfo_size * sizeof(FONT_INFO))); finfo_size *= 2; ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 1, finfo_size * sizeof(FONT_INFO))); finfo = (FONT_INFO *) realloc(finfo, finfo_size * sizeof(FONT_INFO)); if( finfo == (FONT_INFO *) NULL ) Error(37, 7, "run out of memory when increasing font table size", FATAL, &fpos(err)); } /* open the Adobe font metrics (AFM) file of the font */ assert( Down(face) != face, "ReadFont: filename missing!" ); Child(filename, Down(face)); assert( Down(filename) != filename, "ReadFont: filename child missing!" ); debug0(DFS, D, " calling DefineFile from ReadFont"); fnum = DefineFile(string(filename), STR_EMPTY, &fpos(filename), FONT_FILE, FONT_PATH); fp = OpenFile(fnum, FALSE, FALSE); if( fp == NULL ) Error(37, 8, "cannot open font file %s", FATAL, &fpos(filename), FileName(fnum)); /* check that the AFM file begins, as it should, with "StartFontMetrics" */ if( StringFGets(buff, MAX_BUFF, fp) == NULL || sscanf( (char *) buff, "%s", command) != 1 || !StringEqual(command, "StartFontMetrics") ) { debug1(DFT, DD, "first line of AFM file:%s", buff); debug1(DFT, DD, "command:%s", command); Error(37, 9, "font file %s does not begin with StartFontMetrics", FATAL, &fpos(filename), FileName(fnum)); } /* initialise font metrics table for the new font */ ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 1, MAX_CHARS * sizeof(struct metrics))); fnt = (struct metrics *) malloc(MAX_CHARS * sizeof(struct metrics)); if( fnt == (struct metrics *) NULL ) Error(37, 10, "run out of memory while reading font file %s", FATAL, &fpos(err), FileName(fnum)); ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 0, 2*MAX_CHARS*sizeof(FULL_CHAR))); lig = (FULL_CHAR *) malloc(2*MAX_CHARS*sizeof(FULL_CHAR)); /* initialise ligature table for the new font */ if( lig == (FULL_CHAR *) NULL ) Error(37, 11, "run out of memory while reading font file %s", FATAL, &fpos(err), FileName(fnum)); for( i = 0; i < MAX_CHARS; i++ ) lig[i] = 1; /* i.e. char unknown */ ligtop = MAX_CHARS+2; /* must avoid ligtop - MAX_CHARS == 0 or 1 */ /* initialise kerning table for the new font */ ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 0, MAX_CHARS * sizeof(unsigned short))); kt = (unsigned short *) malloc(MAX_CHARS * sizeof(unsigned short)); if( kt == (unsigned short *) NULL ) Error(37, 12, "run out of memory while reading font file %s", FATAL, &fpos(err), FileName(fnum)); for( i = 0; i < MAX_CHARS; i++ ) kt[i] = 0; /* i.e. no kerns */ ks = (SHORT_LENGTH *) NULL; /* i.e. no kern sizes */ /* read font metrics file */ xhfound = upfound = utfound = FALSE; fontname = nilobj; lnum = 1; while ( ( StringFGets(buff, MAX_BUFF, fp) ) != NULL ) { lnum++; sscanf( (char *) buff, "%s", command); switch( command[0] ) { case 'U': if( StringEqual(command, AsciiToFull("UnderlinePosition")) ) { if( upfound ) { Error(37, 13, "UnderlinePosition found twice in font file (line %d)", FATAL, &fpos(filename), lnum); } sscanf( (char *) buff, "UnderlinePosition %f", &fl_under_pos); under_pos = fl_under_pos; upfound = TRUE; } else if( StringEqual(command, AsciiToFull("UnderlineThickness")) ) { if( utfound ) { Error(37, 14, "UnderlineThickness found twice in font file (line %d)", FATAL, &fpos(filename), lnum); } sscanf( (char *) buff, "UnderlineThickness %f", &fl_under_thick); under_thick = fl_under_thick; utfound = TRUE; } break; case 'X': if( StringEqual(command, AsciiToFull("XHeight")) ) { if( xhfound ) { Error(37, 15, "XHeight found twice in font file (line %d)", FATAL, &fpos(filename), lnum); } sscanf( (char *) buff, "XHeight %f", &fl_xheight2); xheight2 = fl_xheight2 / 2; xhfound = TRUE; } break; case 'F': if( StringEqual(command, AsciiToFull("FontName")) ) { if( fontname != nilobj ) { Error(37, 16, "FontName found twice in font file %s (line %d)", FATAL, &fpos(filename), FileName(fnum), lnum); } sscanf( (char *) buff, "FontName %s", command); if( StringEqual(command, STR_EMPTY) ) { Error(37, 17, "FontName empty in font file %s (line %d)", FATAL, &fpos(filename), FileName(fnum), lnum); } Child(x, LastDown(filename)); if( !StringEqual(command, string(x)) ) Error(37, 18, "FontName in font file (%s) and fontdef (%s) disagree", WARN, &fpos(filename), command, string(x)); fontname = MakeWord(WORD, command, &fpos(filename)); } break; case 'I': if( StringEqual(command, AsciiToFull("IsFixedPitch")) ) { sscanf( (char *) buff, "IsFixedPitch %s", command); if( StringEqual(command, AsciiToFull("true")) ) { fixed_pitch = TRUE; } } break; case 'S': if( StringEqual(command, AsciiToFull("StartCharMetrics")) ) { if( fontname == nilobj ) Error(37, 19, "FontName missing in file %s", FATAL, &fpos(filename), FileName(fnum)); if( !xhfound ) xheight2 = DEFAULT_XHEIGHT / 2; while( StringFGets(buff, MAX_BUFF, fp) != NULL && !StringBeginsWith(buff, AsciiToFull("EndCharMetrics")) ) { /* read one line containing metric info for one character */ debug1(DFT, DDD, "ReadFont reading %s", buff); lnum++; ch = '\0'; wxfound = bfound = FALSE; i = 0; while( buff[i] == ' ' ) i++; while( buff[i] != '\n' ) { debug2(DFT, DDD, " ch = %d, &buff[i] = %s", ch, &buff[i]); sscanf( (char *) &buff[i], "%s", command); if( StringEqual(command, "N") ) { sscanf( (char *) &buff[i], "N %s", command); ch = MapCharEncoding(command, font_mapping(filename)); } else if( StringEqual(command, "WX") ) { sscanf( (char *) &buff[i], "WX %f", &fl_wx); wx = fl_wx; wxfound = TRUE; } else if( StringEqual(command, "B") ) { sscanf( (char *) &buff[i], "B %f %f %f %f", &fl_llx, &fl_lly, &fl_urx, &fl_ury); llx = fl_llx; lly = fl_lly; urx = fl_urx; ury = fl_ury; bfound = TRUE; } else if( StringEqual(command, "L") && BackEnd != PLAINTEXT && ch != '\0' ) { if( lig[ch] == 1 ) lig[ch] = ligtop - MAX_CHARS; lig[ligtop++] = ch; i++; /* skip L */ while( buff[i] == ' ' ) i++; while( buff[i] != ';' && buff[i] != '\n' ) { sscanf( (char *) &buff[i], "%s", command); ligchar = MapCharEncoding(command, font_mapping(filename)); if( ligchar != '\0' ) lig[ligtop++] = ligchar; else { Error(37, 20, "ignoring unencoded ligature character %s in font file %s (line %d)", WARN, &fpos(filename), command, FileName(fnum), lnum); lig[ch] = 1; } if( ligtop > 2*MAX_CHARS - 5 ) Error(37, 21, "too many ligature characters in font file %s (line %d)", FATAL, &fpos(filename), FileName(fnum), lnum); while( buff[i] != ' ' && buff[i] != ';' ) i++; while( buff[i] == ' ' ) i++; } lig[ligtop++] = '\0'; } while( buff[i] != ';' && buff[i] != '\n' ) i++; if( buff[i] == ';' ) { i++; while( buff[i] == ' ' ) i++; } } if( ch > '\0' ) { if( !wxfound ) { Error(37, 22, "WX missing in font file %s (line %d)", FATAL, &fpos(filename), FileName(fnum), lnum); } if( !bfound ) { Error(37, 23, "B missing in font file %s (line %d)", FATAL, &fpos(filename), FileName(fnum), lnum); } if( lig[ch] == 1 ) lig[ch] = 0; /* set to known if unknown */ else if( lig[ch] > 1 ) /* add '\0' to end of ligs */ lig[ligtop++] = '\0'; switch( BackEnd ) { case POSTSCRIPT: case PDF: fnt[ch].left = llx; fnt[ch].down = lly - xheight2; fnt[ch].right = wx; fnt[ch].up = ury - xheight2; fnt[ch].last_adjust = (urx == 0 || wx == 0 || fixed_pitch) ? 0 : urx - wx; break; case PLAINTEXT: fnt[ch].left = 0; fnt[ch].down = - PlainCharHeight / 2; fnt[ch].right = PlainCharWidth; fnt[ch].up = PlainCharHeight / 2; fnt[ch].last_adjust = 0; break; } debug6(DFT, DDD, " fnt[%c] = (%d,%d,%d,%d,%d)",ch, fnt[ch].left, fnt[ch].down, fnt[ch].right, fnt[ch].up, fnt[ch].last_adjust); } } } else if( BackEnd != PLAINTEXT && Kern && StringEqual(command, AsciiToFull("StartKernPairs")) ) { FULL_CHAR ch1, ch2, last_ch1; FULL_CHAR name1[30], name2[30]; int kc_top, ks_top, pos, num_pairs, ksize; float fl_ksize; if( sscanf( (char *) buff, "StartKernPairs %d", &num_pairs) != 1 ) Error(37, 24, "syntax error on StartKernPairs line in font file %s (line %d)", FATAL, &fpos(filename), FileName(fnum), lnum); kc_top = 1; ks_top = 1; ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 0, 2*num_pairs * sizeof(FULL_CHAR))); kc = (FULL_CHAR *) malloc(2 * num_pairs * sizeof(FULL_CHAR)); ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 0, 2 * num_pairs * sizeof(unsigned char))); kv = (unsigned char *) malloc(2 * num_pairs * sizeof(unsigned char)); ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 0, num_pairs * sizeof(SHORT_LENGTH))); ks = (SHORT_LENGTH *) malloc(num_pairs * sizeof(SHORT_LENGTH)); last_ch1 = '\0'; while( StringFGets(buff, MAX_BUFF, fp) == (char *) buff && !StringBeginsWith(buff, AsciiToFull("EndKernPairs")) ) { debug1(DFT, DD, "ReadFont reading %s", buff); lnum++; if( StringBeginsWith(buff, AsciiToFull("KPX")) ) { /* get the two character names and kern size from buff */ if( sscanf((char *)buff, "KPX %s %s %f",name1,name2,&fl_ksize)!=3 ) Error(37, 25, "syntax error in font file %s (line %d): %s", FATAL, &fpos(filename), FileName(fnum), lnum, buff); /* ignore size 0 kern pairs (they are frequent, why?) */ ksize = fl_ksize; if( ksize == 0 ) continue; /* check that both characters are encoded */ ch1 = MapCharEncoding(name1, font_mapping(filename)); if( ch1 == '\0' ) { /* *** Error(37, 26, "unencoded kern character %s in font file %s (line %d)", WARN, &fpos(filename), name1, FileName(fnum), lnum); *** */ continue; } ch2 = MapCharEncoding(name2, font_mapping(filename)); if( ch2 == '\0' ) { /* *** Error(37, 27, "unencoded kern character %s in font file %s (line %d)", WARN, &fpos(filename), name2, FileName(fnum), lnum); *** */ continue; } /* check that ch1 is contiguous with previous occurrences */ if( ch1 != last_ch1 && kt[ch1] != 0 ) { Error(37, 28, "non-contiguous kerning pair %s %s in font file %s (line %d)", WARN, &fpos(filename), name1, name2, FileName(fnum), lnum); continue; } last_ch1 = ch1; /* if ch1 never seen before, make new entry in kt[] and kc[] */ if( kt[ch1] == 0 ) { debug2(DFT, DD, " kt[%d] = %d", ch1, kc_top); kt[ch1] = kc_top; kc[kc_top] = (FULL_CHAR) '\0'; kv[kc_top] = 0; kc_top++; } /* find kerning size in ks[] or else add it to the end */ for( pos = 1; pos < ks_top; pos++ ) { if( ks[pos] == ksize ) break; } if( pos == ks_top ) { if( ks_top == num_pairs ) Error(37, 29, "too many kerning pairs in font file %s (line %d)", FATAL, &fpos(filename), FileName(fnum), lnum); debug2(DFT, DD, " ks[%d] = %d", pos, ksize); ks[pos] = ksize; ks_top++; } /* insert ch2 into the kc entries (sorted decreasing) for ch1 */ for( i = kc_top-1; i >= kt[ch1] && kc[i] < ch2; i-- ) { kc[i+1] = kc[i]; kv[i+1] = kv[i]; } if( i >= kt[ch1] && kc[i] == ch2 ) Error(37, 30, "kerning pair %s %s appears twice in font file %s (line %d)", FATAL, &fpos(filename), name1, name2, FileName(fnum), lnum); kc[i+1] = ch2; kv[i+1] = pos; kc_top++; } } ks[0] = ks_top; } break; case 'E': if( StringEqual(command, AsciiToFull("EndFontMetrics")) ) { /* make a new font record and insert into font tree */ font_num(face) = font_num(fontname) = font_count; font_size(fontname) = (BackEnd != PLAINTEXT) ? SZ_DFT : PlainCharHeight; font_xheight2(fontname) = (BackEnd != PLAINTEXT) ? xheight2 : PlainCharHeight / 4; font_mapping(fontname) = font_mapping(filename); ch = MapCharEncoding(STR_PS_SPACENAME, font_mapping(fontname)); font_spacewidth(fontname) = ch == '\0' ? 0 : fnt[ch].right; finfo[font_count].font_table = fontname; finfo[font_count].original_font = face; finfo[font_count].underline_pos = xheight2 - under_pos; finfo[font_count].underline_thick = under_thick; finfo[font_count].size_table = fnt; finfo[font_count].lig_table = lig; finfo[font_count].kern_table = kt; finfo[font_count].kern_chars = kc; finfo[font_count].kern_value = kv; finfo[font_count].kern_sizes = ks; Link(face, fontname); ifdebug(DFT, DD, DebugKernTable(font_count)); /* *** either no errors or too many, so killing this now if( InitializeAll ) { OBJECT family; FULL_CHAR *str; Parent(family, Up(face)); for( i = 0; i < MAX_CHARS; i++ ) { if( lig[i] == 1 ) { str = string(MapTable[font_mapping(fontname)]->vector[i]); if( !StringEqual(str, AsciiToFull(".notdef")) && MapCharEncoding(str, font_mapping(fontname)) == i ) Error(37, 31, "font %s %s has no glyph for character %s", WARN, &fpos(filename), string(family), string(face), str); } } } *** */ /* close file, debug and exit */ fclose(fp); debug4(DFT, D, "ReadFont returning: %d, name %s, fs %d, xh2 %d", font_count, string(fontname), font_size(fontname), xheight2); return; } break; default: break; } } Error(37, 32, "EndFontMetrics missing from font file %s", FATAL, &fpos(filename), FileName(fnum)); } /* end ReadFont */ /*@::FontChange()@************************************************************/ /* */ /* FontChange(style, x) */ /* */ /* Returns an internal font number which is the current font changed */ /* according to word object x. e.g. if current font is Roman 12p and x is */ /* "-3p", then FontChange returns the internal font number of Roman 9p. */ /* */ /* FontChange permits empty and null objects within x; these have no */ /* effect. */ /* */ /*****************************************************************************/ void FontChange(STYLE *style, OBJECT x) { /* register */ int i; OBJECT par[3], family, face, fsize, y, link, new, old, tmpf; GAP gp; SHORT_LENGTH flen; int num, c; unsigned inc; struct metrics *newfnt, *oldfnt; FULL_CHAR *lig; SHORT_LENGTH *oldks, *newks; int klen; debug2(DFT, D, "FontChange( %s, %s )", EchoStyle(style), EchoObject(x)); assert( font(*style) <= font_count, "FontChange: font_count!"); ifdebug(DFT, DD, FontDebug()); /* set par[0..num-1] to the 1, 2 or 3 parameters of the font operator */ num = 0; if( type(x) == NULL_CLOS ) { /* acceptable, but do nothing */ } else if( is_word(type(x)) ) { if( StringEqual(string(x), STR_SMALL_CAPS_ON) ) small_caps(*style) = SMALL_CAPS_ON; else if( StringEqual(string(x), STR_SMALL_CAPS_OFF) ) small_caps(*style) = SMALL_CAPS_OFF; else if( !StringEqual(string(x), STR_EMPTY) ) par[num++] = x; } else if( type(x) == ACAT ) { for( link = Down(x); link != x; link = NextDown(link) ) { Child(y, link); debug1(DFT, DDD, " pars examining y = %s", EchoObject(y)); if( type(y) == GAP_OBJ || type(y) == NULL_CLOS ) continue; if( is_word(type(y)) ) { if( StringEqual(string(y), STR_SMALL_CAPS_ON) ) small_caps(*style) = SMALL_CAPS_ON; else if( StringEqual(string(y), STR_SMALL_CAPS_OFF) ) small_caps(*style) = SMALL_CAPS_OFF; else if( !StringEqual(string(y), STR_EMPTY) ) { if( num >= 3 ) { Error(37, 33, "error in left parameter of %s", WARN, &fpos(x), KW_FONT); debug0(DFT, D, "FontChange returning: ACAT children"); return; } par[num++] = y; } } else { Error(37, 34, "error in left parameter of %s", WARN, &fpos(x), KW_FONT); debug0(DFT, D, "FontChange returning: ACAT children"); return; } } } else { Error(37, 35, "error in left parameter of %s", WARN, &fpos(x), KW_FONT); debug0(DFT, D, "FontChange returning: wrong type"); return; } debug1(DFT, DDD, " found pars, num = %d", num); if( num == 0 ) { debug1(DFT, D, "FontChange returning %s", EchoStyle(style)); return; } /* extract fsize parameter, if any */ assert( num >= 1 && num <= 3, "FontChange: num!" ); fsize = nilobj; for( i = 0; i < num; i++ ) { c = string(par[i])[0]; if( c == CH_INCGAP || c == CH_DECGAP || decimaldigit(c) ) { /* extract fsize, shuffle the rest down */ fsize = par[i]; for( i = i + 1; i < num; i++ ) par[i-1] = par[i]; num--; } } /* *** old now c = string(par[num-1])[0]; if( c == CH_INCGAP || c == CH_DECGAP || decimaldigit(c) ) { fsize = par[num-1]; num--; } else fsize = nilobj; *** */ /* check for initial font case: must have family, face, and size */ if( font(*style) == NO_FONT && (fsize == nilobj || num < 2) ) Error(37, 36, "initial font must have family, face and size", FATAL, &fpos(x)); /* get font family */ if( num == 2 ) { /* par[0] contains a new family name */ for( link = Down(font_root); link != font_root; link = NextDown(link) ) { Child(family, link); if( StringEqual(string(family), string(par[0])) ) break; } if( link == font_root ) { Error(37, 37, "font family %s not defined", WARN, &fpos(par[0]), string(par[0])); return; } } else { /* preserve current family */ assert( Up(finfo[font(*style)].font_table)!=finfo[font(*style)].font_table, "FontChange: Up(finfo[font(*style)].font_table) !" ); Parent(face, Up(finfo[font(*style)].font_table)); assert( is_word(type(face)), "FontChange: type(face)!" ); assert( Up(face) != face, "FontChange: Up(face)!" ); Parent(family, Up(face)); assert( is_word(type(family)), "FontChange: type(family)!" ); } /* get font face */ if( num != 0 ) { /* par[num-1] contains a new face name */ for( link = Down(family); link != family; link = NextDown(link) ) { Child(face, link); if( StringEqual(string(face), string(par[num-1])) ) break; } if( link == family ) { /* missing face name; first check whether a family name was intended */ for( link = Down(font_root); link != font_root; link = NextDown(link) ) { Child(tmpf, link); if( StringEqual(string(tmpf), string(par[num-1])) ) break; } if( font_root == Down(font_root) ) { Error(37, 38, "there are no fonts", FATAL, &fpos(par[num-1])); } else if( link != font_root ) { Error(37, 39, "font family name %s must be accompanied by a face name", WARN, &fpos(par[num-1]), string(par[num-1])); } else Error(37, 40, "font face name %s not defined in font family %s", WARN, &fpos(par[num-1]), string(par[num-1]), string(family)); return; } } else { /* preserve current face name */ Parent(face, Up(finfo[font(*style)].font_table)); assert( is_word(type(face)), "FontChange: type(face)!" ); assert( Up(face) != face, "FontChange: Up(face)!" ); } /* get font size */ if( fsize == nilobj ) flen = font_size(finfo[font(*style)].font_table); else { GetGap(fsize, style, &gp, &inc); if( mode(gp) != EDGE_MODE || units(gp) != FIXED_UNIT ) { Error(37, 56, "syntax error in font size %s; ignoring it", WARN, &fpos(fsize), string(fsize)); flen = font_size(finfo[font(*style)].font_table); } else if( inc == GAP_ABS ) flen = width(gp); else if( font(*style) == NO_FONT ) { Error(37, 41, "no current font on which to base size change %s", FATAL, &fpos(fsize), string(fsize)); } else if( inc == GAP_INC ) flen = font_size(finfo[font(*style)].font_table) + width(gp); else if( inc == GAP_DEC ) flen = font_size(finfo[font(*style)].font_table) - width(gp); else Error(37, 42, "FontChange: %d", INTERN, &fpos(x), inc); } if( flen <= 0 ) { Error(37, 43, "%s %s ignored (result is not positive)", WARN, &fpos(fsize), string(fsize), KW_FONT); return; } /* if the font file has not been read before, read it now */ assert( Down(face) != face && type(Down(face)) == LINK, "FontChange: dn!" ); if( Down(face) == LastDown(face) ) ReadFont(face, x); assert( Down(face) != LastDown(face), "FontChange: after ReadFont!" ); /* search fonts of face for desired size; return if already present */ if( BackEnd == PLAINTEXT ) flen = PlainCharHeight; for( link = NextDown(Down(face)); link != face; link = NextDown(link) ) { Child(fsize, link); if( font_size(fsize) == flen ) { font(*style) = font_num(fsize); SetGap(space_gap(*style), nobreak(space_gap(*style)), FALSE, TRUE, FIXED_UNIT, EDGE_MODE, font_spacewidth(fsize)); debug2(DFT, D,"FontChange returning (old) %d (XHeight2 = %d)", font(*style), font_xheight2(finfo[font(*style)].font_table)); return; } } /* now need to rescale the font; first create a sized font record */ if( ++font_count >= finfo_size ) { if( font_count > MAX_FONT ) Error(37, 44, "too many different fonts and sizes (max is %d)", FATAL, &fpos(x), MAX_FONT); ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, -1, -finfo_size * sizeof(FONT_INFO))); finfo_size *= 2; ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 1, finfo_size * sizeof(FONT_INFO))); finfo = (FONT_INFO *) realloc(finfo, finfo_size * sizeof(FONT_INFO)); if( finfo == (FONT_INFO *) NULL ) Error(37, 45, "run out of memory when increasing font table size", FATAL, &fpos(x)); } assert( Down(face) != face && NextDown(Down(face)) != face, "FontChange!!" ); Child(old, NextDown(Down(face))); assert( is_word(type(old)), "FontChange: old!" ); new = MakeWord(WORD, string(old), no_fpos); Link(face, new); font_size(new) = BackEnd != PLAINTEXT ? flen : font_size(old); font_xheight2(new) = font_xheight2(old) * font_size(new) / font_size(old); font_mapping(new) = font_mapping(old); font_spacewidth(new) = font_spacewidth(old) * font_size(new)/font_size(old); font_num(new) = font_count; finfo[font_count].font_table = new; finfo[font_count].original_font = face; finfo[font_count].underline_pos = (finfo[font_num(old)].underline_pos * font_size(new)) / font_size(old); finfo[font_count].underline_thick = (finfo[font_num(old)].underline_thick * font_size(new)) / font_size(old); ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 1, MAX_CHARS * sizeof(struct metrics))); finfo[font_count].size_table = (struct metrics *) malloc(MAX_CHARS * sizeof(struct metrics)); if( finfo[font_count].size_table == (struct metrics *) NULL ) Error(37, 46, "run out of memory when changing font or font size", FATAL, &fpos(x)); finfo[font_count].lig_table = lig = finfo[font_num(old)].lig_table; /* scale old font to new size */ newfnt = finfo[font_num(new)].size_table; oldfnt = finfo[font_num(old)].size_table; for( i = 0; i < MAX_CHARS; i++ ) if( lig[i] != 1 ) { newfnt[i].left = (oldfnt[i].left * font_size(new)) / font_size(old); newfnt[i].right = (oldfnt[i].right * font_size(new)) / font_size(old); newfnt[i].down = (oldfnt[i].down * font_size(new)) / font_size(old); newfnt[i].up = (oldfnt[i].up * font_size(new)) / font_size(old); newfnt[i].last_adjust = (oldfnt[i].last_adjust * font_size(new)) / font_size(old); } /* copy and scale kerning tables */ finfo[font_count].kern_table = finfo[font_num(old)].kern_table; finfo[font_count].kern_chars = finfo[font_num(old)].kern_chars; finfo[font_count].kern_value = finfo[font_num(old)].kern_value; oldks = finfo[font_num(old)].kern_sizes; if( oldks != (SHORT_LENGTH *) NULL ) { klen = oldks[0]; ifdebug(DMA, D, DebugRegisterUsage(MEM_FONTS, 0, klen * sizeof(SHORT_LENGTH))); finfo[font_count].kern_sizes = newks = (SHORT_LENGTH *) malloc(klen * sizeof(SHORT_LENGTH)); if( newks == (SHORT_LENGTH *) NULL ) Error(37, 47, "run out of memory when changing font or font size", FATAL, &fpos(x)); newks[0] = klen; for( i = 1; i < klen; i++ ) newks[i] = (oldks[i] * font_size(new)) / font_size(old); } else finfo[font_count].kern_sizes = (SHORT_LENGTH *) NULL; /* return new font number and exit */ font(*style) = font_count; SetGap(space_gap(*style), nobreak(space_gap(*style)), FALSE, TRUE, FIXED_UNIT, EDGE_MODE, font_spacewidth(new)); debug2(DFT, D,"FontChange returning (scaled) %d (XHeight2 = %d)", font(*style), font_xheight2(finfo[font(*style)].font_table)); /* FontDebug(); */ } /* end FontChange */ /*****************************************************************************/ /* */ /* KernLength(fnum, ch1, ch2, res) */ /* */ /* Set res to the kern length between ch1 and ch2 in font fnum, or 0 if */ /* none. Actually we first convert ch1 and ch2 to corresponding unaccented */ /* characters, because metrics files don't seem to contain kerning pairs */ /* for accented characters. */ /* */ /*****************************************************************************/ #define KernLength(fnum, mp, ch1, ch2, res) \ { int ua_ch1 = mp[ch1]; \ int ua_ch2 = mp[ch2]; \ int i = finfo[fnum].kern_table[ua_ch1], j; \ if( i == 0 ) res = 0; \ else \ { FULL_CHAR *kc = finfo[fnum].kern_chars; \ for( j = i; kc[j] > ua_ch2; j++ ); \ res = (kc[j] == ua_ch2) ? \ finfo[fnum].kern_sizes[finfo[fnum].kern_value[j]] : 0; \ } \ } /* end KernLength */ /*@::FontWordSize()@**********************************************************/ /* */ /* FontWordSize(x) */ /* */ /* Calculate the horizontal and vertical size of WORD or QWORD x, including */ /* the effect of ligature sequences but not replacing them with ligatures. */ /* */ /*****************************************************************************/ void FontWordSize(OBJECT x) { FULL_CHAR *p, *q, *a, *b, *lig, *unacc, *acc; OBJECT tmp; FULL_CHAR buff[MAX_BUFF]; MAPPING m; int r, u, d, ksize; struct metrics *fnt; debug2(DFT, D, "FontWordSize( %s ), font = %d", string(x), word_font(x)); assert( is_word(type(x)), "FontWordSize: !is_word(type(x))!" ); p = string(x); q = buff; if( *p ) { if ( word_font(x) < 1 || word_font(x) > font_count ) Error(37, 48, "no current font at word %s", FATAL, &fpos(x), string(x)); if ( word_colour(x) == 0 && BackEnd != PLAINTEXT ) Error(37, 49, "no current colour at word %s", FATAL, &fpos(x), string(x)); if ( word_language(x) == 0 ) Error(37, 50, "no current language at word %s", FATAL, &fpos(x),string(x)); fnt = finfo[word_font(x)].size_table; lig = finfo[word_font(x)].lig_table; m = font_mapping(finfo[word_font(x)].font_table); unacc = MapTable[m]->map[MAP_UNACCENTED]; acc = MapTable[m]->map[MAP_ACCENT]; d = u = r = 0; do { /* check for missing glyph (lig[] == 1) or ligatures (lig[] > 1) */ debug2(DFT, D, " examining `%c' lig = %d", *p, lig[*p]); if( lig[*q = *p++] ) { if( lig[*q] == 1 ) { tmp = MakeWord(QWORD, STR_SPACE, &fpos(x)); string(tmp)[0] = *q; /* bug fix: unaccented version exists if unacc differs from self */ if( unacc[*q] != *q ) { /* *** this is acceptable now, let this char through Error(37, 51, "accent dropped from character %s (it has no glyph in font %s)", WARN, &fpos(x), StringQuotedWord(tmp), FontFamilyAndFace(word_font(x))); *(p-1) = *q = unacc[*q]; *** */ debug2(DFT, D, " unacc[%c] = `%c'", *q, unacc[*q]); fnt[*q].up = fnt[unacc[*q]].up; fnt[*q].down = fnt[unacc[*q]].down; fnt[*q].left = fnt[unacc[*q]].left; fnt[*q].right = fnt[unacc[*q]].right; fnt[*q].last_adjust = fnt[unacc[*q]].last_adjust; lig[*q] = 0; } else { debug1(DFT, D, " unacc[%c] = 0, replacing by space", *q); Error(37, 52, "character %s replaced by space (it has no glyph in font %s)", WARN, &fpos(x), StringQuotedWord(tmp), FontFamilyAndFace(word_font(x))); *(p-1) = *q = CH_SPACE; } Dispose(tmp); } else { debug1(DFT, D, " processing ligature beginning at %c", *q); a = &lig[ lig[*(p-1)] + MAX_CHARS ]; while( *a++ == *(p-1) ) { b = p; while( *a == *b && *(a+1) != '\0' && *b != '\0' ) a++, b++; if( *(a+1) == '\0' ) { *q = *a; p = b; break; } else { while( *++a ); a++; } } } } /* accumulate size of *q */ if( fnt[*q].up > u ) u = fnt[*q].up; if( fnt[*q].down < d ) d = fnt[*q].down; r += fnt[*q++].right; } while( *p ); *q = '\0'; /* adjust for last character */ r += fnt[*(q-1)].last_adjust; /* add kern lengths to r */ for( p = buff, q = p+1; *q; p++, q++ ) { KernLength(word_font(x), unacc, *p, *q, ksize); debugcond3(DFT, D, ksize != 0, " KernLength(fnum, %c, %c) = %d", *p, *q, ksize); r += ksize; } /* set sizes of x */ back(x, COLM) = 0; fwd(x, COLM) = r; back(x, ROWM) = u; fwd(x, ROWM) = -d; } else back(x, COLM) = fwd(x, COLM) = back(x, ROWM) = fwd(x, ROWM) = 0; debug4(DFT, D, "FontWordSize returning %hd %hd %hd %hd", back(x, COLM), fwd(x, COLM), back(x, ROWM), fwd(x, ROWM)); } /* end FontWordSize */ /*@::FontSize(), FontHalfXHeight(), FontEncoding(), FontName()@***************/ /* */ /* FULL_LENGTH FontSize(fnum, x) */ /* */ /* Return the size of this font. x is for error messages only. */ /* */ /*****************************************************************************/ FULL_LENGTH FontSize(FONT_NUM fnum, OBJECT x) { debug1(DFT, DD, "FontSize( %d )", fnum); assert( fnum <= font_count, "FontSize!" ); if( fnum <= 0 ) Error(37, 53, "no current font at this point", FATAL, &fpos(x)); debug1(DFT, DD, "FontSize returning %d", font_size(finfo[fnum].font_table)); return font_size(finfo[fnum].font_table); } /* end FontSize */ /*****************************************************************************/ /* */ /* FULL_LENGTH FontHalfXHeight(fnum) */ /* */ /* Return the xheight2 value of this font. */ /* */ /*****************************************************************************/ FULL_LENGTH FontHalfXHeight(FONT_NUM fnum) { debug1(DFT, DD, "FontHalfXHeight( %d )", fnum); assert( fnum <= font_count, "FontHalfXHeight!" ); debug1(DFT, DD, "FontHalfXHeight returning %d", font_xheight2(finfo[fnum].font_table)); return font_xheight2(finfo[fnum].font_table); } /* end FontHalfXHeight */ /*****************************************************************************/ /* */ /* MAPPING FontMapping(fnum, xfpos) */ /* */ /* Return the character mapping of this font, to use for small caps, etc. */ /* xfpos is the file position for error messages. */ /* */ /*****************************************************************************/ MAPPING FontMapping(FONT_NUM fnum, FILE_POS *xfpos) { debug1(DFT, DD, "FontMapping( %d )", fnum); assert( fnum <= font_count, "FontMapping!" ); if( fnum <= 0 ) Error(37, 54, "no current font at this point", FATAL, xfpos); debug1(DFT, DD, "FontMapping returning %d", font_mapping(finfo[fnum].font_table)); return font_mapping(finfo[fnum].font_table); } /* end FontMapping */ /*****************************************************************************/ /* */ /* FULL_CHAR *FontName(fnum) */ /* */ /* Return the short PostScript name of this font. */ /* */ /*****************************************************************************/ FULL_CHAR *FontName(FONT_NUM fnum) { OBJECT face, AFMfilename, short_name; debug1(DFT, D, "FontName( %d )", fnum); assert( fnum <= font_count, "FontName!" ); Parent(face, Up(finfo[fnum].font_table)); Child(AFMfilename, Down(face)); Child(short_name, Down(AFMfilename)); assert( is_word(type(short_name)), "FontName: short_name!" ); debug1(DFT, D, "FontName returning %s", string(short_name)); return string(short_name); } /* end FontName */ /*@::FontFamily(), FontFace@**************************************************/ /* */ /* FULL_CHAR *FontFamilyAndFace(fnum) */ /* */ /* Return a static string of the current font family and face. */ /* */ /*****************************************************************************/ FULL_CHAR *FontFamily(FONT_NUM fnum) { OBJECT face, family; debug1(DFT, D, "FontFamily( %d )", fnum); assert( fnum <= font_count, "FontFamiliy!" ); Parent(face, Up(finfo[fnum].font_table)); Parent(family, Up(face)); debug1(DFT, D, "FontFamily returning %s", string(family)); return string(family); } /* end FontFamilyAndFace */ FULL_CHAR *FontFace(FONT_NUM fnum) { OBJECT face, family; debug1(DFT, D, "FontFacec( %d )", fnum); assert( fnum <= font_count, "FontFamiliy!" ); Parent(face, Up(finfo[fnum].font_table)); Parent(family, Up(face)); debug1(DFT, D, "FontFace returning %s", string(face)); return string(face); } /* end FontFamilyAndFace */ /*@::FontFamilyAndFace(), FontPrintAll()@*************************************/ /* */ /* FULL_CHAR *FontFamilyAndFace(fnum) */ /* */ /* Return a static string of the current font family and face. */ /* */ /*****************************************************************************/ FULL_CHAR *FontFamilyAndFace(FONT_NUM fnum) { OBJECT face, family; static FULL_CHAR buff[80]; debug1(DFT, D, "FontFamilyAndFace( %d )", fnum); assert( fnum <= font_count, "FontName!" ); Parent(face, Up(finfo[fnum].font_table)); Parent(family, Up(face)); if( StringLength(string(family)) + StringLength(string(face)) + 1 > 80 ) Error(37, 55, "family and face names %s %s are too long", FATAL, no_fpos, string(family), string(face)); StringCopy(buff, string(family)); StringCat(buff, STR_SPACE); StringCat(buff, string(face)); debug1(DFT, D, "FontName returning %s", buff); return buff; } /* end FontFamilyAndFace */ /*****************************************************************************/ /* */ /* FontPrintAll(fp) */ /* */ /* Print all font encoding commands on output file fp */ /* */ /*****************************************************************************/ void FontPrintAll(FILE *fp) { OBJECT family, face, AFMfilename, short_name, ps_name, link, flink; assert(font_root!=nilobj && type(font_root)==ACAT, "FontDebug: font_root!"); debug0(DFT, DD, "FontPrintAll(fp)"); for( link = Down(font_root); link != font_root; link = NextDown(link) ) { Child(family, link); assert( is_word(type(family)), "FontPrintAll: family!" ); for( flink = Down(family); flink != family; flink = NextDown(flink) ) { Child(face, flink); assert( is_word(type(face)), "FontPrintAll: face!" ); assert( Down(face) != face, "FontDebug: Down(face)!"); Child(AFMfilename, Down(face)); assert( is_word(type(AFMfilename)), "FontPrintAll: filename!" ); assert( Down(AFMfilename) != AFMfilename, "FontPrintAll: 1!" ); assert( LastDown(AFMfilename) != Down(AFMfilename), "FontPrintAll: 2!" ); Child(short_name, Down(AFMfilename)); assert( is_word(type(short_name)), "FontPrintAll: short_name!" ); Child(ps_name, LastDown(AFMfilename)); assert( is_word(type(ps_name)), "FontPrintAll: ps_name!" ); if( font_recoded(face) ) { fprintf(fp, "/%s%s %s /%s LoutRecode\n", string(ps_name), string(short_name), MapEncodingName(font_mapping(AFMfilename)), string(ps_name)); fprintf(fp, "/%s { /%s%s LoutFont } def\n", string(short_name), string(ps_name), string(short_name)); } else fprintf(fp, "/%s { /%s LoutFont } def\n", string(short_name), string(ps_name)); } } fputs("\n", fp); debug0(DFT, DD, "FontPrintAll returning."); } /* end FontPrintAll */ /*@@**************************************************************************/ /* */ /* FontPrintPageSetup(fp) */ /* */ /* Print all font encoding commands needed for the current page onto fp. */ /* */ /*****************************************************************************/ void FontPrintPageSetup(FILE *fp) { OBJECT face, AFMfilename, short_name, ps_name, link; assert(font_root!=nilobj && type(font_root)==ACAT, "FontDebug: font_root!"); assert(font_used!=nilobj && type(font_used)==ACAT, "FontDebug: font_used!"); debug0(DFT, DD, "FontPrintPageSetup(fp)"); for( link = Down(font_used); link != font_used; link = NextDown(link) ) { Child(face, link); assert( is_word(type(face)), "FontPrintPageSetup: face!" ); assert( Down(face) != face, "FontDebug: Down(face)!"); /* record that face is used on the first page, if this is the first page */ if( font_curr_page == 1 ) font_firstpage(face) = TRUE; /* print font encoding command unless already done */ if( !font_firstpage(face) || font_curr_page == 1 ) { Child(AFMfilename, Down(face)); assert( is_word(type(AFMfilename)), "FontPrintPageSetup: filename!" ); assert( Down(AFMfilename) != AFMfilename, "FontPrintPageSetup: 1!" ); assert( LastDown(AFMfilename)!=Down(AFMfilename), "FontPrintPageSetup!"); Child(short_name, Down(AFMfilename)); assert( is_word(type(short_name)), "FontPrintPageSetup: short_name!" ); Child(ps_name, LastDown(AFMfilename)); assert( is_word(type(ps_name)), "FontPrintPageSetup: ps_name!" ); fprintf(fp, "%%%%IncludeResource: font %s\n", string(ps_name)); switch( BackEnd ) { case POSTSCRIPT: if( font_recoded(face) ) { fprintf(fp, "/%s%s %s /%s LoutRecode\n", string(ps_name), string(short_name), MapEncodingName(font_mapping(AFMfilename)), string(ps_name)); fprintf(fp, "/%s { /%s%s LoutFont } def\n", string(short_name), string(ps_name), string(short_name)); } else fprintf(fp, "/%s { /%s LoutFont } def\n", string(short_name), string(ps_name)); break; case PDF: PDFFont_AddFont(fp, string(short_name), string(ps_name), MapEncodingName(font_mapping(AFMfilename))); break; } } } debug0(DFT, DD, "FontPrintPageSetup returning."); } /* end FontPrintPageSetup */ /*@@**************************************************************************/ /* */ /* FontPrintPageResources(fp) */ /* */ /* Print all page resources (i.e. fonts needed or supplied) onto fp. */ /* */ /*****************************************************************************/ void FontPrintPageResources(FILE *fp) { OBJECT face, AFMfilename, short_name, ps_name, link; BOOLEAN first; assert(font_root!=nilobj && type(font_root)==ACAT, "FontDebug: font_root!"); assert(font_used!=nilobj && type(font_used)==ACAT, "FontDebug: font_used!"); debug0(DFT, DD, "FontPrintPageResources(fp)"); first = TRUE; for( link = Down(font_used); link != font_used; link = NextDown(link) ) { Child(face, link); assert( is_word(type(face)), "FontPrintPageResources: face!" ); assert( Down(face) != face, "FontDebug: Down(face)!"); Child(AFMfilename, Down(face)); assert( is_word(type(AFMfilename)), "FontPrintPageResources: filename!" ); assert( Down(AFMfilename) != AFMfilename, "FontPrintPageResources: 1!" ); assert( LastDown(AFMfilename)!=Down(AFMfilename), "FontPrintPageRes!"); Child(short_name, Down(AFMfilename)); assert( is_word(type(short_name)), "FontPrintPageResources: short_name!" ); Child(ps_name, LastDown(AFMfilename)); assert( is_word(type(ps_name)), "FontPrintPageResources: ps_name!" ); switch( BackEnd ) { case POSTSCRIPT: fprintf(fp, "%s font %s\n", first ? "%%PageResources:" : "%%+", string(ps_name)); first = FALSE; break; case PDF: /* PDFWriteFontResource(fp, string(ps_name)); */ break; } } debug0(DFT, DD, "FontPrintPageResources returning."); } /* end FontPrintPageResources */ /*@@**************************************************************************/ /* */ /* FontAdvanceCurrentPage() */ /* */ /* Advance the current page. */ /* */ /*****************************************************************************/ void FontAdvanceCurrentPage(void) { debug0(DFT, DD, "FontAdvanceCurrentPage()"); while( Down(font_used) != font_used ) DeleteLink(Down(font_used)); font_curr_page++; debug0(DFT, DD, "FontAdvanceCurrentPage() returning."); } /* end FontAdvanceCurrentPage */ /*@::FontPageUsed()@**********************************************************/ /* */ /* OBJECT FontPageUsed(face) */ /* */ /* Declares that font face is used on the current page. */ /* */ /*****************************************************************************/ void FontPageUsed(OBJECT face) { debug1(DFT, DD, "FontPageUsed(%d)", font_num(face)); assert( font_page(face) < font_curr_page, "FontPageUsed!" ); Link(font_used, face); font_page(face) = font_curr_page; debug0(DFT, DD, "FontPageUsed returning"); } /* end FontPageUsed */ /*@::FontNeeded()@************************************************************/ /* */ /* OBJECT FontNeeded(fp) */ /* */ /* Writes font needed resources onto file out_fp. Returns TRUE if none. */ /* */ /*****************************************************************************/ BOOLEAN FontNeeded(FILE *fp) { BOOLEAN first_need = TRUE; OBJECT link, flink, family, face, x; for( link = Down(font_root); link != font_root; link = NextDown(link) ) { Child(family, link); for( flink = Down(family); flink != family; flink = NextDown(flink) ) { Child(face, flink); if( LastDown(face) != Down(face) ) { Child(x, LastDown(face)); fprintf(fp, "%s font %s\n", first_need ? "%%DocumentNeededResources:" : "%%+", string(x)); first_need = FALSE; } } } return first_need; } /* end FontNeeded */