X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferParams.cpp;h=9302a35c294e43881eeebd47bbbf8816bbb004f9;hb=700af7e7011f102d92222744791638ff2db18f94;hp=8947763561c07773ffd4557a3069e7635d5dabae;hpb=f910f7d6e8e9f651632fc62a459e7f8277b7e520;p=lyx.git diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index 8947763561..9302a35c29 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -18,44 +18,43 @@ #include "BufferParams.h" #include "Author.h" +#include "LayoutFile.h" #include "BranchList.h" +#include "buffer_funcs.h" #include "Bullet.h" -#include "debug.h" +#include "Color.h" #include "Encoding.h" -#include "gettext.h" #include "Language.h" #include "LaTeXFeatures.h" -#include "Messages.h" -#include "Color.h" +#include "ModuleList.h" #include "Font.h" #include "Lexer.h" #include "LyXRC.h" -#include "TextClassList.h" #include "OutputParams.h" #include "Spacing.h" #include "TexRow.h" #include "VSpace.h" +#include "PDFOptions.h" #include "frontends/alert.h" -#include "support/lyxalgo.h" // for lyx::count +#include "insets/InsetListingsParams.h" + #include "support/convert.h" +#include "support/debug.h" +#include "support/docstream.h" +#include "support/FileName.h" +#include "support/filetools.h" +#include "support/gettext.h" +#include "support/Messages.h" #include "support/Translator.h" +#include "support/lstrings.h" -#include - +#include #include -using std::endl; -using std::string; -using std::istringstream; -using std::ostream; -using std::ostringstream; -using std::pair; - -using lyx::support::bformat; -using lyx::support::rtrim; -using lyx::support::tokenPos; +using namespace std; +using namespace lyx::support; static char const * const string_paragraph_separation[] = { @@ -96,13 +95,14 @@ namespace lyx { namespace { // Paragraph separation -typedef Translator ParSepTranslator; +typedef Translator ParSepTranslator; ParSepTranslator const init_parseptranslator() { - ParSepTranslator translator(string_paragraph_separation[0], BufferParams::PARSEP_INDENT); - translator.addPair(string_paragraph_separation[1], BufferParams::PARSEP_SKIP); + ParSepTranslator translator + (string_paragraph_separation[0], BufferParams::ParagraphIndentSeparation); + translator.addPair(string_paragraph_separation[1], BufferParams::ParagraphSkipSeparation); return translator; } @@ -115,17 +115,18 @@ ParSepTranslator const & parseptranslator() // Quotes language -typedef Translator QuotesLangTranslator; +typedef Translator QuotesLangTranslator; QuotesLangTranslator const init_quoteslangtranslator() { - QuotesLangTranslator translator(string_quotes_language[0], InsetQuotes::EnglishQ); - translator.addPair(string_quotes_language[1], InsetQuotes::SwedishQ); - translator.addPair(string_quotes_language[2], InsetQuotes::GermanQ); - translator.addPair(string_quotes_language[3], InsetQuotes::PolishQ); - translator.addPair(string_quotes_language[4], InsetQuotes::FrenchQ); - translator.addPair(string_quotes_language[5], InsetQuotes::DanishQ); + QuotesLangTranslator translator + (string_quotes_language[0], InsetQuotes::EnglishQuotes); + translator.addPair(string_quotes_language[1], InsetQuotes::SwedishQuotes); + translator.addPair(string_quotes_language[2], InsetQuotes::GermanQuotes); + translator.addPair(string_quotes_language[3], InsetQuotes::PolishQuotes); + translator.addPair(string_quotes_language[4], InsetQuotes::FrenchQuotes); + translator.addPair(string_quotes_language[5], InsetQuotes::DanishQuotes); return translator; } @@ -138,10 +139,10 @@ QuotesLangTranslator const & quoteslangtranslator() // Paper size -typedef Translator PaperSizeTranslator; +typedef Translator PaperSizeTranslator; -PaperSizeTranslator const init_papersizetranslator() +static PaperSizeTranslator initPaperSizeTranslator() { PaperSizeTranslator translator(string_papersize[0], PAPER_DEFAULT); translator.addPair(string_papersize[1], PAPER_CUSTOM); @@ -160,7 +161,7 @@ PaperSizeTranslator const init_papersizetranslator() PaperSizeTranslator const & papersizetranslator() { - static PaperSizeTranslator translator = init_papersizetranslator(); + static PaperSizeTranslator translator = initPaperSizeTranslator(); return translator; } @@ -185,13 +186,13 @@ PaperOrientationTranslator const & paperorientationtranslator() // Page sides -typedef Translator SidesTranslator; +typedef Translator SidesTranslator; SidesTranslator const init_sidestranslator() { - SidesTranslator translator(1, TextClass::OneSide); - translator.addPair(2, TextClass::TwoSides); + SidesTranslator translator(1, OneSide); + translator.addPair(2, TwoSides); return translator; } @@ -266,15 +267,6 @@ SpaceTranslator const & spacetranslator() } -textclass_type defaultTextclass() -{ - // Initialize textclass to point to article. if `first' is - // true in the returned pair, then `second' is the textclass - // number; if it is false, second is 0. In both cases, second - // is what we want. - return textclasslist.numberOfClass("article").second; -} - } // anon namespace @@ -285,18 +277,21 @@ public: AuthorList authorlist; BranchList branchlist; - boost::array temp_bullets; - boost::array user_defined_bullets; + vector extraEmbeddedFiles; + Bullet temp_bullets[4]; + Bullet user_defined_bullets[4]; Spacing spacing; /** This is the amount of space used for paragraph_separation "skip", * and for detached paragraphs in "indented" documents. */ VSpace defskip; + PDFOptions pdfoptions; + LayoutFileIndex baseClass_; }; BufferParams::Impl::Impl() - : defskip(VSpace::MEDSKIP) + : defskip(VSpace::MEDSKIP), baseClass_(string("")) { // set initial author // FIXME UNICODE @@ -320,10 +315,12 @@ void BufferParams::MemoryTraits::destroy(BufferParams::Impl * ptr) BufferParams::BufferParams() - : textclass(defaultTextclass()), pimpl_(new Impl) + : pimpl_(new Impl) { - paragraph_separation = PARSEP_INDENT; - quotes_language = InsetQuotes::EnglishQ; + setBaseClass(defaultBaseclass()); + makeDocumentClass(); + paragraph_separation = ParagraphIndentSeparation; + quotes_language = InsetQuotes::EnglishQuotes; fontsize = "default"; /* PaperLayout */ @@ -349,10 +346,12 @@ BufferParams::BufferParams() fontsTypewriterScale = 100; inputenc = "auto"; graphicsDriver = "default"; - sides = TextClass::OneSide; + sides = OneSide; columns = 1; + listings_params = string(); pagestyle = "default"; compressed = false; + embedded = lyxrc.use_bundled_format; for (int iter = 0; iter < 4; ++iter) { user_defined_bullet(iter) = ITEMIZE_DEFAULTS[iter]; temp_bullet(iter) = ITEMIZE_DEFAULTS[iter]; @@ -360,11 +359,7 @@ BufferParams::BufferParams() } -BufferParams::~BufferParams() -{} - - -docstring const BufferParams::B_(string const & l10n) const +docstring BufferParams::B_(string const & l10n) const { BOOST_ASSERT(language); return getMessages(language->code()).get(l10n); @@ -383,6 +378,18 @@ AuthorList const & BufferParams::authors() const } +vector & BufferParams::extraEmbeddedFiles() +{ + return pimpl_->extraEmbeddedFiles; +} + + +vector const & BufferParams::extraEmbeddedFiles() const +{ + return pimpl_->extraEmbeddedFiles; +} + + BranchList & BufferParams::branchlist() { return pimpl_->branchlist; @@ -435,6 +442,18 @@ Spacing const & BufferParams::spacing() const } +PDFOptions & BufferParams::pdfoptions() +{ + return pimpl_->pdfoptions; +} + + +PDFOptions const & BufferParams::pdfoptions() const +{ + return pimpl_->pdfoptions; +} + + VSpace const & BufferParams::getDefSkip() const { return pimpl_->defskip; @@ -447,41 +466,50 @@ void BufferParams::setDefSkip(VSpace const & vs) } -string const BufferParams::readToken(Lexer & lex, string const & token) +string BufferParams::readToken(Lexer & lex, string const & token, + FileName const & filepath, FileName const & temppath) { if (token == "\\textclass") { lex.next(); string const classname = lex.getString(); - pair pp = - textclasslist.numberOfClass(classname); - if (pp.first) { - textclass = pp.second; + // if there exists a local layout file, ignore the system one + // NOTE: in this case, the textclass (.cls file) is assumed to be available. + string tcp; + LayoutFileList & bcl = LayoutFileList::get(); + if (!temppath.empty()) + tcp = bcl.addLayoutFile(classname, temppath.absFilename(), LayoutFileList::Embedded); + if (tcp.empty() && !filepath.empty()) + tcp = bcl.addLayoutFile(classname, filepath.absFilename(), LayoutFileList::Local); + if (!tcp.empty()) + setBaseClass(tcp); + else if (bcl.haveClass(classname)) { + setBaseClass(classname); } else { - // if text class does not exist, try to load it from filepath - pp = textclasslist.addTextClass(classname, filepath); - if (pp.first) { - textclass = pp.second; - } else { - textclass = defaultTextclass(); - return classname; - } + // a warning will be given for unknown class + setBaseClass(defaultBaseclass()); + return classname; } - // FIXME: isTeXClassAvailable will try to load the layout file, but will - // fail because of the lack of path info. Warnings will be given although - // the layout file will be correctly loaded later. - if (!getTextClass().isTeXClassAvailable()) { + // FIXME: this warning will be given even if there exists a local .cls + // file. Even worse, the .lyx file can not be compiled or exported + // because the textclass is marked as unavilable. + if (!baseClass()->isTeXClassAvailable()) { docstring const msg = bformat(_("The layout file requested by this document,\n" - "%1$s.layout,\n" - "is not usable. This is probably because a LaTeX\n" - "class or style file required by it is not\n" - "available. See the Customization documentation\n" - "for more information.\n"), from_utf8(classname)); + "%1$s.layout,\n" + "is not usable. This is probably because a LaTeX\n" + "class or style file required by it is not\n" + "available. See the Customization documentation\n" + "for more information.\n"), from_utf8(classname)); frontend::Alert::warning(_("Document class not available"), msg + _("LyX will not be able to produce output.")); - } + } + } else if (token == "\\begin_preamble") { readPreamble(lex); + } else if (token == "\\begin_local_layout") { + readLocalLayout(lex); + } else if (token == "\\begin_modules") { + readModules(lex); } else if (token == "\\options") { lex.eatLine(); options = lex.getString(); @@ -513,7 +541,11 @@ string const BufferParams::readToken(Lexer & lex, string const & token) paragraph_separation = parseptranslator().find(parsep); } else if (token == "\\defskip") { lex.next(); - pimpl_->defskip = VSpace(lex.getString()); + string defskip = lex.getString(); + if (defskip == "defskip") + // this is invalid + defskip = "medskip"; + pimpl_->defskip = VSpace(defskip); } else if (token == "\\quotes_language") { string quotes_lang; lex >> quotes_lang; @@ -565,7 +597,7 @@ string const BufferParams::readToken(Lexer & lex, string const & token) branch_ptr->setColor(color); // Update also the Color table: if (color == "none") - color = lcolor.getX11Name(Color::background); + color = lcolor.getX11Name(Color_background); // FIXME UNICODE lcolor.setColor(to_utf8(branch), color); @@ -599,10 +631,16 @@ string const BufferParams::readToken(Lexer & lex, string const & token) lex >> headsep; } else if (token == "\\footskip") { lex >> footskip; + } else if (token == "\\columnsep") { + lex >> columnsep; } else if (token == "\\paperfontsize") { lex >> fontsize; } else if (token == "\\papercolumns") { lex >> columns; + } else if (token == "\\listings_params") { + string par; + lex >> par; + listings_params = InsetListingsParams(par).params(); } else if (token == "\\papersides") { int psides; lex >> psides; @@ -627,7 +665,27 @@ string const BufferParams::readToken(Lexer & lex, string const & token) spacing().set(spacetranslator().find(nspacing), tmp_val); } else if (token == "\\float_placement") { lex >> float_placement; + + } else if (prefixIs(token, "\\pdf_") || token == "\\use_hyperref") { + string toktmp = pdfoptions().readToken(lex, token); + if (!toktmp.empty()) { + lyxerr << "PDFOptions::readToken(): Unknown token: " << + toktmp << endl; + return toktmp; + } + } else if (token == "\\extra_embedded_files") { + extraEmbeddedFiles().clear(); + string par; + lex >> par; + string tmp; + par = split(par, tmp, ','); + while (!tmp.empty()) { + extraEmbeddedFiles().push_back(tmp); + par = split(par, tmp, ','); + } } else { + lyxerr << "BufferParams::readToken(): Unknown token: " << + token << endl; return token; } @@ -641,7 +699,7 @@ void BufferParams::writeFile(ostream & os) const // Prints out the buffer info into the .lyx file given by file // the textclass - os << "\\textclass " << textclasslist[textclass].name() << '\n'; + os << "\\textclass " << baseClass()->name() << '\n'; // then the preamble if (!preamble.empty()) { @@ -656,6 +714,24 @@ void BufferParams::writeFile(ostream & os) const if (!options.empty()) { os << "\\options " << options << '\n'; } + + //the modules + if (!layoutModules_.empty()) { + os << "\\begin_modules" << '\n'; + LayoutModuleList::const_iterator it = layoutModules_.begin(); + for (; it != layoutModules_.end(); it++) + os << *it << '\n'; + os << "\\end_modules" << '\n'; + } + + // local layout information + if (!local_layout.empty()) { + // remove '\n' from the end + string const tmplocal = rtrim(local_layout, "\n"); + os << "\\begin_local_layout\n" + << tmplocal + << "\n\\end_local_layout\n"; + } // then the text parameters if (language != ignore_language) @@ -677,6 +753,7 @@ void BufferParams::writeFile(ostream & os) const os << "\\paperfontsize " << fontsize << '\n'; spacing().writeFile(os); + pdfoptions().writeFile(os); os << "\\papersize " << string_papersize[papersize] << "\n\\use_geometry " << convert(use_geometry) @@ -724,6 +801,9 @@ void BufferParams::writeFile(ostream & os) const if (!footskip.empty()) os << "\\footskip " << VSpace(footskip).asLyXCommand() << '\n'; + if (!columnsep.empty()) + os << "\\columnsep " + << VSpace(columnsep).asLyXCommand() << '\n'; os << "\\secnumdepth " << secnumdepth << "\n\\tocdepth " << tocdepth << "\n\\paragraph_separation " @@ -734,6 +814,9 @@ void BufferParams::writeFile(ostream & os) const << "\n\\papercolumns " << columns << "\n\\papersides " << sides << "\n\\paperpagestyle " << pagestyle << '\n'; + if (!listings_params.empty()) + os << "\\listings_params \"" << + InsetListingsParams(listings_params).encodedString() << "\"\n"; for (int i = 0; i < 4; ++i) { if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) { if (user_defined_bullet(i).getFont() != -1) { @@ -757,8 +840,104 @@ void BufferParams::writeFile(ostream & os) const AuthorList::Authors::const_iterator a_it = pimpl_->authorlist.begin(); AuthorList::Authors::const_iterator a_end = pimpl_->authorlist.end(); for (; a_it != a_end; ++a_it) { - os << "\\author " << a_it->second << "\n"; + if (a_it->second.used()) + os << "\\author " << a_it->second << "\n"; + else + os << "\\author " << Author() << "\n"; } + + vector::const_iterator e_it = extraEmbeddedFiles().begin(); + vector::const_iterator e_end = extraEmbeddedFiles().end(); + os << "\\extra_embedded_files \""; + bool first = true; + for (; e_it != e_end; ++e_it) { + if (!first) + os << ","; + else + first = false; + os << *e_it; + } + os << "\"\n"; +} + + +void BufferParams::validate(LaTeXFeatures & features) const +{ + features.require(documentClass().requires()); + + if (outputChanges) { + bool dvipost = LaTeXFeatures::isAvailable("dvipost"); + bool xcolorsoul = LaTeXFeatures::isAvailable("soul") && + LaTeXFeatures::isAvailable("xcolor"); + + switch (features.runparams().flavor) { + case OutputParams::LATEX: + if (dvipost) { + features.require("ct-dvipost"); + features.require("dvipost"); + } else if (xcolorsoul) { + features.require("ct-xcolor-soul"); + features.require("soul"); + features.require("xcolor"); + } else { + features.require("ct-none"); + } + break; + case OutputParams::PDFLATEX: + if (xcolorsoul) { + features.require("ct-xcolor-soul"); + features.require("soul"); + features.require("xcolor"); + // improves color handling in PDF output + features.require("pdfcolmk"); + } else { + features.require("ct-none"); + } + break; + default: + break; + } + } + + // Floats with 'Here definitely' as default setting. + if (float_placement.find('H') != string::npos) + features.require("float"); + + // AMS Style is at document level + if (use_amsmath == package_on + || documentClass().provides("amsmath")) + features.require("amsmath"); + if (use_esint == package_on) + features.require("esint"); + + // Document-level line spacing + if (spacing().getSpace() != Spacing::Single && !spacing().isDefault()) + features.require("setspace"); + + // the bullet shapes are buffer level not paragraph level + // so they are tested here + for (int i = 0; i < 4; ++i) { + if (user_defined_bullet(i) == ITEMIZE_DEFAULTS[i]) + continue; + int const font = user_defined_bullet(i).getFont(); + if (font == 0) { + int const c = user_defined_bullet(i).getCharacter(); + if (c == 16 + || c == 17 + || c == 25 + || c == 26 + || c == 31) { + features.require("latexsym"); + } + } else if (font == 1) { + features.require("amssymb"); + } else if (font >= 2 && font <= 5) { + features.require("pifont"); + } + } + + if (pdfoptions().use_hyperref) + features.require("hyperref"); } @@ -767,7 +946,7 @@ bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features, { os << "\\documentclass"; - TextClass const & tclass = getTextClass(); + DocumentClass const & tclass = documentClass(); ostringstream clsoptions; // the document class options. @@ -778,7 +957,7 @@ bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features, } // custom, A3, B3 and B4 paper sizes need geometry - bool nonstandard_papersize = papersize == PAPER_B3 + bool nonstandard_papersize = papersize == PAPER_B3 || papersize == PAPER_B4 || papersize == PAPER_A3 || papersize == PAPER_CUSTOM; @@ -815,10 +994,10 @@ bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features, // if needed if (sides != tclass.sides()) { switch (sides) { - case TextClass::OneSide: + case OneSide: clsoptions << "oneside,"; break; - case TextClass::TwoSides: + case TwoSides: clsoptions << "twoside,"; break; } @@ -851,7 +1030,13 @@ bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features, language_options << ','; language_options << language->babel(); } - if (lyxrc.language_global_options) + // when Vietnamese is used, babel must directly be loaded with the + // language options, not in the class options, see + // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129417.html + size_t viet = language_options.str().find("vietnam"); + // viet = string::npos when not found + if (lyxrc.language_global_options && !language_options.str().empty() + && viet == string::npos) clsoptions << language_options.str() << ','; } @@ -883,69 +1068,39 @@ bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features, if (fontsDefaultFamily != "default") os << "\\renewcommand{\\familydefault}{\\" << from_ascii(fontsDefaultFamily) << "}\n"; + + // set font encoding // this one is not per buffer + // for arabic_arabi and farsi we also need to load the LAE and LFE encoding if (lyxrc.fontenc != "default") { - os << "\\usepackage[" << from_ascii(lyxrc.fontenc) - << "]{fontenc}\n"; - texrow.newline(); - } - - if (inputenc == "auto") { - string const doc_encoding = - language->encoding()->latexName(); - Encoding::Package const package = - language->encoding()->package(); - - // Create a list with all the input encodings used - // in the document - std::set encodings = - features.getEncodingSet(doc_encoding); - - if (!encodings.empty() || package == Encoding::inputenc) { - os << "\\usepackage["; - std::set::const_iterator it = encodings.begin(); - std::set::const_iterator const end = encodings.end(); - if (it != end) { - os << from_ascii(*it); - ++it; - } - for (; it != end; ++it) - os << ',' << from_ascii(*it); - if (package == Encoding::inputenc) { - if (!encodings.empty()) - os << ','; - os << from_ascii(doc_encoding); - } - os << "]{inputenc}\n"; - texrow.newline(); - } - if (package == Encoding::CJK) { - os << "\\usepackage{CJK}\n"; + if (language->lang() == "arabic_arabi" || language->lang() == "farsi") { + os << "\\usepackage[" << from_ascii(lyxrc.fontenc) + << ",LFE,LAE]{fontenc}\n"; texrow.newline(); - } - } else if (inputenc != "default") { - switch (language->encoding()->package()) { - case Encoding::none: - break; - case Encoding::inputenc: - os << "\\usepackage[" << from_ascii(inputenc) - << "]{inputenc}\n"; - texrow.newline(); - break; - case Encoding::CJK: - os << "\\usepackage{CJK}\n"; + } else { + os << "\\usepackage[" << from_ascii(lyxrc.fontenc) + << "]{fontenc}\n"; texrow.newline(); - break; } } - // The encoding "armscii8" is only available when the package "armtex" is loaded. - // armscii8 is used for Armenian. - if (language->encoding()->latexName() == "armscii8" || inputenc == "armscii8") { - os << "\\usepackage{armtex}\n"; + // handle inputenc etc. + writeEncodingPreamble(os, features, texrow); + + if (!listings_params.empty()) { + os << "\\usepackage{listings}\n"; + texrow.newline(); + os << "\\lstset{"; + // do not test validity because listings_params is supposed to be valid + string par = InsetListingsParams(listings_params).separatedParams(true); + os << from_ascii(par); + // count the number of newlines + for (size_t i = 0; i < par.size(); ++i) + if (par[i] == '\n') + texrow.newline(); + os << "}\n"; texrow.newline(); } - if (use_geometry || nonstandard_papersize) { os << "\\usepackage{geometry}\n"; texrow.newline(); @@ -1033,6 +1188,8 @@ bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features, os << ",headsep=" << from_ascii(Length(headsep).asLatexString()); if (!footskip.empty()) os << ",footskip=" << from_ascii(Length(footskip).asLatexString()); + if (!columnsep.empty()) + os << ",columnsep=" << from_ascii(Length(columnsep).asLatexString()); os << "}\n"; texrow.newline(); } @@ -1102,6 +1259,56 @@ bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features, // The optional packages; docstring lyxpreamble(from_ascii(features.getPackages())); + // Line spacing + lyxpreamble += from_utf8(spacing().writePreamble(tclass.provides("SetSpace"))); + + // We try to load babel late, in case it interferes + // with other packages. But some packages also need babel to be loaded + // before, e.g. jurabib has to be called after babel. + // So load babel after the optional packages but before the user-defined + // preamble. This allows the users to redefine babel commands, e.g. to + // translate the word "Index" to the German "Stichwortverzeichnis". + // For more infos why this place was chosen, see + // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg128425.html + // If you encounter problems, you can shift babel to its old place behind + // the user-defined preamble. But in this case you must change the Vietnamese + // support from currently "\usepackage[vietnamese]{babel}" to: + // \usepackage{vietnamese} + // \usepackage{babel} + // because vietnamese must be loaded before hyperref + if (use_babel && !features.isRequired("jurabib")) { + // FIXME UNICODE + lyxpreamble += from_utf8(babelCall(language_options.str())) + '\n'; + lyxpreamble += from_utf8(features.getBabelOptions()); + } + + // When the language "japanese-plain" is used, the package "japanese" must + // be loaded behind babel (it provides babel support for Japanese) but before + // hyperref, see + // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129680.html + if (language->lang() == "japanese-plain" && + !documentClass().provides("japanese")) { + //load babel in case it was not loaded due to an empty language list + if (language_options.str().empty()) + lyxpreamble += "\\usepackage{babel}\n"; + lyxpreamble += "\\usepackage{japanese}\n"; + } + + // PDF support. + // * Hyperref manual: "Make sure it comes last of your loaded + // packages, to give it a fighting chance of not being over-written, + // since its job is to redefine many LATEX commands." + // * Email from Heiko Oberdiek: "It is usually better to load babel + // before hyperref. Then hyperref has a chance to detect babel. + // * Has to be loaded before the "LyX specific LaTeX commands" to + // avoid errors with algorithm floats. + // use hyperref explicitely when it is required + if (features.isRequired("hyperref")) { + odocstringstream oss; + pdfoptions().writeLaTeX(oss, documentClass().provides("hyperref")); + lyxpreamble += oss.str(); + } + // this might be useful... lyxpreamble += "\n\\makeatletter\n"; @@ -1165,29 +1372,10 @@ bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features, if (!bullets_def.empty()) lyxpreamble += bullets_def + "}\n\n"; - // We try to load babel late, in case it interferes - // with other packages. - // Jurabib has to be called after babel, though. - if (use_babel && !features.isRequired("jurabib")) { - // FIXME UNICODE - lyxpreamble += from_utf8(babelCall(language_options.str())) + '\n'; - lyxpreamble += from_utf8(features.getBabelOptions()); - } - - lyxpreamble += "\\makeatother\n"; - - // dvipost settings come after everything else - if (features.isAvailable("dvipost") && outputChanges) { - lyxpreamble += - "\\dvipostlayout\n" - "\\dvipost{osstart color push Red}\n" - "\\dvipost{osend color pop}\n" - "\\dvipost{cbstart color push Blue}\n" - "\\dvipost{cbend color pop}\n"; - } + lyxpreamble += "\\makeatother\n\n"; int const nlines = - int(lyx::count(lyxpreamble.begin(), lyxpreamble.end(), '\n')); + int(count(lyxpreamble.begin(), lyxpreamble.end(), '\n')); for (int j = 0; j != nlines; ++j) { texrow.newline(); } @@ -1199,7 +1387,7 @@ bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features, void BufferParams::useClassDefaults() { - TextClass const & tclass = textclasslist[textclass]; + DocumentClass const & tclass = documentClass(); sides = tclass.sides(); columns = tclass.columns(); @@ -1215,34 +1403,162 @@ void BufferParams::useClassDefaults() bool BufferParams::hasClassDefaults() const { - TextClass const & tclass = textclasslist[textclass]; + DocumentClass const & tclass = documentClass(); - return (sides == tclass.sides() + return sides == tclass.sides() && columns == tclass.columns() && pagestyle == tclass.pagestyle() && options == tclass.options() && secnumdepth == tclass.secnumdepth() - && tocdepth == tclass.tocdepth()); + && tocdepth == tclass.tocdepth(); +} + + +DocumentClass const & BufferParams::documentClass() const +{ + return *doc_class_; +} + + +DocumentClass * BufferParams::documentClassPtr() const { + return doc_class_; +} + + +void BufferParams::setDocumentClass(DocumentClass const * const tc) { + // evil, but this function is evil + doc_class_ = const_cast(tc); +} + + +bool BufferParams::setBaseClass(string const & classname) +{ + LYXERR(Debug::TCLASS, "setBaseClass: " << classname); + LayoutFileList const & bcl = LayoutFileList::get(); + if (!bcl.haveClass(classname)) { + docstring s = + bformat(_("The document class %1$s could not be found."), + from_utf8(classname)); + frontend::Alert::error(_("Class not found"), s); + return false; + } + + if (bcl[classname].load()) { + pimpl_->baseClass_ = classname; + return true; + } + + docstring s = + bformat(_("The document class %1$s could not be loaded."), + from_utf8(classname)); + frontend::Alert::error(_("Could not load class"), s); + return false; +} + + +LayoutFile const * BufferParams::baseClass() const +{ + if (LayoutFileList::get().haveClass(pimpl_->baseClass_)) + return &(LayoutFileList::get()[pimpl_->baseClass_]); + else + return 0; +} + + +LayoutFileIndex const & BufferParams::baseClassID() const +{ + return pimpl_->baseClass_; +} + + +void BufferParams::makeDocumentClass() +{ + if (!baseClass()) + return; + + doc_class_ = &(DocumentClassBundle::get().newClass(*baseClass())); + + //FIXME It might be worth loading the children's modules here, + //just as we load their bibliographies and such, instead of just + //doing a check in InsetInclude. + LayoutModuleList::const_iterator it = layoutModules_.begin(); + for (; it != layoutModules_.end(); it++) { + string const modName = *it; + LyXModule * lm = moduleList[modName]; + if (!lm) { + docstring const msg = + bformat(_("The module %1$s has been requested by\n" + "this document but has not been found in the list of\n" + "available modules. If you recently installed it, you\n" + "probably need to reconfigure LyX.\n"), from_utf8(modName)); + frontend::Alert::warning(_("Module not available"), + msg + _("Some layouts may not be available.")); + lyxerr << "BufferParams::makeDocumentClass(): Module " << + modName << " requested but not found in module list." << + endl; + continue; + } + if (!lm->isAvailable()) { + docstring const msg = + bformat(_("The module %1$s requires a package that is\n" + "not available in your LaTeX installation. LaTeX output\n" + "may not be possible.\n"), from_utf8(modName)); + frontend::Alert::warning(_("Package not available"), msg); + } + FileName layout_file = libFileSearch("layouts", lm->getFilename()); + if (!doc_class_->read(layout_file, TextClass::MODULE)) { + docstring const msg = + bformat(_("Error reading module %1$s\n"), from_utf8(modName)); + frontend::Alert::warning(_("Read Error"), msg); + } + } + if (!local_layout.empty()) { + if (!doc_class_->read(local_layout, TextClass::MODULE)) { + docstring const msg = _("Error reading internal layout information"); + frontend::Alert::warning(_("Read Error"), msg); + } + } } -TextClass const & BufferParams::getTextClass() const +vector const & BufferParams::getModules() const { - return textclasslist[textclass]; + return layoutModules_; +} + + + +bool BufferParams::addLayoutModule(string const & modName) +{ + LayoutModuleList::const_iterator it = layoutModules_.begin(); + LayoutModuleList::const_iterator end = layoutModules_.end(); + for (; it != end; it++) { + if (*it == modName) + break; + } + if (it != layoutModules_.end()) + return false; + layoutModules_.push_back(modName); + return true; +} + + +void BufferParams::clearLayoutModules() +{ + layoutModules_.clear(); } Font const BufferParams::getFont() const { - Font f = getTextClass().defaultfont(); - f.setLanguage(language); + FontInfo f = documentClass().defaultfont(); if (fontsDefaultFamily == "rmdefault") - f.setFamily(Font::ROMAN_FAMILY); + f.setFamily(ROMAN_FAMILY); else if (fontsDefaultFamily == "sfdefault") - f.setFamily(Font::SANS_FAMILY); + f.setFamily(SANS_FAMILY); else if (fontsDefaultFamily == "ttdefault") - f.setFamily(Font::TYPEWRITER_FAMILY); - return f; + f.setFamily(TYPEWRITER_FAMILY); + return Font(f, language); } @@ -1256,6 +1572,16 @@ void BufferParams::readPreamble(Lexer & lex) } +void BufferParams::readLocalLayout(Lexer & lex) +{ + if (lex.getString() != "\\begin_local_layout") + lyxerr << "Error (BufferParams::readLocalLayout):" + "consistency check failed." << endl; + + local_layout = lex.getLongString("\\end_local_layout"); +} + + void BufferParams::readLanguage(Lexer & lex) { if (!lex.next()) return; @@ -1276,7 +1602,8 @@ void BufferParams::readLanguage(Lexer & lex) void BufferParams::readGraphicsDriver(Lexer & lex) { - if (!lex.next()) return; + if (!lex.next()) + return; string const tmptok = lex.getString(); // check if tmptok is part of tex_graphics in tex_defs.h @@ -1300,7 +1627,8 @@ void BufferParams::readGraphicsDriver(Lexer & lex) void BufferParams::readBullets(Lexer & lex) { - if (!lex.next()) return; + if (!lex.next()) + return; int const index = lex.getInteger(); lex.next(); @@ -1319,7 +1647,8 @@ void BufferParams::readBullets(Lexer & lex) void BufferParams::readBulletsLaTeX(Lexer & lex) { // The bullet class should be able to read this. - if (!lex.next()) return; + if (!lex.next()) + return; int const index = lex.getInteger(); lex.next(true); docstring const temp_str = lex.getDocString(); @@ -1329,27 +1658,79 @@ void BufferParams::readBulletsLaTeX(Lexer & lex) } -string const BufferParams::paperSizeName() const +void BufferParams::readModules(Lexer & lex) +{ + if (!lex.eatLine()) { + lyxerr << "Error (BufferParams::readModules):" + "Unexpected end of input." << endl; + return; + } + while (true) { + string mod = lex.getString(); + if (mod == "\\end_modules") + break; + addLayoutModule(mod); + lex.eatLine(); + } +} + + +string BufferParams::paperSizeName(PapersizePurpose purpose) const { char real_papersize = papersize; if (real_papersize == PAPER_DEFAULT) real_papersize = lyxrc.default_papersize; switch (real_papersize) { + case PAPER_DEFAULT: + // could be anything, so don't guess + return string(); + case PAPER_CUSTOM: { + if (purpose == XDVI && !paperwidth.empty() && + !paperheight.empty()) { + // heightxwidth + string first = paperwidth; + string second = paperheight; + if (orientation == ORIENTATION_LANDSCAPE) + first.swap(second); + // cut off unit. + return first.erase(first.length() - 2) + + "x" + second; + } + return string(); + } case PAPER_A3: return "a3"; case PAPER_A4: return "a4"; case PAPER_A5: return "a5"; + case PAPER_B3: + // dvips and dvipdfm do not know this + if (purpose == DVIPS || purpose == DVIPDFM) + return string(); + return "b3"; + case PAPER_B4: + // dvipdfm does not know this + if (purpose == DVIPDFM) + return string(); + return "b4"; case PAPER_B5: + // dvipdfm does not know this + if (purpose == DVIPDFM) + return string(); return "b5"; case PAPER_USEXECUTIVE: + // dvipdfm does not know this + if (purpose == DVIPDFM) + return string(); return "foolscap"; case PAPER_USLEGAL: return "legal"; case PAPER_USLETTER: default: + if (purpose == XDVI) + return "us"; return "letter"; } } @@ -1369,9 +1750,9 @@ string const BufferParams::dvips_options() const result += ' ' + paperwidth; result += ',' + paperheight; } else { - string const paper_option = paperSizeName(); - if (paper_option != "letter" || - orientation != ORIENTATION_LANDSCAPE) { + string const paper_option = paperSizeName(DVIPS); + if (!paper_option.empty() && (paper_option != "letter" || + orientation != ORIENTATION_LANDSCAPE)) { // dvips won't accept -t letter -t landscape. // In all other cases, include the paper size // explicitly. @@ -1386,17 +1767,92 @@ string const BufferParams::dvips_options() const } -string const BufferParams::babelCall(string const & lang_opts) const +string BufferParams::babelCall(string const & lang_opts) const { - string tmp = lyxrc.language_package; - if (!lyxrc.language_global_options && tmp == "\\usepackage{babel}") - tmp = string("\\usepackage[") + lang_opts + "]{babel}"; + string lang_pack = lyxrc.language_package; + if (lang_pack != "\\usepackage{babel}") + return lang_pack; // suppress the babel call when there is no babel language defined - // in the lib/languages file - if (lyxrc.language_global_options && tmp == "\\usepackage{babel}" && - language->babel().empty() ) - tmp = string(""); - return tmp; + // for the document language in the lib/languages file and if no + // other languages are used (lang_opts is then empty) + if (lang_opts.empty()) + return string(); + // when Vietnamese is used, babel must directly be loaded with the + // language options, see + // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129417.html + size_t viet = lang_opts.find("vietnam"); + // viet = string::npos when not found + if (!lyxrc.language_global_options || viet != string::npos) + return "\\usepackage[" + lang_opts + "]{babel}"; + return lang_pack; +} + + +void BufferParams::writeEncodingPreamble(odocstream & os, + LaTeXFeatures & features, TexRow & texrow) const +{ + if (inputenc == "auto") { + string const doc_encoding = + language->encoding()->latexName(); + Encoding::Package const package = + language->encoding()->package(); + + // Create a list with all the input encodings used + // in the document + set encodings = + features.getEncodingSet(doc_encoding); + + // When the encodings EUC-JP-plain, JIS-plain, or SJIS-plainare used, the + // package inputenc must be omitted. Therefore set the encoding to empty. + // see http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129680.html + if (doc_encoding == "EUC-JP-plain" || doc_encoding == "JIS-plain" || + doc_encoding == "SJIS-plain") + encodings.clear(); + + if (!encodings.empty() || package == Encoding::inputenc) { + os << "\\usepackage["; + set::const_iterator it = encodings.begin(); + set::const_iterator const end = encodings.end(); + if (it != end) { + os << from_ascii(*it); + ++it; + } + for (; it != end; ++it) + os << ',' << from_ascii(*it); + if (package == Encoding::inputenc) { + if (!encodings.empty()) + os << ','; + os << from_ascii(doc_encoding); + } + os << "]{inputenc}\n"; + texrow.newline(); + } + if (package == Encoding::CJK || features.mustProvide("CJK")) { + os << "\\usepackage{CJK}\n"; + texrow.newline(); + } + } else if (inputenc != "default") { + switch (encoding().package()) { + case Encoding::none: + break; + case Encoding::inputenc: + os << "\\usepackage[" << from_ascii(inputenc) + << "]{inputenc}\n"; + texrow.newline(); + break; + case Encoding::CJK: + os << "\\usepackage{CJK}\n"; + texrow.newline(); + break; + } + } + + // The encoding "armscii8" is only available when the package "armtex" is loaded. + // armscii8 is used for Armenian. + if (language->encoding()->latexName() == "armscii8" || inputenc == "armscii8") { + os << "\\usepackage{armtex}\n"; + texrow.newline(); + } } @@ -1530,7 +1986,7 @@ string const BufferParams::loadFonts(string const & rm, else if (tt == "courier" ) os << "\\usepackage{" << tt << "}\n"; // Computer Modern, Latin Modern, CM Bright - else if (tt != "default") + else if (tt != "default") os << "\\renewcommand{\\ttdefault}{" << tt << "}\n"; return os.str(); @@ -1540,29 +1996,28 @@ string const BufferParams::loadFonts(string const & rm, Encoding const & BufferParams::encoding() const { if (inputenc == "auto" || inputenc == "default") - return *(language->encoding()); - Encoding const * const enc = - encodings.getFromLaTeXName(inputenc); + return *language->encoding(); + Encoding const * const enc = encodings.fromLaTeXName(inputenc); if (enc) return *enc; - lyxerr << "Unknown inputenc value `" << inputenc - << "'. Using `auto' instead." << endl; - return *(language->encoding()); + LYXERR0("Unknown inputenc value `" << inputenc + << "'. Using `auto' instead."); + return *language->encoding(); } -biblio::CiteEngine BufferParams::getEngine() const +biblio::CiteEngine BufferParams::citeEngine() const { // FIXME the class should provide the numerical/ // authoryear choice - if (getTextClass().provides("natbib") + if (documentClass().provides("natbib") && cite_engine_ != biblio::ENGINE_NATBIB_NUMERICAL) return biblio::ENGINE_NATBIB_AUTHORYEAR; return cite_engine_; } -void BufferParams::setCiteEngine(biblio::CiteEngine const cite_engine) +void BufferParams::setCiteEngine(biblio::CiteEngine cite_engine) { cite_engine_ = cite_engine; }