/* Font.C * * Anything having to do with changing the style, family, or size of the * fonts is handled through this Font class. * * Copyright 1992 Jonathan Monsarrat. Permission given to freely distribute, * edit and use as long as this copyright statement remains intact. * */ #include "Global.h" #include "Font.h" #include #include #include FontUsed *Font::_fonts_used[MAXFONTS]; int Font::_num_fonts_used; // Build a list of fonts actually used, along with the characters // used for each font. FontUsed::FontUsed(char *style, char *petname, int font) { strcpy(_style, style); strcpy(_petname, petname); _font = font; for(int x=0; x<256; x++) _charused[x] = (x >= '0' && x <= '9') ? 1 : 0; } void FontUsed::used(char *textstr) { for(int x=0; textstr[x]; x++) _charused[textstr[x]] = 1; } int FontUsed::font() { return _font; } void FontUsed::dump(ofstream &outfile) { outfile << "{" << _style << endl; int num=0; for(int x=0; x < 256; x++) if(_charused[x]) { if(num++ > 25) { outfile << endl << endl; num=0; } switch(x) { case '&': case '#': case '{': case '}': case '_': case '%': case '$': outfile << '\\'; default: outfile << (char) x << " "; break; } } outfile << "`` '' ." << endl; outfile << "}" << endl; } int FontUsed::match(char *textstr) { return !strcmp(textstr,_petname); } char *FontUsed::petname() { return _petname; } Font::Font() { _basesize = 10; _fontsize = 10; strcpy(_fontstyle,"rm"); strcpy(_fontsizecmd,"\\normalsize"); _fonts_used[0] = new FontUsed("\\normalsize\\rm", "/cm10rm", 1); _num_fonts_used = 1; Global::files->outfile << endl; _currentused = 0; _pending = 1; _pending_whitespace = 1; } Font::Font(Font *base) { _basesize = base->_basesize; _fontsize = base->_fontsize; strcpy(_fontstyle, base->_fontstyle); strcpy(_fontsizecmd, base->_fontsizecmd); _currentused = base->_currentused; _pending = base->_pending; _pending_whitespace = base->_pending_whitespace; } Font::~Font() { for(int x=0; x < _num_fonts_used; x++) delete _fonts_used[x]; } Param *Font::copy() { return new Font(this); } void Font::newsize(float val) { if(_fontsize != val) { _fontsize = (int) val; /* We are updating the size of the font, so we had better update * also the amount of space to skip between lines. This is done * by doing a set statement on \baselinestretch. */ if(!Global::files->plain_text_output) { Stack::set(Environment::PLength, 0, Stack::get(Environment::PLength, Length::Parameter, "\\baselinestretch"), "\\baselinestretch"); } _pending = 1; // Print the postscript command for this. _pending_whitespace = Global::files->whitespace_next(); } } void Font::set_fontstyle(char *style) { if(strcmp(_fontstyle,style)!=0) { strcpy(_fontstyle,style); _pending = 1; // Print the postscript command for this. _pending_whitespace = Global::files->whitespace_next(); } } int Font::set(int subtype, float value, char *textstr) { /* The Font Family is hard-coded to "cm" for Computer Modern */ switch(subtype) { /* Base sizes */ case Base10pt: // Do nothing; this is the default. break; case Base11pt: delete _fonts_used[0]; _fonts_used[0] = new FontUsed("\\normalsize\\rm", "/cm11rm", 1); Global::files->outfile << endl; Global::files->outfile << "/basefont /cm11rm def" << endl; _basesize = 11; _fontsize = 11; _pending = 1; _pending_whitespace = Global::files->whitespace_next(); break; case Base12pt: delete _fonts_used[0]; _fonts_used[0] = new FontUsed("\\normalsize\\rm", "/cm12rm", 1); Global::files->outfile << endl; Global::files->outfile << "/basefont /cm12rm def" << endl; _basesize = 12; _fontsize = 12; _pending = 1; _pending_whitespace = Global::files->whitespace_next(); break; /* _Font styles */ case Italic: if(strcmp(_fontstyle,"it")==0) strcpy(_fontstyle,"rm"); // Double Italic is Roman! else strcpy(_fontstyle,"it"); _pending = 1; // Print the postscript command for this. _pending_whitespace = Global::files->whitespace_next(); break; case Bold: set_fontstyle("bf"); break; case Roman: set_fontstyle("rm"); break; case SansSerif: set_fontstyle("sf"); break; case Slant: set_fontstyle("sl"); break; case SmallCaps: set_fontstyle("sc"); break; case Typewriter: set_fontstyle("tt"); break; /* Sizes relative to the base size */ case Tiny: strcpy(_fontsizecmd,"\\tiny"); switch(_basesize) { case 10: default: newsize(4); break; case 11: case 12: newsize(6); break; } break; case Scriptsize: strcpy(_fontsizecmd,"\\scriptsize"); switch(_basesize) { case 10: default: newsize(6); break; case 11: case 12: newsize(7); break; } break; case Footnotesize: strcpy(_fontsizecmd,"\\footnotesize"); switch(_basesize) { case 10: default: newsize(7); break; case 11: newsize(8); break; case 12: newsize(10); break; } break; case Small: strcpy(_fontsizecmd,"\\small"); switch(_basesize) { case 10: default: newsize(8); break; case 11: newsize(10); break; case 12: newsize(11); break; } break; case Normalsize: strcpy(_fontsizecmd,"\\normalsize"); newsize(_basesize); break; case large: strcpy(_fontsizecmd,"\\large"); switch(_basesize) { case 10: default: newsize(11); break; case 11: newsize(12); break; case 12: newsize(13); break; } break; case Large: strcpy(_fontsizecmd,"\\Large"); switch(_basesize) { case 10: case 11: default: newsize(13); break; case 12: newsize(15); break; } break; case LARGE: strcpy(_fontsizecmd,"\\LARGE"); switch(_basesize) { case 10: case 11: default: newsize(15); break; case 12: newsize(18); break; } break; case huge: strcpy(_fontsizecmd,"\\huge"); switch(_basesize) { case 10: case 11: default: newsize(18); break; case 12: newsize(22); break; } break; case Huge: strcpy(_fontsizecmd,"\\Huge"); newsize(22); break; case Currentused: _currentused = do_command(textstr, textstr, Global::files->whitespace_next(), 0); break; case Pending: if(_pending) { postscript_set(0); _pending = 0; } break; case Size: set_fontstyle(""); newsize(value); break; /* These font styles aren't handled right now */ case Used: _fonts_used[_currentused]->used(textstr); break; case FunnyPrint: Global::files->outfile << endl << "[ 0"; if(Global::files->whitespace_next()) Global::files->outfile << " false"; else Global::files->outfile << " true"; Global::files->outfile << " ] NewFont " << textstr << " NW " << endl; _pending = 1; // Print the postscript command for this. _pending_whitespace = Global::files->whitespace_next(); // postscript_set(0); break; default: break; } return TRUE; } float Font::get(int subtype, char *comparestr) { switch(subtype) { case Base: return(_basesize); case Currentused: return(_currentused); case Pending: return(_pending); case Style: return(!strcmp(comparestr,_fontstyle)); case ShutDown: if(!Global::files->plain_text_output) shutdown(); break; case Size: default: break; } return(_fontsize); } void Font::revert(Param *from) { int size = (int)from->get(Size,""); if(size != _fontsize || !from->get(Style,_fontstyle) || from->get(Pending,"") || _currentused != from->get(Currentused,"")) _pending = 1; // Print the postscript command for this. _pending_whitespace = Global::files->whitespace_next(); } void Font::use_command(char *style, char *petname, int font) { _fonts_used[_num_fonts_used++] = new FontUsed(style,petname, font); } int Font::do_command(char *style, char *petname, int whitespace, int font) { for(int used=0; used < _num_fonts_used; used++) if(_fonts_used[used]->match(petname)) break; if(_num_fonts_used >= MAXFONTS) Global::files->fatal_error("Too many fonts"); if(used >= _num_fonts_used) use_command(style, petname, font); Global::files->outfile << endl << "[ " << used+1; if(whitespace) Global::files->outfile << " false"; else Global::files->outfile << " true"; Global::files->outfile << " ] NewFont % " << petname << endl; return(used); } void Font::postscript_set(int) { /* The Font Family is hard-coded to "cm" for Computer Modern */ char petname[MAXSTRING]; sprintf(petname,"/cm%d%s",_fontsize,_fontstyle); char style[MAXSTRING]; sprintf(style,"%s\\%s",_fontsizecmd,_fontstyle); _currentused = do_command(style, petname, _pending_whitespace, 1); } // This is an important routine which builds a dummy latex files // Just to trick LaTeX into giving us the proper fonts. void Font::shutdown() { cerr << "Making dummy file for snarfing LaTeX fonts..." << endl; ofstream dummy("lametex.tex"); dummy << "\\documentstyle[" << _basesize << "pt]{report}" << endl; dummy << "\\pagestyle{empty}" << endl; dummy << "\\begin{document}" << endl; for(int x=0; x < _num_fonts_used; x++) _fonts_used[x]->dump(dummy); dummy << "\\end{document}" << endl; dummy.close(); cerr << "Snarfing LaTeX fonts..." << endl; system( LATEX ); system( DVIPS ); ifstream latex_postscript("lametex.ps"); if(!latex_postscript) { // Open file failed? cerr << "Unable to open postscript temp file lametex.ps for reading" << endl; exit (-1); } ofstream lametex_postscript("lametex.PS"); if(!lametex_postscript) { // Open file failed? cerr << "Unable to open postscript temp file lametex.PS for writing" << endl; exit (-1); } char line[MAXSTRING]; while(!latex_postscript.eof()) { latex_postscript.getline(line, MAXSTRING, '\n'); if(strstr(line,"TeXDict begin")==line) break; } lametex_postscript << line << endl; while(!latex_postscript.eof()) { latex_postscript.getline(line, MAXSTRING, '\n'); lametex_postscript << line << endl; if(strstr(line,"%%EndSetup")) break; } int y; for(x=_num_fonts_used-1, y=0; x >= 0 ; x--) if(_fonts_used[x]->font()) lametex_postscript << _fonts_used[x]->petname() << "{ pop F" << (char) ('a' + y++) << " } bind def" << endl; lametex_postscript << "/fontnames [" << endl; lametex_postscript << "/TIMESROMAN" << endl; for(x=0; x < _num_fonts_used; x++) lametex_postscript << _fonts_used[x]->petname() << endl; lametex_postscript << "] def" << endl; latex_postscript.close(); lametex_postscript.close(); system("rm lametex.tex lametex.dvi lametex.ps"); cerr << " ** SNARF! **" << endl; }