/* Document.C * * Of all the parameters, those in the derived class Document are probably * the most basic. Calls to class Document routines can usually be resolved * directly into some simple PostScript output involving no nesting. * * Copyright 1992 Jonathan Monsarrat. Permission given to freely distribute, * edit and use as long as this copyright statement remains intact. * */ #include "Operator.h" #include "Global.h" #include "Document.h" #include "Font.h" #include #include float Document::start_page; Document::Document() { for(int x=0; x < LastType; x++) values[x] = 0.0; begin_command[0] = '\0'; start_page = 0; } Document::Document(Document *base) { for(int x=0; x < LastType; x++) values[x] = base->values[x]; strcpy(begin_command, base->begin_command); } Param *Document::copy() { Document *doc = new Document(this); doc->set(End, 0.0, begin_command); return doc; } int Document::set(int subtype, float value, char *replacestr) { switch(subtype) { case Begin: strcpy(begin_command,replacestr); break; case DocumentStart: values[DocumentStart] = value; break; case End: if(strcmp(begin_command,replacestr) != 0) { char message[MAXSTRING]; sprintf(message, "\\end{%s} does not match a \\begin{%s}", replacestr, begin_command); Global::files->fatal_error(message); } begin_command[0] = '\0'; break; case NewLine: Operator::do_vspace(0, 0, Global::stack->get(Environment::PLength, Length::Parameter, "\\baselineskip"), ""); break; case StartPage: start_page = value; break; case Stealth: if(!Stack::get(Environment::PDocument, Document::Comment, "")) Global::files->fatal_error( "\\stealth must go on a comment line to be compatible with LaTeX"); values[Stealth] = value; break; default: values[subtype] = value; break; } return TRUE; } float Document::get(int subtype, char *comparestr) { switch(subtype) { case CloseBrace: if(begin_command[0] != '\0') { char message[MAXSTRING]; sprintf(message, "\\begin{%s} closed with '}' instead of \\end{%s}", begin_command, begin_command); Global::files->fatal_error(message); } break; case Begin: case End: return !strcmp(comparestr, begin_command); case ShutDown: // only happens in Stack::shutdown() if(begin_command[0] != '\0') { char message[MAXSTRING]; sprintf(message, "\\begin{%s} not closed at end of input", begin_command); Global::files->fatal_error(message); } break; case StartPage: return start_page; default: break; } return values[subtype]; } void Document::revert(Param *from) { for(int subtype=0; subtype < LastType; subtype++) { switch(subtype) { case End: case Begin: case CloseBrace: case ShutDown: break; default: if(values[subtype] != from->get(subtype,"revert")) postscript_set(subtype); break; } } } void Document::postscript_set(int subtype) { switch(subtype) { case DocumentStart: // No postscript output needed default: break; } } /* Begins an environment */ void Document::begin(int paramtype, int subtype, float value, char *replacestr) { Token openbrace; if(!openbrace.match("{")) Global::files->fatal_error("Expecting '{' after \\begin statement"); Token command; if(command.match("}")) Global::files-> fatal_error("Expecting command before closing '}' in \\begin"); if(!command.match("stealth")) Stack::push(paramtype, subtype, value, replacestr); Token closebrace; if(!closebrace.match("}")) Global::files-> fatal_error("More than one word before closing '}' in \\begin"); char begin_command[MAXSTRING]; strcpy(begin_command,"\\"); // Make the token "foo" into command "\foo" char *tokentext = command.get_text(); // One of the values in parameter "Document" keeps track of the // command executed by the last \begin command, to that when the \end // command is called, we can check it against the \begin command. if(!command.match("stealth")) Stack::set(Environment::PDocument, Document::Begin, 0.0, tokentext); strcat(begin_command,tokentext); command.make_text(begin_command); command.handle(); // Handle the command. } /* Ends an environment */ void Document::end(int, int, float, char *) { Token openbrace; if(!openbrace.match("{")) Global::files->fatal_error("Expecting '{' after \\end"); Token command; // Assume it's the same as the last begin command if(command.match("}")) Global::files->fatal_error( "Expecting command before '}' in \\end statement"); if(!command.match("stealth")) { char *begin_command = command.get_text(); Stack::set(Environment::PDocument, Document::End, 0.0, begin_command); } Token closebrace; if(!closebrace.match("}")) Global::files-> fatal_error("More than one word before closing '}' in \\end"); if(command.match("stealth")) Stack::set(Environment::PDocument, Document::Stealth, 0.0, ""); else Stack::pop(0, Document::End, 0.0, ""); } void Document::documentstyle(int, int, float, char *) { Token openbrace; if(openbrace.match("[")) { Global::files->comma_delimiter(TRUE); // Commas are valid delimiters for(Token option = Token(); !option.match("]"); option = Token()) { if(option.match("")) continue; if(option.match("10pt")) { // Do nothing. This is the default. } else if(option.match("11pt")) { // Makes 11 point type the default size Stack::set(Environment::PFont, Font::Base11pt, 0.0, ""); Stack::set(Environment::PLength, Length::Parameter, 23.5, "\\bigskipamount"); Stack::set(Environment::PLength, Length::Parameter, 18.6, "\\medskipamount"); Stack::set(Environment::PLength, Length::Parameter, 16.0, "\\smallskipamount"); } else if(option.match("12pt")) { // Makes 12 point type the default size Stack::set(Environment::PFont, Font::Base12pt, 0.0, ""); Stack::set(Environment::PLength, Length::Parameter, 25.7, "\\bigskipamount"); Stack::set(Environment::PLength, Length::Parameter, 20.3, "\\medskipamount"); Stack::set(Environment::PLength, Length::Parameter, 17.4, "\\smallskipamount"); } else if(option.match("twoside")) { // Formats the output for printing on both sides of a page Global::files->warning("twoside option not supported in LameTeX"); } else if(option.match("twocolumn")) { // Makes two-column pages Global::files->warning( "twocolumn option not supported in LameTeX"); } else if(option.match("titlepage")) { // article option only! Global::files->warning("twoside option not supported in LameTeX"); } else if(option.match("openbib")) { Global::files->warning("openbib option not supported in LameTeX"); } else if(option.match("leqno")) { Global::files->warning("leqno option not supported in LameTeX"); } else if(option.match("fleqn")) { Global::files->warning("fleqn option not supported in LameTeX"); } else Global::files->warning("Unknown option in \\documentstyle[]"); } Global::files->comma_delimiter(FALSE); // Commas not valid delimiters openbrace = Token(); // An openbrace should come now } if(openbrace.match("{")) { } else Global::files->fatal_error("Expecting '{' or '[' in \\documentstyle"); Token style; if(style.match("}")) Global::files->fatal_error( "Expecting style before '}' in \\documentstyle"); if(style.match("article")) { // the article style does not have a \chapter command // the default pagestyle is plain // \flushbottom if [twoside], else \raggedbottom // numbers figures and tables consecutively throughout // if [titlepage] is used \abstract looks like normal article paragraph // \parskip is zero } else if(style.match("book")) { // \flushbottom // numbers figures and tables by chapter // there is no \abstract environment // \parskip is zero } else if(style.match("letter")) { // same as the \letter command. Very complex. // special commands \opening, \address, \signature, \cc, \encl, etc. // \raggedbottom // \parskip is non-zero Global::files->warning("letter style not supported in LameTeX"); } else if(style.match("report")) { // the default pagestyle is plain // \flushbottom if [twoside], else \raggedbottom // numbers figures and tables by chapter // \abstract is placed on a separate page // \parskip is zero } else if(style.match("slides")) { // same as the \slide command. Very complex. Global::files->warning("slides style not supported in LameTeX"); } else Global::files->warning( "Unknown style in \\documentstyle. User-defined styles not supported."); Token closebrace; if(!closebrace.match("}")) Global::files->fatal_error( "Only one word allowed between braces in \\documentstyle"); }