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 = 13;
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()
134 pagestyle_ = "default";
135 defaultfont_ = sane_font;
136 opt_fontsize_ = "10|11|12";
137 opt_pagestyle_ = "empty|plain|headings|fancy";
138 titletype_ = TITLE_COMMAND_AFTER;
139 titlename_ = "maketitle";
141 _("Plain Layout"); // a hack to make this translatable
145 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
147 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
148 if (!lay.read(lexrc, *this)) {
149 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
153 lay.resfont = lay.font;
154 lay.resfont.realize(defaultfont_);
155 lay.reslabelfont = lay.labelfont;
156 lay.reslabelfont.realize(defaultfont_);
157 return true; // no errors
195 LexerKeyword textClassTags[] = {
196 { "addtopreamble", TC_ADDTOPREAMBLE },
197 { "classoptions", TC_CLASSOPTIONS },
198 { "columns", TC_COLUMNS },
199 { "counter", TC_COUNTER },
200 { "defaultfont", TC_DEFAULTFONT },
201 { "defaultmodule", TC_DEFAULTMODULE },
202 { "defaultstyle", TC_DEFAULTSTYLE },
203 { "excludesmodule", TC_EXCLUDESMODULE },
204 { "float", TC_FLOAT },
205 { "format", TC_FORMAT },
206 { "input", TC_INPUT },
207 { "insetlayout", TC_INSETLAYOUT },
208 { "leftmargin", TC_LEFTMARGIN },
209 { "nofloat", TC_NOFLOAT },
210 { "nostyle", TC_NOSTYLE },
211 { "outputtype", TC_OUTPUTTYPE },
212 { "pagestyle", TC_PAGESTYLE },
213 { "preamble", TC_PREAMBLE },
214 { "provides", TC_PROVIDES },
215 { "providesmodule", TC_PROVIDESMODULE },
216 { "requires", TC_REQUIRES },
217 { "rightmargin", TC_RIGHTMARGIN },
218 { "secnumdepth", TC_SECNUMDEPTH },
219 { "sides", TC_SIDES },
220 { "style", TC_STYLE },
221 { "titlelatexname", TC_TITLELATEXNAME },
222 { "titlelatextype", TC_TITLELATEXTYPE },
223 { "tocdepth", TC_TOCDEPTH }
229 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
231 LYXERR(Debug::TCLASS, "Converting layout file to " << FORMAT);
232 FileName const tempfile = FileName::tempName("convert_layout");
233 bool success = layout2layout(filename, tempfile);
235 success = read(tempfile, rt);
236 tempfile.removeFile();
240 bool TextClass::read(FileName const & filename, ReadType rt)
242 if (!filename.isReadableFile()) {
243 lyxerr << "Cannot read layout file `" << filename << "'."
248 LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
249 to_utf8(makeDisplayPath(filename.absFilename())));
251 // Define the plain layout used in table cells, ert, etc. Note that
252 // we do this before loading any layout file, so that classes can
253 // override features of this layout if they should choose to do so.
254 if (rt == BASECLASS && !hasLayout(plain_layout_))
255 layoutlist_.push_back(createBasicLayout(plain_layout_));
257 Lexer lexrc(textClassTags);
258 lexrc.setFile(filename);
259 ReturnValues retval = read(lexrc, rt);
261 LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
262 to_utf8(makeDisplayPath(filename.absFilename())));
264 if (retval != FORMAT_MISMATCH)
267 bool const worx = convertLayoutFormat(filename, rt);
269 LYXERR0 ("Unable to convert " << filename <<
270 " to format " << FORMAT);
277 bool TextClass::validate(std::string const & str)
280 return tc.read(str, VALIDATION);
284 bool TextClass::read(std::string const & str, ReadType rt)
286 Lexer lexrc(textClassTags);
287 istringstream is(str);
289 ReturnValues retval = read(lexrc, rt);
291 if (retval != FORMAT_MISMATCH)
294 // write the layout string to a temporary file
295 FileName const tempfile = FileName::tempName("TextClass_read");
296 ofstream os(tempfile.toFilesystemEncoding().c_str());
298 LYXERR0("Unable to create temporary file");
304 // now try to convert it
305 bool const worx = convertLayoutFormat(tempfile, rt);
307 LYXERR0("Unable to convert internal layout information to format "
310 tempfile.removeFile();
315 // Reads a textclass structure from file.
316 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
318 bool error = !lexrc.isOK();
320 // Format of files before the 'Format' tag was introduced
324 while (lexrc.isOK() && !error) {
325 int le = lexrc.lex();
328 case Lexer::LEX_FEOF:
331 case Lexer::LEX_UNDEF:
332 lexrc.printError("Unknown TextClass tag `$$Token'");
340 switch (static_cast<TextClassTags>(le)) {
344 format = lexrc.getInteger();
347 case TC_OUTPUTTYPE: // output type definition
348 readOutputType(lexrc);
351 case TC_INPUT: // Include file
353 string const inc = lexrc.getString();
354 FileName tmp = libFileSearch("layouts", inc,
358 lexrc.printError("Could not find input file: " + inc);
360 } else if (!read(tmp, MERGE)) {
361 lexrc.printError("Error reading input"
362 "file: " + tmp.absFilename());
368 case TC_DEFAULTSTYLE:
370 docstring const name = from_utf8(subst(lexrc.getString(),
372 defaultlayout_ = name;
378 lexrc.printError("No name given for style: `$$Token'.");
382 docstring const name = from_utf8(subst(lexrc.getString(),
385 string s = "Could not read name for style: `$$Token' "
386 + lexrc.getString() + " is probably not valid UTF-8!";
387 lexrc.printError(s.c_str());
389 // Since we couldn't read the name, we just scan the rest
390 // of the style and discard it.
391 error = !readStyle(lexrc, lay);
392 } else if (hasLayout(name)) {
393 Layout & lay = operator[](name);
394 error = !readStyle(lexrc, lay);
397 layout.setName(name);
398 error = !readStyle(lexrc, layout);
400 layoutlist_.push_back(layout);
402 if (defaultlayout_.empty()) {
403 // We do not have a default layout yet, so we choose
404 // the first layout we encounter.
405 defaultlayout_ = name;
413 docstring const style = from_utf8(subst(lexrc.getString(),
415 if (!deleteLayout(style))
416 lyxerr << "Cannot delete style `"
417 << to_utf8(style) << '\'' << endl;
423 columns_ = lexrc.getInteger();
428 switch (lexrc.getInteger()) {
429 case 1: sides_ = OneSide; break;
430 case 2: sides_ = TwoSides; break;
432 lyxerr << "Impossible number of page"
433 " sides, setting to one."
443 pagestyle_ = rtrim(lexrc.getString());
447 defaultfont_ = lyxRead(lexrc);
448 if (!defaultfont_.resolved()) {
449 lexrc.printError("Warning: defaultfont should "
450 "be fully instantiated!");
451 defaultfont_.realize(sane_font);
457 secnumdepth_ = lexrc.getInteger();
462 tocdepth_ = lexrc.getInteger();
465 // First step to support options
466 case TC_CLASSOPTIONS:
467 readClassOptions(lexrc);
471 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
474 case TC_ADDTOPREAMBLE:
475 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
480 string const feature = lexrc.getString();
482 if (lexrc.getInteger())
483 provides_.insert(feature);
485 provides_.erase(feature);
491 vector<string> const req
492 = getVectorFromString(lexrc.getString());
493 requires_.insert(req.begin(), req.end());
497 case TC_DEFAULTMODULE: {
499 string const module = lexrc.getString();
500 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
501 default_modules_.push_back(module);
505 case TC_PROVIDESMODULE: {
507 string const module = lexrc.getString();
508 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
509 provided_modules_.push_back(module);
513 case TC_EXCLUDESMODULE: {
515 string const module = lexrc.getString();
516 // modules already have their own way to exclude other modules
518 LYXERR0("ExcludesModule tag cannot be used in a module!");
521 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
522 excluded_modules_.push_back(module);
526 case TC_LEFTMARGIN: // left margin type
528 leftmargin_ = lexrc.getDocString();
531 case TC_RIGHTMARGIN: // right margin type
533 rightmargin_ = lexrc.getDocString();
536 case TC_INSETLAYOUT: {
538 lexrc.printError("No name given for InsetLayout: `$$Token'.");
542 docstring const name = subst(lexrc.getDocString(), '_', ' ');
544 string s = "Could not read name for InsetLayout: `$$Token' "
545 + lexrc.getString() + " is probably not valid UTF-8!";
546 lexrc.printError(s.c_str());
548 // Since we couldn't read the name, we just scan the rest
549 // of the style and discard it.
550 il.read(lexrc, *this);
552 } else if (hasInsetLayout(name)) {
553 InsetLayout & il = insetlayoutlist_[name];
554 error = !il.read(lexrc, *this);
558 error = !il.read(lexrc, *this);
560 insetlayoutlist_[name] = il;
571 docstring const name = lexrc.getDocString();
573 string s = "Could not read name for counter: `$$Token' "
574 + lexrc.getString() + " is probably not valid UTF-8!";
575 lexrc.printError(s.c_str());
577 // Since we couldn't read the name, we just scan the rest
581 error = !counters_.read(lexrc, name);
584 lexrc.printError("No name given for style: `$$Token'.");
589 case TC_TITLELATEXTYPE:
590 readTitleType(lexrc);
593 case TC_TITLELATEXNAME:
595 titlename_ = lexrc.getString();
600 string const nofloat = lexrc.getString();
601 floatlist_.erase(nofloat);
606 //Note that this is triggered the first time through the loop unless
607 //we hit a format tag.
608 if (format != FORMAT)
612 if (format != FORMAT)
613 return FORMAT_MISMATCH;
616 return (error ? ERROR : OK);
618 if (defaultlayout_.empty()) {
619 LYXERR0("Error: Textclass '" << name_
620 << "' is missing a defaultstyle.");
624 // Try to erase "stdinsets" from the provides_ set.
626 // Provides stdinsets 1
627 // declaration simply tells us that the standard insets have been
628 // defined. (It's found in stdinsets.inc but could also be used in
629 // user-defined files.) There isn't really any such package. So we
630 // might as well go ahead and erase it.
631 // If we do not succeed, then it was not there, which means that
632 // the textclass did not provide the definitions of the standard
633 // insets. So we need to try to load them.
634 int erased = provides_.erase("stdinsets");
636 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
639 throw ExceptionMessage(WarningException, _("Missing File"),
640 _("Could not find stdinsets.inc! This may lead to data loss!"));
642 } else if (!read(tmp, MERGE)) {
643 throw ExceptionMessage(WarningException, _("Corrupt File"),
644 _("Could not read stdinsets.inc! This may lead to data loss!"));
649 min_toclevel_ = Layout::NOT_IN_TOC;
650 max_toclevel_ = Layout::NOT_IN_TOC;
651 const_iterator lit = begin();
652 const_iterator len = end();
653 for (; lit != len; ++lit) {
654 int const toclevel = lit->toclevel;
655 if (toclevel != Layout::NOT_IN_TOC) {
656 if (min_toclevel_ == Layout::NOT_IN_TOC)
657 min_toclevel_ = toclevel;
659 min_toclevel_ = min(min_toclevel_, toclevel);
660 max_toclevel_ = max(max_toclevel_, toclevel);
663 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
664 << ", maximum is " << max_toclevel_);
666 return (error ? ERROR : OK);
670 void TextClass::readTitleType(Lexer & lexrc)
672 LexerKeyword titleTypeTags[] = {
673 { "commandafter", TITLE_COMMAND_AFTER },
674 { "environment", TITLE_ENVIRONMENT }
677 PushPopHelper pph(lexrc, titleTypeTags);
679 int le = lexrc.lex();
681 case Lexer::LEX_UNDEF:
682 lexrc.printError("Unknown output type `$$Token'");
684 case TITLE_COMMAND_AFTER:
685 case TITLE_ENVIRONMENT:
686 titletype_ = static_cast<TitleLatexType>(le);
689 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
695 void TextClass::readOutputType(Lexer & lexrc)
697 LexerKeyword outputTypeTags[] = {
698 { "docbook", DOCBOOK },
700 { "literate", LITERATE }
703 PushPopHelper pph(lexrc, outputTypeTags);
705 int le = lexrc.lex();
707 case Lexer::LEX_UNDEF:
708 lexrc.printError("Unknown output type `$$Token'");
713 outputType_ = static_cast<OutputType>(le);
716 LYXERR0("Unhandled value " << le);
722 void TextClass::readClassOptions(Lexer & lexrc)
732 LexerKeyword classOptionsTags[] = {
734 {"fontsize", CO_FONTSIZE },
735 {"header", CO_HEADER },
736 {"other", CO_OTHER },
737 {"pagestyle", CO_PAGESTYLE }
740 lexrc.pushTable(classOptionsTags);
742 while (!getout && lexrc.isOK()) {
743 int le = lexrc.lex();
745 case Lexer::LEX_UNDEF:
746 lexrc.printError("Unknown ClassOption tag `$$Token'");
753 opt_fontsize_ = rtrim(lexrc.getString());
757 opt_pagestyle_ = rtrim(lexrc.getString());
761 options_ = lexrc.getString();
765 class_header_ = subst(lexrc.getString(), """, "\"");
776 void TextClass::readFloat(Lexer & lexrc)
790 LexerKeyword floatTags[] = {
792 { "extension", FT_EXT },
793 { "guiname", FT_NAME },
794 { "latexbuiltin", FT_BUILTIN },
795 { "listname", FT_LISTNAME },
796 { "numberwithin", FT_WITHIN },
797 { "placement", FT_PLACEMENT },
798 { "style", FT_STYLE },
802 lexrc.pushTable(floatTags);
811 bool builtin = false;
814 while (!getout && lexrc.isOK()) {
815 int le = lexrc.lex();
817 case Lexer::LEX_UNDEF:
818 lexrc.printError("Unknown float tag `$$Token'");
825 type = lexrc.getString();
826 if (floatlist_.typeExist(type)) {
827 Floating const & fl = floatlist_.getType(type);
828 placement = fl.placement();
830 within = fl.within();
833 listName = fl.listName();
834 builtin = fl.builtin();
839 name = lexrc.getString();
843 placement = lexrc.getString();
847 ext = lexrc.getString();
851 within = lexrc.getString();
852 if (within == "none")
857 style = lexrc.getString();
861 listName = lexrc.getString();
865 builtin = lexrc.getBool();
873 // Here if have a full float if getout == true
875 Floating fl(type, placement, ext, within,
876 style, name, listName, builtin);
877 floatlist_.newFloat(fl);
878 // each float has its own counter
879 counters_.newCounter(from_ascii(type), from_ascii(within),
880 docstring(), docstring());
881 // also define sub-float counters
882 docstring const subtype = "sub-" + from_ascii(type);
883 counters_.newCounter(subtype, from_ascii(type),
884 "\\alph{" + subtype + "}", docstring());
891 bool TextClass::hasLayout(docstring const & n) const
893 docstring const name = n.empty() ? defaultLayoutName() : n;
895 return find_if(layoutlist_.begin(), layoutlist_.end(),
896 LayoutNamesEqual(name))
897 != layoutlist_.end();
901 bool TextClass::hasInsetLayout(docstring const & n) const
905 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
906 InsetLayouts::const_iterator en = insetlayoutlist_.end();
907 for (; it != en; ++it)
914 Layout const & TextClass::operator[](docstring const & name) const
916 LASSERT(!name.empty(), /**/);
919 find_if(begin(), end(), LayoutNamesEqual(name));
922 lyxerr << "We failed to find the layout '" << to_utf8(name)
923 << "' in the layout list. You MUST investigate!"
925 for (const_iterator cit = begin(); cit != end(); ++cit)
926 lyxerr << " " << to_utf8(cit->name()) << endl;
928 // we require the name to exist
929 LASSERT(false, /**/);
936 Layout & TextClass::operator[](docstring const & name)
938 LASSERT(!name.empty(), /**/);
940 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
943 LYXERR0("We failed to find the layout '" << to_utf8(name)
944 << "' in the layout list. You MUST investigate!");
945 for (const_iterator cit = begin(); cit != end(); ++cit)
946 LYXERR0(" " << to_utf8(cit->name()));
948 // we require the name to exist
949 LASSERT(false, /**/);
956 bool TextClass::deleteLayout(docstring const & name)
958 if (name == defaultLayoutName() || name == plainLayoutName())
961 LayoutList::iterator it =
962 remove_if(layoutlist_.begin(), layoutlist_.end(),
963 LayoutNamesEqual(name));
965 LayoutList::iterator end = layoutlist_.end();
966 bool const ret = (it != end);
967 layoutlist_.erase(it, end);
972 // Load textclass info if not loaded yet
973 bool TextClass::load(string const & path) const
978 // Read style-file, provided path is searched before the system ones
979 // If path is a file, it is loaded directly.
980 FileName layout_file(path);
981 if (!path.empty() && !layout_file.isReadableFile())
982 layout_file = FileName(addName(path, name_ + ".layout"));
983 if (layout_file.empty() || !layout_file.exists())
984 layout_file = libFileSearch("layouts", name_, "layout");
985 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
988 lyxerr << "Error reading `"
989 << to_utf8(makeDisplayPath(layout_file.absFilename()))
990 << "'\n(Check `" << name_
991 << "')\nCheck your installation and "
992 "try Options/Reconfigure..." << endl;
999 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1002 layoutlist_.push_back(createBasicLayout(n, true));
1006 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1008 // FIXME The fix for the InsetLayout part of 4812 would be here:
1009 // Add the InsetLayout to the document class if it is not found.
1011 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1012 while (!n.empty()) {
1013 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1014 if (cit != cen && cit->first == n)
1016 size_t i = n.find(':');
1017 if (i == string::npos)
1021 return plain_insetlayout_;
1025 docstring const & TextClass::defaultLayoutName() const
1027 // This really should come from the actual layout... (Lgb)
1028 return defaultlayout_;
1032 Layout const & TextClass::defaultLayout() const
1034 return operator[](defaultLayoutName());
1038 bool TextClass::isDefaultLayout(Layout const & layout) const
1040 return layout.name() == defaultLayoutName();
1044 bool TextClass::isPlainLayout(Layout const & layout) const
1046 return layout.name() == plainLayoutName();
1050 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1052 static Layout * defaultLayout = NULL;
1054 if (defaultLayout) {
1055 defaultLayout->setUnknown(unknown);
1056 defaultLayout->setName(name);
1057 return *defaultLayout;
1060 static char const * s = "Margin Static\n"
1061 "LatexType Paragraph\n"
1064 "AlignPossible Left, Right, Center\n"
1065 "LabelType No_Label\n"
1067 istringstream ss(s);
1068 Lexer lex(textClassTags);
1070 defaultLayout = new Layout;
1071 defaultLayout->setUnknown(unknown);
1072 defaultLayout->setName(name);
1073 if (!readStyle(lex, *defaultLayout)) {
1074 // The only way this happens is because the hardcoded layout above
1076 LASSERT(false, /**/);
1078 return *defaultLayout;
1081 /////////////////////////////////////////////////////////////////////////
1083 // DocumentClassBundle
1085 /////////////////////////////////////////////////////////////////////////
1087 DocumentClassBundle::~DocumentClassBundle()
1089 for (size_t i = 0; i != documentClasses_.size(); ++i)
1090 delete documentClasses_[i];
1091 documentClasses_.clear();
1094 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1096 DocumentClass * dc = new DocumentClass(baseClass);
1097 documentClasses_.push_back(dc);
1098 return *documentClasses_.back();
1102 DocumentClassBundle & DocumentClassBundle::get()
1104 static DocumentClassBundle singleton;
1109 DocumentClass & DocumentClassBundle::makeDocumentClass(
1110 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1112 DocumentClass & doc_class = newClass(baseClass);
1113 LayoutModuleList::const_iterator it = modlist.begin();
1114 LayoutModuleList::const_iterator en = modlist.end();
1115 for (; it != en; it++) {
1116 string const modName = *it;
1117 LyXModule * lm = moduleList[modName];
1119 docstring const msg =
1120 bformat(_("The module %1$s has been requested by\n"
1121 "this document but has not been found in the list of\n"
1122 "available modules. If you recently installed it, you\n"
1123 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1124 ExceptionMessage(WarningException,_("Module not available"),
1125 msg + _("Some layouts may not be available."));
1126 LYXERR0("DocumentClassBundle::makeDocumentClass(): Module " <<
1127 modName << " requested but not found in module list.");
1130 if (!lm->isAvailable()) {
1131 docstring const msg =
1132 bformat(_("The module %1$s requires a package that is\n"
1133 "not available in your LaTeX installation. LaTeX output\n"
1134 "may not be possible.\n"), from_utf8(modName));
1135 ExceptionMessage(WarningException, _("Package not available"), msg);
1137 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1138 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1139 docstring const msg =
1140 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1141 throw ExceptionMessage(WarningException, _("Read Error"), msg);
1148 /////////////////////////////////////////////////////////////////////////
1152 /////////////////////////////////////////////////////////////////////////
1154 DocumentClass::DocumentClass(LayoutFile const & tc)
1159 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1161 LayoutList::const_iterator it = layoutlist_.begin();
1162 LayoutList::const_iterator end = layoutlist_.end();
1163 for (; it != end; ++it)
1164 if (it->latexname() == lay)
1170 bool DocumentClass::provides(string const & p) const
1172 return provides_.find(p) != provides_.end();
1176 bool DocumentClass::hasTocLevels() const
1178 return min_toclevel_ != Layout::NOT_IN_TOC;
1182 /////////////////////////////////////////////////////////////////////////
1186 /////////////////////////////////////////////////////////////////////////
1188 ostream & operator<<(ostream & os, PageSides p)