/* Length.C * * The official LaTeX length parameters are implemented here. They control * variables such as the paragraph indentation and the space to skip between * lines. * * 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 #include LengthParam::LengthParam(LengthParam *lp, Length *parent) { _value = lp->_value; _tokentext = lp->_tokentext; _parent = parent; } LengthParam::LengthParam(float value, char *tokentext, Length *parent) { _value = value; _tokentext = new char [strlen(tokentext)+1]; strcpy(_tokentext,tokentext); _parent = parent; } LengthParam::~LengthParam() { delete _tokentext; } int LengthParam::compare(const void *length1, const void *length2) { LengthParam **len1; LengthParam **len2; len1 = (LengthParam **) length1; len2 = (LengthParam **) length2; return strcmp((*len1)->_tokentext, (*len2)->_tokentext); } void LengthParam::set(float value) { if(_value != value) { if(value > _value && match("\\baselineskip")) Global::files->readjust_vspace = value; _value = value; postscript_set(); } } float LengthParam::get() { return _value; } void LengthParam::revert(Length *from) { LengthParam **lp; lp = from->fetch(_tokentext); if(_value != (*lp)->get()) postscript_set(); } void LengthParam::postscript_set() { LengthParam **lp; Global::files->outfile << endl; if(match("\\baselineskip")) Global::files->outfile << _value << " BASELINESKIP" << endl; else if(match("\\parindent")) Global::files->outfile << "/parindent " << _value << " def" << endl; else if(match("\\parskip")) Global::files->outfile << "/parskip " << _value << " def" << endl; else if(match("\\textheight")) { lp = _parent->fetch("\\topmargin"); Global::files->outfile << "/bottommargin " << 684.0 - _value - (*lp)->get() << " def" << endl; } else if(match("\\textwidth") || match("\\linewidth")) { lp = _parent->fetch("\\oddsidemargin"); Global::files->outfile << "/rightmargin " << 540.0 - _value - (*lp)->get() << " def" << endl; } else if(match("\\topmargin")) { Global::files->outfile << "/topmargin " << _value+108 << " def" << endl; lp = _parent->fetch("\\textheight"); Global::files->outfile << "/bottommargin " << 684.0 - _value - (*lp)->get() << " def" << endl; } else if(match("\\oddsidemargin") || match("\\evensidemargin")) { Global::files->outfile << "/leftmargin " << _value+72 << " def" << endl; lp = _parent->fetch("\\textwidth"); Global::files->outfile << "/rightmargin " << 540.0 - _value - (*lp)->get() << " def" << endl; } } int LengthParam::match(char *tokentext) { return !strcmp(_tokentext,tokentext); } Length::Length() { /* The LaTeX defaults for the LaTeX Length Parameters */ numvalues = 0; makeparam( 12.0, "\\baselineskip"); // space between lines makeparam( 1.0, "\\baselinestretch"); // ditto, in units of lines makeparam(126.0, "\\linewidth"); // same as textwidth makeparam( 18.0, "\\parindent"); // paragraph indentation makeparam( 0.0, "\\parskip"); // space between paragraphs makeparam(540.0, "\\textheight"); // height of the page makeparam(360.0, "\\textwidth"); // width of the page makeparam( 54.0, "\\topmargin"); // top margin makeparam( 54.0, "\\oddsidemargin"); // left margin, basically makeparam( 54.0, "\\evensidemargin"); // left margin, basically makeparam( 21.4, "\\bigskipamount"); // big vertical skip makeparam( 16.9, "\\medskipamount"); // medium vertical skip makeparam( 14.5, "\\smallskipamount"); // small vertical skip makeparam( 28.34,"cm"); // centimeters (28 pts) makeparam( 10.0, "em"); // width of letter M in current font makeparam( 8.0, "ex"); // width of letter X in current font makeparam( 72.0, "in"); // inches (72 pts) makeparam( 12.0, "pc"); // Picas (1pc = 12pt) makeparam( 1.0, "pt"); // Points makeparam( 2.83,"mm"); // millimeters for(int x=0; x < numvalues; x++) // Initialize the variables postscript_set(x); } /* The class Length contains an array of pointers. The automatic definition * created by the C++ compiler just copies over these pointers to the new * Length. But we want the new Length being created to actually have copies * of all the LengthParams in *values[], not pointers to the same ones! * So an explicit definition is written here. */ Length::Length(Length *base) { numvalues = base->numvalues; for(int index=0; index < numvalues; index++) values[index] = new LengthParam(base->values[index], this); } Length::~Length() { for(int x=0; x < numvalues; x++) delete values[x]; } Param *Length::copy() { return new Length(this); } // Fetches the LengthParam in array values with given name LengthParam **Length::fetch(char *tokenstr) { LengthParam key(0.0, tokenstr, this); LengthParam *keyptr = &key; LengthParam **keyptrptr = &keyptr; LengthParam **lp = (LengthParam **) bsearch((char *)keyptrptr, (char *) values, numvalues, sizeof(LengthParam *), LengthParam::compare); return lp; } void Length::makeparam(float value, char *tokentext) { values[numvalues++] = new LengthParam(value, tokentext, this); qsort((char*)values, numvalues, sizeof(LengthParam *), LengthParam::compare); } void Length::set_lp(LengthParam *lp, float value) { LengthParam **skip; lp->set(value); if(lp->match("\\baselinestretch")) { skip = fetch("\\baselineskip"); float fontsize = Stack::get(Environment::PFont, Font::Size, ""); (*skip)->set(value * fontsize * 1.2); // 1.2 is magic spacing number } else if(lp->match("\\oddsidemargin")) { skip = fetch("\\evensidemargin"); (*skip)->set(value); } else if(lp->match("\\evensidemargin")) { skip = fetch("\\oddsidemargin"); (*skip)->set(value); } } // Set the value of the LengthParam in the values array with given name // to the given value. Returns success boolean. int Length::set(int, float value, char *tokentext) { LengthParam **lp; if((lp=fetch(tokentext)) == NULL) return FALSE; set_lp(*lp,value); return TRUE; } float Length::get(int subtype, char *tokentext) { if(subtype == Parse_Length) return length_argument(); LengthParam **lp; // Get parameter value if((lp=fetch(tokentext)) == NULL) { char message[MAXSTRING]; sprintf(message, "No length parameter %s defined", tokentext); Global::files->fatal_error(message); } return (*lp)->get(); } void Length::postscript_set(int index) { values[index]->postscript_set(); } void Length::revert(Param *from) { for(int index=0; index < numvalues; index++) values[index]->revert((Length *)from); } /* Parses an argument between braces to be used in a length function * such as \addtolength and \setlength. Returns the length in points. */ float Length::length_argument() { char *tokenname; LengthParam **lp; float val = 1.0; Global::files->set_parsing_length(TRUE); for(Token command; !command.match("}"); command = Token()) { if(command.match("")) continue; tokenname = command.get_text(); if(tokenname[0] >= '0' && tokenname[0] <= '9' || tokenname[0]=='-' || tokenname[0]=='.') { float t; // A number sscanf(tokenname,"%f",&t); val *= t; } else { // A variable lp = fetch(tokenname); if(!lp) { char message[MAXSTRING]; sprintf(message, "Undefined length parameter %s", tokenname); Global::files->fatal_error(message); } val *= (*lp)->get(); } } Global::files->set_parsing_length(FALSE); return val; } void Length::addtolength(int, int, float, char *) { Token openbrace; if(!openbrace.match("{")) Global::files->fatal_error( "Expecting '{' after \\addtolength statement"); Token lengthparam; if(lengthparam.match("}")) Global::files->fatal_error( "Expecting lengthparam before closing '}' in \\addtolength"); LengthParam **lp = fetch(lengthparam.get_text()); Token closebrace; if(!closebrace.match("}")) Global::files->fatal_error( "More than one word before closing '}' in \\addtolength"); openbrace = Token(); if(!openbrace.match("{")) Global::files->fatal_error( "Expecting second '{' after \\addtolength statement"); set_lp(*lp,(*lp)->get()+Length::length_argument()); } void Length::newlength(int, int, float, char *) { Token openbrace; if(!openbrace.match("{")) Global::files->fatal_error("Expecting '{' after \\newlength statement"); Token lengthparam; if(lengthparam.match("}")) Global::files->fatal_error( "Expecting lengthparam before closing '}' in \\newlength"); makeparam(0.0,lengthparam.get_text()); Token closebrace; if(!closebrace.match("}")) Global::files->fatal_error( "More than one word before closing '}' in \\newlength"); } void Length::setlength(int, int, float, char *) { Token openbrace; if(!openbrace.match("{")) Global::files->fatal_error("Expecting '{' after \\setlength statement"); Token lengthparam; if(lengthparam.match("}")) Global::files->fatal_error( "Expecting lengthparam before closing '}' in \\setlength"); LengthParam **lp = fetch(lengthparam.get_text()); Token closebrace; if(!closebrace.match("}")) Global::files->fatal_error( "More than one word before closing '}' in \\setlength"); openbrace = Token(); if(!openbrace.match("{")) Global::files->fatal_error( "Expecting second '{' after \\setlength statement"); set_lp(*lp,Length::length_argument()); }