3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Jean-Marc Lasgouttes
8 * \author Angus Leeming
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
17 #include "TextClass.h"
19 #include "LayoutFile.h"
23 #include "FloatList.h"
27 #include "ModuleList.h"
29 #include "frontends/alert.h"
31 #include "support/lassert.h"
32 #include "support/debug.h"
33 #include "support/ExceptionMessage.h"
34 #include "support/FileName.h"
35 #include "support/filetools.h"
36 #include "support/gettext.h"
37 #include "support/lstrings.h"
38 #include "support/os.h"
45 using namespace lyx::support;
51 class LayoutNamesEqual : public unary_function<Layout, bool> {
53 LayoutNamesEqual(docstring const & name)
56 bool operator()(Layout const & c) const
58 return c.name() == name_;
64 // Keep the changes documented in the Customization manual.
65 int const FORMAT = 15;
68 bool layout2layout(FileName const & filename, FileName const & tempfile)
70 FileName const script = libFileSearch("scripts", "layout2layout.py");
72 LYXERR0("Could not find layout conversion "
73 "script layout2layout.py.");
77 ostringstream command;
78 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
79 << ' ' << quoteName(filename.toFilesystemEncoding())
80 << ' ' << quoteName(tempfile.toFilesystemEncoding());
81 string const command_str = command.str();
83 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
85 cmd_ret const ret = runCommand(command_str);
87 LYXERR0("Could not run layout conversion script layout2layout.py.");
94 std::string translateRT(TextClass::ReadType rt)
97 case TextClass::BASECLASS:
99 case TextClass::MERGE:
101 case TextClass::MODULE:
102 return "module file";
103 case TextClass::VALIDATION:
113 // This string should not be translated here,
114 // because it is a layout identifier.
115 docstring const TextClass::plain_layout_ = from_ascii("Plain Layout");
118 InsetLayout DocumentClass::plain_insetlayout_;
121 /////////////////////////////////////////////////////////////////////////
125 /////////////////////////////////////////////////////////////////////////
127 TextClass::TextClass()
130 outputFormat_ = "latex";
135 pagestyle_ = "default";
136 defaultfont_ = sane_font;
137 opt_fontsize_ = "10|11|12";
138 opt_pagestyle_ = "empty|plain|headings|fancy";
139 titletype_ = TITLE_COMMAND_AFTER;
140 titlename_ = "maketitle";
142 _("Plain Layout"); // a hack to make this translatable
146 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
148 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
149 if (!lay.read(lexrc, *this)) {
150 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
154 lay.resfont = lay.font;
155 lay.resfont.realize(defaultfont_);
156 lay.reslabelfont = lay.labelfont;
157 lay.reslabelfont.realize(defaultfont_);
158 return true; // no errors
197 LexerKeyword textClassTags[] = {
198 { "addtopreamble", TC_ADDTOPREAMBLE },
199 { "classoptions", TC_CLASSOPTIONS },
200 { "columns", TC_COLUMNS },
201 { "counter", TC_COUNTER },
202 { "defaultfont", TC_DEFAULTFONT },
203 { "defaultmodule", TC_DEFAULTMODULE },
204 { "defaultstyle", TC_DEFAULTSTYLE },
205 { "excludesmodule", TC_EXCLUDESMODULE },
206 { "float", TC_FLOAT },
207 { "format", TC_FORMAT },
208 { "input", TC_INPUT },
209 { "insetlayout", TC_INSETLAYOUT },
210 { "leftmargin", TC_LEFTMARGIN },
211 { "nofloat", TC_NOFLOAT },
212 { "nostyle", TC_NOSTYLE },
213 { "outputformat", TC_OUTPUTFORMAT },
214 { "outputtype", TC_OUTPUTTYPE },
215 { "pagestyle", TC_PAGESTYLE },
216 { "preamble", TC_PREAMBLE },
217 { "provides", TC_PROVIDES },
218 { "providesmodule", TC_PROVIDESMODULE },
219 { "requires", TC_REQUIRES },
220 { "rightmargin", TC_RIGHTMARGIN },
221 { "secnumdepth", TC_SECNUMDEPTH },
222 { "sides", TC_SIDES },
223 { "style", TC_STYLE },
224 { "titlelatexname", TC_TITLELATEXNAME },
225 { "titlelatextype", TC_TITLELATEXTYPE },
226 { "tocdepth", TC_TOCDEPTH }
232 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
234 LYXERR(Debug::TCLASS, "Converting layout file to " << FORMAT);
235 FileName const tempfile = FileName::tempName("convert_layout");
236 bool success = layout2layout(filename, tempfile);
238 success = readWithoutConv(tempfile, rt) == OK;
239 tempfile.removeFile();
244 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
246 if (!filename.isReadableFile()) {
247 lyxerr << "Cannot read layout file `" << filename << "'."
252 LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
253 to_utf8(makeDisplayPath(filename.absFilename())));
255 // Define the plain layout used in table cells, ert, etc. Note that
256 // we do this before loading any layout file, so that classes can
257 // override features of this layout if they should choose to do so.
258 if (rt == BASECLASS && !hasLayout(plain_layout_))
259 layoutlist_.push_back(createBasicLayout(plain_layout_));
261 Lexer lexrc(textClassTags);
262 lexrc.setFile(filename);
263 ReturnValues retval = read(lexrc, rt);
265 LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
266 to_utf8(makeDisplayPath(filename.absFilename())));
272 bool TextClass::read(FileName const & filename, ReadType rt)
274 ReturnValues const retval = readWithoutConv(filename, rt);
275 if (retval != FORMAT_MISMATCH)
278 bool const worx = convertLayoutFormat(filename, rt);
280 LYXERR0 ("Unable to convert " << filename <<
281 " to format " << FORMAT);
288 bool TextClass::validate(std::string const & str)
291 return tc.read(str, VALIDATION);
295 bool TextClass::read(std::string const & str, ReadType rt)
297 Lexer lexrc(textClassTags);
298 istringstream is(str);
300 ReturnValues retval = read(lexrc, rt);
302 if (retval != FORMAT_MISMATCH)
305 // write the layout string to a temporary file
306 FileName const tempfile = FileName::tempName("TextClass_read");
307 ofstream os(tempfile.toFilesystemEncoding().c_str());
309 LYXERR0("Unable to create temporary file");
315 // now try to convert it
316 bool const worx = convertLayoutFormat(tempfile, rt);
318 LYXERR0("Unable to convert internal layout information to format "
321 tempfile.removeFile();
326 // Reads a textclass structure from file.
327 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
329 bool error = !lexrc.isOK();
331 // Format of files before the 'Format' tag was introduced
335 while (lexrc.isOK() && !error) {
336 int le = lexrc.lex();
339 case Lexer::LEX_FEOF:
342 case Lexer::LEX_UNDEF:
343 lexrc.printError("Unknown TextClass tag `$$Token'");
351 switch (static_cast<TextClassTags>(le)) {
355 format = lexrc.getInteger();
358 case TC_OUTPUTFORMAT:
360 outputFormat_ = lexrc.getString();
364 readOutputType(lexrc);
365 switch(outputType_) {
367 outputFormat_ = "latex";
370 outputFormat_ = "docbook";
373 outputFormat_ = "literate";
378 case TC_INPUT: // Include file
380 string const inc = lexrc.getString();
381 FileName tmp = libFileSearch("layouts", inc,
385 lexrc.printError("Could not find input file: " + inc);
387 } else if (!read(tmp, MERGE)) {
388 lexrc.printError("Error reading input"
389 "file: " + tmp.absFilename());
395 case TC_DEFAULTSTYLE:
397 docstring const name = from_utf8(subst(lexrc.getString(),
399 defaultlayout_ = name;
405 lexrc.printError("No name given for style: `$$Token'.");
409 docstring const name = from_utf8(subst(lexrc.getString(),
412 string s = "Could not read name for style: `$$Token' "
413 + lexrc.getString() + " is probably not valid UTF-8!";
414 lexrc.printError(s.c_str());
416 // Since we couldn't read the name, we just scan the rest
417 // of the style and discard it.
418 error = !readStyle(lexrc, lay);
419 } else if (hasLayout(name)) {
420 Layout & lay = operator[](name);
421 error = !readStyle(lexrc, lay);
424 layout.setName(name);
425 error = !readStyle(lexrc, layout);
427 layoutlist_.push_back(layout);
429 if (defaultlayout_.empty()) {
430 // We do not have a default layout yet, so we choose
431 // the first layout we encounter.
432 defaultlayout_ = name;
440 docstring const style = from_utf8(subst(lexrc.getString(),
442 if (!deleteLayout(style))
443 lyxerr << "Cannot delete style `"
444 << to_utf8(style) << '\'' << endl;
450 columns_ = lexrc.getInteger();
455 switch (lexrc.getInteger()) {
456 case 1: sides_ = OneSide; break;
457 case 2: sides_ = TwoSides; break;
459 lyxerr << "Impossible number of page"
460 " sides, setting to one."
470 pagestyle_ = rtrim(lexrc.getString());
474 defaultfont_ = lyxRead(lexrc);
475 if (!defaultfont_.resolved()) {
476 lexrc.printError("Warning: defaultfont should "
477 "be fully instantiated!");
478 defaultfont_.realize(sane_font);
484 secnumdepth_ = lexrc.getInteger();
489 tocdepth_ = lexrc.getInteger();
492 // First step to support options
493 case TC_CLASSOPTIONS:
494 readClassOptions(lexrc);
498 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
501 case TC_ADDTOPREAMBLE:
502 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
507 string const feature = lexrc.getString();
509 if (lexrc.getInteger())
510 provides_.insert(feature);
512 provides_.erase(feature);
518 vector<string> const req
519 = getVectorFromString(lexrc.getString());
520 requires_.insert(req.begin(), req.end());
524 case TC_DEFAULTMODULE: {
526 string const module = lexrc.getString();
527 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
528 default_modules_.push_back(module);
532 case TC_PROVIDESMODULE: {
534 string const module = lexrc.getString();
535 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
536 provided_modules_.push_back(module);
540 case TC_EXCLUDESMODULE: {
542 string const module = lexrc.getString();
543 // modules already have their own way to exclude other modules
545 LYXERR0("ExcludesModule tag cannot be used in a module!");
548 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
549 excluded_modules_.push_back(module);
553 case TC_LEFTMARGIN: // left margin type
555 leftmargin_ = lexrc.getDocString();
558 case TC_RIGHTMARGIN: // right margin type
560 rightmargin_ = lexrc.getDocString();
563 case TC_INSETLAYOUT: {
565 lexrc.printError("No name given for InsetLayout: `$$Token'.");
569 docstring const name = subst(lexrc.getDocString(), '_', ' ');
571 string s = "Could not read name for InsetLayout: `$$Token' "
572 + lexrc.getString() + " is probably not valid UTF-8!";
573 lexrc.printError(s.c_str());
575 // Since we couldn't read the name, we just scan the rest
576 // of the style and discard it.
577 il.read(lexrc, *this);
579 } else if (hasInsetLayout(name)) {
580 InsetLayout & il = insetlayoutlist_[name];
581 error = !il.read(lexrc, *this);
585 error = !il.read(lexrc, *this);
587 insetlayoutlist_[name] = il;
598 docstring const name = lexrc.getDocString();
600 string s = "Could not read name for counter: `$$Token' "
601 + lexrc.getString() + " is probably not valid UTF-8!";
602 lexrc.printError(s.c_str());
604 // Since we couldn't read the name, we just scan the rest
608 error = !counters_.read(lexrc, name);
611 lexrc.printError("No name given for style: `$$Token'.");
616 case TC_TITLELATEXTYPE:
617 readTitleType(lexrc);
620 case TC_TITLELATEXNAME:
622 titlename_ = lexrc.getString();
627 string const nofloat = lexrc.getString();
628 floatlist_.erase(nofloat);
633 //Note that this is triggered the first time through the loop unless
634 //we hit a format tag.
635 if (format != FORMAT)
639 if (format != FORMAT)
640 return FORMAT_MISMATCH;
643 return (error ? ERROR : OK);
645 if (defaultlayout_.empty()) {
646 LYXERR0("Error: Textclass '" << name_
647 << "' is missing a defaultstyle.");
651 // Try to erase "stdinsets" from the provides_ set.
653 // Provides stdinsets 1
654 // declaration simply tells us that the standard insets have been
655 // defined. (It's found in stdinsets.inc but could also be used in
656 // user-defined files.) There isn't really any such package. So we
657 // might as well go ahead and erase it.
658 // If we do not succeed, then it was not there, which means that
659 // the textclass did not provide the definitions of the standard
660 // insets. So we need to try to load them.
661 int erased = provides_.erase("stdinsets");
663 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
666 throw ExceptionMessage(WarningException, _("Missing File"),
667 _("Could not find stdinsets.inc! This may lead to data loss!"));
669 } else if (!read(tmp, MERGE)) {
670 throw ExceptionMessage(WarningException, _("Corrupt File"),
671 _("Could not read stdinsets.inc! This may lead to data loss!"));
676 min_toclevel_ = Layout::NOT_IN_TOC;
677 max_toclevel_ = Layout::NOT_IN_TOC;
678 const_iterator lit = begin();
679 const_iterator len = end();
680 for (; lit != len; ++lit) {
681 int const toclevel = lit->toclevel;
682 if (toclevel != Layout::NOT_IN_TOC) {
683 if (min_toclevel_ == Layout::NOT_IN_TOC)
684 min_toclevel_ = toclevel;
686 min_toclevel_ = min(min_toclevel_, toclevel);
687 max_toclevel_ = max(max_toclevel_, toclevel);
690 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
691 << ", maximum is " << max_toclevel_);
693 return (error ? ERROR : OK);
697 void TextClass::readTitleType(Lexer & lexrc)
699 LexerKeyword titleTypeTags[] = {
700 { "commandafter", TITLE_COMMAND_AFTER },
701 { "environment", TITLE_ENVIRONMENT }
704 PushPopHelper pph(lexrc, titleTypeTags);
706 int le = lexrc.lex();
708 case Lexer::LEX_UNDEF:
709 lexrc.printError("Unknown output type `$$Token'");
711 case TITLE_COMMAND_AFTER:
712 case TITLE_ENVIRONMENT:
713 titletype_ = static_cast<TitleLatexType>(le);
716 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
722 void TextClass::readOutputType(Lexer & lexrc)
724 LexerKeyword outputTypeTags[] = {
725 { "docbook", DOCBOOK },
727 { "literate", LITERATE }
730 PushPopHelper pph(lexrc, outputTypeTags);
732 int le = lexrc.lex();
734 case Lexer::LEX_UNDEF:
735 lexrc.printError("Unknown output type `$$Token'");
740 outputType_ = static_cast<OutputType>(le);
743 LYXERR0("Unhandled value " << le);
749 void TextClass::readClassOptions(Lexer & lexrc)
759 LexerKeyword classOptionsTags[] = {
761 {"fontsize", CO_FONTSIZE },
762 {"header", CO_HEADER },
763 {"other", CO_OTHER },
764 {"pagestyle", CO_PAGESTYLE }
767 lexrc.pushTable(classOptionsTags);
769 while (!getout && lexrc.isOK()) {
770 int le = lexrc.lex();
772 case Lexer::LEX_UNDEF:
773 lexrc.printError("Unknown ClassOption tag `$$Token'");
780 opt_fontsize_ = rtrim(lexrc.getString());
784 opt_pagestyle_ = rtrim(lexrc.getString());
788 options_ = lexrc.getString();
792 class_header_ = subst(lexrc.getString(), """, "\"");
803 void TextClass::readFloat(Lexer & lexrc)
817 LexerKeyword floatTags[] = {
819 { "extension", FT_EXT },
820 { "guiname", FT_NAME },
821 { "latexbuiltin", FT_BUILTIN },
822 { "listname", FT_LISTNAME },
823 { "numberwithin", FT_WITHIN },
824 { "placement", FT_PLACEMENT },
825 { "style", FT_STYLE },
829 lexrc.pushTable(floatTags);
838 bool builtin = false;
841 while (!getout && lexrc.isOK()) {
842 int le = lexrc.lex();
844 case Lexer::LEX_UNDEF:
845 lexrc.printError("Unknown float tag `$$Token'");
852 type = lexrc.getString();
853 if (floatlist_.typeExist(type)) {
854 Floating const & fl = floatlist_.getType(type);
855 placement = fl.placement();
857 within = fl.within();
860 listName = fl.listName();
861 builtin = fl.builtin();
866 name = lexrc.getString();
870 placement = lexrc.getString();
874 ext = lexrc.getString();
878 within = lexrc.getString();
879 if (within == "none")
884 style = lexrc.getString();
888 listName = lexrc.getString();
892 builtin = lexrc.getBool();
900 // Here if have a full float if getout == true
902 Floating fl(type, placement, ext, within,
903 style, name, listName, builtin);
904 floatlist_.newFloat(fl);
905 // each float has its own counter
906 counters_.newCounter(from_ascii(type), from_ascii(within),
907 docstring(), docstring());
908 // also define sub-float counters
909 docstring const subtype = "sub-" + from_ascii(type);
910 counters_.newCounter(subtype, from_ascii(type),
911 "\\alph{" + subtype + "}", docstring());
918 bool TextClass::hasLayout(docstring const & n) const
920 docstring const name = n.empty() ? defaultLayoutName() : n;
922 return find_if(layoutlist_.begin(), layoutlist_.end(),
923 LayoutNamesEqual(name))
924 != layoutlist_.end();
928 bool TextClass::hasInsetLayout(docstring const & n) const
932 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
933 InsetLayouts::const_iterator en = insetlayoutlist_.end();
934 for (; it != en; ++it)
941 Layout const & TextClass::operator[](docstring const & name) const
943 LASSERT(!name.empty(), /**/);
946 find_if(begin(), end(), LayoutNamesEqual(name));
949 lyxerr << "We failed to find the layout '" << to_utf8(name)
950 << "' in the layout list. You MUST investigate!"
952 for (const_iterator cit = begin(); cit != end(); ++cit)
953 lyxerr << " " << to_utf8(cit->name()) << endl;
955 // we require the name to exist
956 LASSERT(false, /**/);
963 Layout & TextClass::operator[](docstring const & name)
965 LASSERT(!name.empty(), /**/);
967 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
970 LYXERR0("We failed to find the layout '" << to_utf8(name)
971 << "' in the layout list. You MUST investigate!");
972 for (const_iterator cit = begin(); cit != end(); ++cit)
973 LYXERR0(" " << to_utf8(cit->name()));
975 // we require the name to exist
976 LASSERT(false, /**/);
983 bool TextClass::deleteLayout(docstring const & name)
985 if (name == defaultLayoutName() || name == plainLayoutName())
988 LayoutList::iterator it =
989 remove_if(layoutlist_.begin(), layoutlist_.end(),
990 LayoutNamesEqual(name));
992 LayoutList::iterator end = layoutlist_.end();
993 bool const ret = (it != end);
994 layoutlist_.erase(it, end);
999 // Load textclass info if not loaded yet
1000 bool TextClass::load(string const & path) const
1005 // Read style-file, provided path is searched before the system ones
1006 // If path is a file, it is loaded directly.
1007 FileName layout_file(path);
1008 if (!path.empty() && !layout_file.isReadableFile())
1009 layout_file = FileName(addName(path, name_ + ".layout"));
1010 if (layout_file.empty() || !layout_file.exists())
1011 layout_file = libFileSearch("layouts", name_, "layout");
1012 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1015 lyxerr << "Error reading `"
1016 << to_utf8(makeDisplayPath(layout_file.absFilename()))
1017 << "'\n(Check `" << name_
1018 << "')\nCheck your installation and "
1019 "try Options/Reconfigure..." << endl;
1026 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1029 layoutlist_.push_back(createBasicLayout(n, true));
1033 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1035 // FIXME The fix for the InsetLayout part of 4812 would be here:
1036 // Add the InsetLayout to the document class if it is not found.
1038 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1039 while (!n.empty()) {
1040 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1041 if (cit != cen && cit->first == n)
1043 size_t i = n.find(':');
1044 if (i == string::npos)
1048 return plain_insetlayout_;
1052 docstring const & TextClass::defaultLayoutName() const
1054 // This really should come from the actual layout... (Lgb)
1055 return defaultlayout_;
1059 Layout const & TextClass::defaultLayout() const
1061 return operator[](defaultLayoutName());
1065 bool TextClass::isDefaultLayout(Layout const & layout) const
1067 return layout.name() == defaultLayoutName();
1071 bool TextClass::isPlainLayout(Layout const & layout) const
1073 return layout.name() == plainLayoutName();
1077 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1079 static Layout * defaultLayout = NULL;
1081 if (defaultLayout) {
1082 defaultLayout->setUnknown(unknown);
1083 defaultLayout->setName(name);
1084 return *defaultLayout;
1087 static char const * s = "Margin Static\n"
1088 "LatexType Paragraph\n"
1091 "AlignPossible Left, Right, Center\n"
1092 "LabelType No_Label\n"
1094 istringstream ss(s);
1095 Lexer lex(textClassTags);
1097 defaultLayout = new Layout;
1098 defaultLayout->setUnknown(unknown);
1099 defaultLayout->setName(name);
1100 if (!readStyle(lex, *defaultLayout)) {
1101 // The only way this happens is because the hardcoded layout above
1103 LASSERT(false, /**/);
1105 return *defaultLayout;
1108 /////////////////////////////////////////////////////////////////////////
1110 // DocumentClassBundle
1112 /////////////////////////////////////////////////////////////////////////
1114 DocumentClassBundle::~DocumentClassBundle()
1116 for (size_t i = 0; i != documentClasses_.size(); ++i)
1117 delete documentClasses_[i];
1118 documentClasses_.clear();
1121 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1123 DocumentClass * dc = new DocumentClass(baseClass);
1124 documentClasses_.push_back(dc);
1125 return *documentClasses_.back();
1129 DocumentClassBundle & DocumentClassBundle::get()
1131 static DocumentClassBundle singleton;
1136 DocumentClass & DocumentClassBundle::makeDocumentClass(
1137 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1139 DocumentClass & doc_class = newClass(baseClass);
1140 LayoutModuleList::const_iterator it = modlist.begin();
1141 LayoutModuleList::const_iterator en = modlist.end();
1142 for (; it != en; it++) {
1143 string const modName = *it;
1144 LyXModule * lm = moduleList[modName];
1146 docstring const msg =
1147 bformat(_("The module %1$s has been requested by\n"
1148 "this document but has not been found in the list of\n"
1149 "available modules. If you recently installed it, you\n"
1150 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1151 ExceptionMessage(WarningException,_("Module not available"),
1152 msg + _("Some layouts may not be available."));
1155 if (!lm->isAvailable()) {
1156 docstring const msg =
1157 bformat(_("The module %1$s requires a package that is\n"
1158 "not available in your LaTeX installation. LaTeX output\n"
1159 "may not be possible.\n"), from_utf8(modName));
1160 ExceptionMessage(WarningException, _("Package not available"), msg);
1162 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1163 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1164 docstring const msg =
1165 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1166 throw ExceptionMessage(WarningException, _("Read Error"), msg);
1173 /////////////////////////////////////////////////////////////////////////
1177 /////////////////////////////////////////////////////////////////////////
1179 DocumentClass::DocumentClass(LayoutFile const & tc)
1184 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1186 LayoutList::const_iterator it = layoutlist_.begin();
1187 LayoutList::const_iterator end = layoutlist_.end();
1188 for (; it != end; ++it)
1189 if (it->latexname() == lay)
1195 bool DocumentClass::provides(string const & p) const
1197 return provides_.find(p) != provides_.end();
1201 bool DocumentClass::hasTocLevels() const
1203 return min_toclevel_ != Layout::NOT_IN_TOC;
1207 /////////////////////////////////////////////////////////////////////////
1211 /////////////////////////////////////////////////////////////////////////
1213 ostream & operator<<(ostream & os, PageSides p)