diff options
Diffstat (limited to 'z37.c')
-rw-r--r-- | z37.c | 1480 |
1 files changed, 1480 insertions, 0 deletions
@@ -0,0 +1,1480 @@ +/*@z37.c:Font Service:Declarations@*******************************************/ +/* */ +/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.17) */ +/* COPYRIGHT (C) 1991, 1999 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 <family> <face> { <inside> }, where */ +/* <inside> ::= 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 */ |