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"
20 #include "CiteEnginesList.h"
24 #include "FloatList.h"
28 #include "ModuleList.h"
30 #include "frontends/alert.h"
32 #include "support/lassert.h"
33 #include "support/debug.h"
34 #include "support/ExceptionMessage.h"
35 #include "support/FileName.h"
36 #include "support/filetools.h"
37 #include "support/gettext.h"
38 #include "support/lstrings.h"
39 #include "support/os.h"
40 #include "support/TempFile.h"
51 using namespace lyx::support;
55 // Keep the changes documented in the Customization manual.
57 // If you change this format, then you MUST also make sure that
58 // your changes do not invalidate the hardcoded layout file in
59 // LayoutFile.cpp. Additions will never do so, but syntax changes
60 // could. See LayoutFileList::addEmptyClass() and, especially, the
61 // definition of the layoutpost string.
62 // You should also run the development/tools/updatelayouts.py script,
63 // to update the format of all of our layout files.
65 int const LAYOUT_FORMAT = 81; // rikiheck: GuiName for counters
68 // Layout format for the current lyx file format. Controls which format is
69 // targeted by Local Layout > Convert. In master, equal to LAYOUT_FORMAT.
70 int const LYXFILE_LAYOUT_FORMAT = LAYOUT_FORMAT;
75 class LayoutNamesEqual : public unary_function<Layout, bool> {
77 LayoutNamesEqual(docstring const & name)
80 bool operator()(Layout const & c) const
82 return c.name() == name_;
89 bool layout2layout(FileName const & filename, FileName const & tempfile,
90 int const format = LAYOUT_FORMAT)
92 FileName const script = libFileSearch("scripts", "layout2layout.py");
94 LYXERR0("Could not find layout conversion "
95 "script layout2layout.py.");
99 ostringstream command;
100 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
102 << ' ' << quoteName(filename.toFilesystemEncoding())
103 << ' ' << quoteName(tempfile.toFilesystemEncoding());
104 string const command_str = command.str();
106 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
108 cmd_ret const ret = runCommand(command_str);
109 if (ret.first != 0) {
110 if (format == LAYOUT_FORMAT)
111 LYXERR0("Conversion of layout with layout2layout.py has failed.");
118 string translateReadType(TextClass::ReadType rt)
121 case TextClass::BASECLASS:
123 case TextClass::MERGE:
125 case TextClass::MODULE:
126 return "module file";
127 case TextClass::CITE_ENGINE:
128 return "cite engine";
129 case TextClass::VALIDATION:
139 // This string should not be translated here,
140 // because it is a layout identifier.
141 docstring const TextClass::plain_layout_ = from_ascii(N_("Plain Layout"));
144 /////////////////////////////////////////////////////////////////////////
148 /////////////////////////////////////////////////////////////////////////
150 TextClass::TextClass()
151 : loaded_(false), tex_class_avail_(false),
152 opt_enginetype_("authoryear|numerical"), opt_fontsize_("10|11|12"),
153 opt_pagesize_("default|a4|a5|b5|letter|legal|executive"),
154 opt_pagestyle_("empty|plain|headings|fancy"), fontsize_format_("$$spt"), pagesize_("default"),
155 pagesize_format_("$$spaper"), pagestyle_("default"), tablestyle_("default"),
156 columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3), outputType_(LATEX),
157 outputFormat_("latex"), has_output_format_(false), defaultfont_(sane_font),
158 titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
159 min_toclevel_(0), max_toclevel_(0), maxcitenames_(2),
160 cite_full_author_list_(true), bibintoc_(false)
165 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
167 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
168 if (!lay.read(lexrc, *this)) {
169 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
173 lay.resfont = lay.font;
174 lay.resfont.realize(defaultfont_);
175 lay.reslabelfont = lay.labelfont;
176 lay.reslabelfont.realize(defaultfont_);
177 return true; // no errors
217 TC_ADDTOHTMLPREAMBLE,
239 LexerKeyword textClassTags[] = {
240 { "addtociteengine", TC_ADDTOCITEENGINE },
241 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
242 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
243 { "addtopreamble", TC_ADDTOPREAMBLE },
244 { "bibintoc", TC_BIBINTOC },
245 { "citeengine", TC_CITEENGINE },
246 { "citeenginetype", TC_CITEENGINETYPE },
247 { "citeformat", TC_CITEFORMAT },
248 { "citeframework", TC_CITEFRAMEWORK },
249 { "classoptions", TC_CLASSOPTIONS },
250 { "columns", TC_COLUMNS },
251 { "counter", TC_COUNTER },
252 { "defaultbiblio", TC_DEFAULTBIBLIO },
253 { "defaultfont", TC_DEFAULTFONT },
254 { "defaultmodule", TC_DEFAULTMODULE },
255 { "defaultstyle", TC_DEFAULTSTYLE },
256 { "excludesmodule", TC_EXCLUDESMODULE },
257 { "float", TC_FLOAT },
258 { "format", TC_FORMAT },
259 { "fullauthorlist", TC_FULLAUTHORLIST },
260 { "htmlpreamble", TC_HTMLPREAMBLE },
261 { "htmlstyles", TC_HTMLSTYLES },
262 { "htmltocsection", TC_HTMLTOCSECTION },
263 { "ifcounter", TC_IFCOUNTER },
264 { "input", TC_INPUT },
265 { "insetlayout", TC_INSETLAYOUT },
266 { "leftmargin", TC_LEFTMARGIN },
267 { "maxcitenames", TC_MAXCITENAMES },
268 { "modifystyle", TC_MODIFYSTYLE },
269 { "nocounter", TC_NOCOUNTER },
270 { "nofloat", TC_NOFLOAT },
271 { "noinsetlayout", TC_NOINSETLAYOUT },
272 { "nostyle", TC_NOSTYLE },
273 { "outlinername", TC_OUTLINERNAME },
274 { "outputformat", TC_OUTPUTFORMAT },
275 { "outputtype", TC_OUTPUTTYPE },
276 { "packageoptions", TC_PKGOPTS },
277 { "pagesize", TC_PAGESIZE },
278 { "pagestyle", TC_PAGESTYLE },
279 { "preamble", TC_PREAMBLE },
280 { "provides", TC_PROVIDES },
281 { "providesmodule", TC_PROVIDESMODULE },
282 { "providestyle", TC_PROVIDESTYLE },
283 { "requires", TC_REQUIRES },
284 { "rightmargin", TC_RIGHTMARGIN },
285 { "secnumdepth", TC_SECNUMDEPTH },
286 { "sides", TC_SIDES },
287 { "style", TC_STYLE },
288 { "tablestyle", TC_TABLESTYLE },
289 { "titlelatexname", TC_TITLELATEXNAME },
290 { "titlelatextype", TC_TITLELATEXTYPE },
291 { "tocdepth", TC_TOCDEPTH }
297 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
299 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
300 TempFile tmp("convertXXXXXX.layout");
301 FileName const tempfile = tmp.name();
302 bool success = layout2layout(filename, tempfile);
304 success = readWithoutConv(tempfile, rt) == OK;
309 std::string TextClass::convert(std::string const & str)
311 TempFile tmp1("localXXXXXX.layout");
312 FileName const fn = tmp1.name();
313 ofstream os(fn.toFilesystemEncoding().c_str());
316 TempFile tmp2("convert_localXXXXXX.layout");
317 FileName const tempfile = tmp2.name();
318 bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
321 ifstream is(tempfile.toFilesystemEncoding().c_str());
333 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
335 if (!filename.isReadableFile()) {
336 lyxerr << "Cannot read layout file `" << filename << "'."
341 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
342 to_utf8(makeDisplayPath(filename.absFileName())));
344 // Define the plain layout used in table cells, ert, etc. Note that
345 // we do this before loading any layout file, so that classes can
346 // override features of this layout if they should choose to do so.
347 if (rt == BASECLASS && !hasLayout(plain_layout_))
348 layoutlist_.push_back(createBasicLayout(plain_layout_));
350 Lexer lexrc(textClassTags);
351 lexrc.setFile(filename);
352 ReturnValues retval = read(lexrc, rt);
354 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
355 to_utf8(makeDisplayPath(filename.absFileName())));
361 bool TextClass::read(FileName const & filename, ReadType rt)
363 ReturnValues const retval = readWithoutConv(filename, rt);
364 if (retval != FORMAT_MISMATCH)
367 bool const worx = convertLayoutFormat(filename, rt);
369 LYXERR0 ("Unable to convert " << filename <<
370 " to format " << LAYOUT_FORMAT);
375 TextClass::ReturnValues TextClass::validate(std::string const & str)
378 return tc.read(str, VALIDATION);
382 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
384 Lexer lexrc(textClassTags);
385 istringstream is(str);
387 ReturnValues retval = read(lexrc, rt);
389 if (retval != FORMAT_MISMATCH)
392 // write the layout string to a temporary file
393 TempFile tmp("TextClass_read");
394 FileName const tempfile = tmp.name();
395 ofstream os(tempfile.toFilesystemEncoding().c_str());
397 LYXERR0("Unable to create temporary file");
403 // now try to convert it to LAYOUT_FORMAT
404 if (!convertLayoutFormat(tempfile, rt)) {
405 LYXERR0("Unable to convert internal layout information to format "
414 // Reads a textclass structure from file.
415 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
420 // The first usable line should be
421 // Format LAYOUT_FORMAT
422 if (lexrc.lex() != TC_FORMAT || !lexrc.next()
423 || lexrc.getInteger() != LAYOUT_FORMAT)
424 return FORMAT_MISMATCH;
428 while (lexrc.isOK() && !error) {
429 int le = lexrc.lex();
432 case Lexer::LEX_FEOF:
435 case Lexer::LEX_UNDEF:
436 lexrc.printError("Unknown TextClass tag `$$Token'");
444 // used below to track whether we are in an IfStyle or IfCounter tag.
445 bool modifystyle = false;
446 bool providestyle = false;
447 bool ifcounter = false;
449 switch (static_cast<TextClassTags>(le)) {
453 lexrc.printError("Duplicate Format directive");
456 case TC_OUTPUTFORMAT:
458 outputFormat_ = lexrc.getString();
459 has_output_format_ = true;
464 readOutputType(lexrc);
465 switch(outputType_) {
467 outputFormat_ = "latex";
470 outputFormat_ = "docbook";
473 outputFormat_ = "literate";
478 case TC_INPUT: // Include file
481 string const inc = lexrc.getString();
482 if (!path().empty() && (prefixIs(inc, "./") ||
483 prefixIs(inc, "../")))
484 tmp = fileSearch(path(), inc, "layout");
486 tmp = libFileSearch("layouts", inc,
490 lexrc.printError("Could not find input file: " + inc);
492 } else if (!read(tmp, MERGE)) {
493 lexrc.printError("Error reading input file: " + tmp.absFileName());
499 case TC_DEFAULTSTYLE:
501 docstring const name = from_utf8(subst(lexrc.getString(),
503 defaultlayout_ = name;
510 case TC_PROVIDESTYLE:
511 // if modifystyle is true, then we got here by falling through
512 // so we are not in an ProvideStyle block
518 lexrc.printError("No name given for style: `$$Token'.");
522 docstring const name = from_utf8(subst(lexrc.getString(),
525 string s = "Could not read name for style: `$$Token' "
526 + lexrc.getString() + " is probably not valid UTF-8!";
529 // Since we couldn't read the name, we just scan the rest
530 // of the style and discard it.
531 error = !readStyle(lexrc, lay);
535 bool const have_layout = hasLayout(name);
537 // If the layout already exists, then we want to add it to
538 // the existing layout, as long as we are not in an ProvideStyle
540 if (have_layout && !providestyle) {
541 Layout & lay = operator[](name);
542 error = !readStyle(lexrc, lay);
544 // If the layout does not exist, then we want to create a new
545 // one, but not if we are in a ModifyStyle block.
546 else if (!have_layout && !modifystyle) {
548 layout.setName(name);
549 error = !readStyle(lexrc, layout);
551 layoutlist_.push_back(layout);
553 if (defaultlayout_.empty()) {
554 // We do not have a default layout yet, so we choose
555 // the first layout we encounter.
556 defaultlayout_ = name;
559 // There are two ways to get here:
560 // (i) The layout exists but we are in an ProvideStyle block
561 // (ii) The layout doesn't exist, but we are in an ModifyStyle
563 // Either way, we just scan the rest and discard it
566 // signal to coverity that we do not care about the result
567 (void)readStyle(lexrc, lay);
574 docstring const style = from_utf8(subst(lexrc.getString(),
576 if (!deleteLayout(style))
577 lyxerr << "Cannot delete style `"
578 << to_utf8(style) << '\'' << endl;
582 case TC_NOINSETLAYOUT:
584 docstring const style = from_utf8(subst(lexrc.getString(),
586 if (!deleteInsetLayout(style))
587 LYXERR0("Style `" << style << "' cannot be removed\n"
588 "because it was not found!");
594 columns_ = lexrc.getInteger();
599 switch (lexrc.getInteger()) {
600 case 1: sides_ = OneSide; break;
601 case 2: sides_ = TwoSides; break;
603 lyxerr << "Impossible number of page"
604 " sides, setting to one."
614 pagesize_ = rtrim(lexrc.getString());
619 pagestyle_ = rtrim(lexrc.getString());
623 defaultfont_ = lyxRead(lexrc);
624 if (!defaultfont_.resolved()) {
625 lexrc.printError("Warning: defaultfont should "
626 "be fully instantiated!");
627 defaultfont_.realize(sane_font);
633 secnumdepth_ = lexrc.getInteger();
638 tocdepth_ = lexrc.getInteger();
641 // First step to support options
642 case TC_CLASSOPTIONS:
643 readClassOptions(lexrc);
647 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
650 case TC_HTMLPREAMBLE:
651 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
655 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
658 case TC_HTMLTOCSECTION:
659 html_toc_section_ = from_utf8(trim(lexrc.getString()));
662 case TC_ADDTOPREAMBLE:
663 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
666 case TC_ADDTOHTMLPREAMBLE:
667 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
670 case TC_ADDTOHTMLSTYLES:
671 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
676 string const feature = lexrc.getString();
678 if (lexrc.getInteger())
679 provides_.insert(feature);
681 provides_.erase(feature);
687 vector<string> const req
688 = getVectorFromString(lexrc.getString());
689 requires_.insert(req.begin(), req.end());
695 string const pkg = lexrc.getString();
697 string const options = lexrc.getString();
698 package_options_[pkg] = options;
702 case TC_DEFAULTMODULE: {
704 string const module = lexrc.getString();
705 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
706 default_modules_.push_back(module);
710 case TC_PROVIDESMODULE: {
712 string const module = lexrc.getString();
713 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
714 provided_modules_.push_back(module);
718 case TC_EXCLUDESMODULE: {
720 string const module = lexrc.getString();
721 // modules already have their own way to exclude other modules
723 LYXERR0("ExcludesModule tag cannot be used in a module!");
726 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
727 excluded_modules_.push_back(module);
731 case TC_LEFTMARGIN: // left margin type
733 leftmargin_ = lexrc.getDocString();
736 case TC_RIGHTMARGIN: // right margin type
738 rightmargin_ = lexrc.getDocString();
741 case TC_INSETLAYOUT: {
743 lexrc.printError("No name given for InsetLayout: `$$Token'.");
747 docstring const name = subst(lexrc.getDocString(), '_', ' ');
748 bool const validating = (rt == VALIDATION);
750 string s = "Could not read name for InsetLayout: `$$Token' "
751 + lexrc.getString() + " is probably not valid UTF-8!";
754 // Since we couldn't read the name, we just scan the rest
755 // of the style and discard it.
756 il.read(lexrc, *this);
757 // Let's try to continue rather than abort, unless we're validating
758 // in which case we want to report the error
761 } else if (hasInsetLayout(name)) {
762 InsetLayout & il = insetlayoutlist_[name];
763 error = !il.read(lexrc, *this, validating);
767 error = !il.read(lexrc, *this, validating);
769 insetlayoutlist_[name] = il;
775 error = !readFloat(lexrc);
779 error = !readCiteEngine(lexrc, rt);
782 case TC_ADDTOCITEENGINE:
783 error = !readCiteEngine(lexrc, rt, true);
786 case TC_CITEENGINETYPE:
788 opt_enginetype_ = rtrim(lexrc.getString());
792 error = !readCiteFormat(lexrc, rt);
795 case TC_CITEFRAMEWORK:
797 citeframework_ = rtrim(lexrc.getString());
800 case TC_MAXCITENAMES:
802 maxcitenames_ = size_t(lexrc.getInteger());
805 case TC_DEFAULTBIBLIO:
807 vector<string> const dbs =
808 getVectorFromString(rtrim(lexrc.getString()), "|");
809 for (auto const & dbase : dbs) {
810 if (!contains(dbase, ':')) {
811 vector<string> const enginetypes =
812 getVectorFromString(opt_enginetype_, "|");
813 for (string const & s: enginetypes)
814 cite_default_biblio_style_[s] = dbase;
817 string const db = split(dbase, eng, ':');
818 cite_default_biblio_style_[eng] = db;
826 bibintoc_ = lexrc.getBool();
829 case TC_FULLAUTHORLIST:
831 cite_full_author_list_ &= lexrc.getBool();
836 docstring const cnt = lexrc.getDocString();
837 if (!counters_.remove(cnt))
838 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
847 docstring const name = lexrc.getDocString();
849 string s = "Could not read name for counter: `$$Token' "
850 + lexrc.getString() + " is probably not valid UTF-8!";
851 lexrc.printError(s.c_str());
853 // Since we couldn't read the name, we just scan the rest
857 error = !counters_.read(lexrc, name, !ifcounter);
860 lexrc.printError("No name given for style: `$$Token'.");
865 case TC_TITLELATEXTYPE:
866 readTitleType(lexrc);
869 case TC_TITLELATEXNAME:
871 titlename_ = lexrc.getString();
876 string const nofloat = lexrc.getString();
877 floatlist_.erase(nofloat);
881 case TC_OUTLINERNAME:
882 error = !readOutlinerName(lexrc);
887 tablestyle_ = rtrim(lexrc.getString());
892 // at present, we abort if we encounter an error,
893 // so there is no point continuing.
900 if (defaultlayout_.empty()) {
901 LYXERR0("Error: Textclass '" << name_
902 << "' is missing a defaultstyle.");
906 // Try to erase "stdinsets" from the provides_ set.
908 // Provides stdinsets 1
909 // declaration simply tells us that the standard insets have been
910 // defined. (It's found in stdinsets.inc but could also be used in
911 // user-defined files.) There isn't really any such package. So we
912 // might as well go ahead and erase it.
913 // If we do not succeed, then it was not there, which means that
914 // the textclass did not provide the definitions of the standard
915 // insets. So we need to try to load them.
916 size_type const erased = provides_.erase("stdinsets");
918 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
921 frontend::Alert::warning(_("Missing File"),
922 _("Could not find stdinsets.inc! This may lead to data loss!"));
924 } else if (!read(tmp, MERGE)) {
925 frontend::Alert::warning(_("Corrupt File"),
926 _("Could not read stdinsets.inc! This may lead to data loss!"));
931 min_toclevel_ = Layout::NOT_IN_TOC;
932 max_toclevel_ = Layout::NOT_IN_TOC;
933 for (auto const & lay : *this) {
934 int const toclevel = lay.toclevel;
935 if (toclevel != Layout::NOT_IN_TOC) {
936 if (min_toclevel_ == Layout::NOT_IN_TOC)
937 min_toclevel_ = toclevel;
939 min_toclevel_ = min(min_toclevel_, toclevel);
940 max_toclevel_ = max(max_toclevel_, toclevel);
943 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
944 << ", maximum is " << max_toclevel_);
946 return (error ? ERROR : OK);
950 void TextClass::readTitleType(Lexer & lexrc)
952 LexerKeyword titleTypeTags[] = {
953 { "commandafter", TITLE_COMMAND_AFTER },
954 { "environment", TITLE_ENVIRONMENT }
957 PushPopHelper pph(lexrc, titleTypeTags);
959 int le = lexrc.lex();
961 case Lexer::LEX_UNDEF:
962 lexrc.printError("Unknown output type `$$Token'");
964 case TITLE_COMMAND_AFTER:
965 case TITLE_ENVIRONMENT:
966 titletype_ = static_cast<TitleLatexType>(le);
969 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
975 void TextClass::readOutputType(Lexer & lexrc)
977 LexerKeyword outputTypeTags[] = {
978 { "docbook", DOCBOOK },
980 { "literate", LITERATE }
983 PushPopHelper pph(lexrc, outputTypeTags);
985 int le = lexrc.lex();
987 case Lexer::LEX_UNDEF:
988 lexrc.printError("Unknown output type `$$Token'");
993 outputType_ = static_cast<OutputType>(le);
996 LYXERR0("Unhandled value " << le);
1002 void TextClass::readClassOptions(Lexer & lexrc)
1015 LexerKeyword classOptionsTags[] = {
1017 {"fontsize", CO_FONTSIZE },
1018 {"fontsizeformat", CO_FONTSIZE_FORMAT },
1019 {"header", CO_HEADER },
1020 {"other", CO_OTHER },
1021 {"pagesize", CO_PAGESIZE },
1022 {"pagesizeformat", CO_PAGESIZE_FORMAT },
1023 {"pagestyle", CO_PAGESTYLE }
1026 lexrc.pushTable(classOptionsTags);
1027 bool getout = false;
1028 while (!getout && lexrc.isOK()) {
1029 int le = lexrc.lex();
1031 case Lexer::LEX_UNDEF:
1032 lexrc.printError("Unknown ClassOption tag `$$Token'");
1040 opt_fontsize_ = rtrim(lexrc.getString());
1042 case CO_FONTSIZE_FORMAT:
1044 fontsize_format_ = rtrim(lexrc.getString());
1048 opt_pagesize_ = rtrim(lexrc.getString());
1050 case CO_PAGESIZE_FORMAT:
1052 pagesize_format_ = rtrim(lexrc.getString());
1056 opt_pagestyle_ = rtrim(lexrc.getString());
1060 if (options_.empty())
1061 options_ = lexrc.getString();
1063 options_ += ',' + lexrc.getString();
1067 class_header_ = subst(lexrc.getString(), """, "\"");
1078 vector<CitationStyle> const & TextClass::getCiteStyles(
1079 CiteEngineType const & type) const
1081 static vector<CitationStyle> empty;
1082 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1083 if (it == cite_styles_.end())
1089 bool TextClass::readCiteEngine(Lexer & lexrc, ReadType rt, bool const add)
1091 int const type = readCiteEngineType(lexrc);
1092 bool authoryear = (type & ENGINE_TYPE_AUTHORYEAR);
1093 bool numerical = (type & ENGINE_TYPE_NUMERICAL);
1094 bool defce = (type & ENGINE_TYPE_DEFAULT);
1096 if (rt == CITE_ENGINE) {
1097 // The cite engines are not supposed to overwrite
1098 // CiteStyle defined by the class or a module.
1100 authoryear = getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1102 numerical = getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1104 defce = getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1107 if (rt != CITE_ENGINE && !add) {
1108 // Reset if we defined CiteStyle
1109 // from the class or a module
1111 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1113 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1115 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1119 bool getout = false;
1120 while (!getout && lexrc.isOK()) {
1122 def = lexrc.getString();
1123 def = subst(def, " ", "");
1124 def = subst(def, "\t", "");
1125 if (compare_ascii_no_case(def, "end") == 0) {
1130 char ichar = def[0];
1133 if (isUpperCase(ichar)) {
1134 cs.forceUpperCase = true;
1135 def[0] = lowercase(ichar);
1138 /** For portability reasons (between different
1139 * cite engines such as natbib and biblatex),
1140 * we distinguish between:
1141 * 1. The LyX name as output in the LyX file
1142 * 2. Possible aliases that might fall back to
1143 * the given LyX name in the current engine
1144 * 3. The actual LaTeX command that is output
1145 * (2) and (3) are optional.
1146 * Also, the GUI string for the starred version can
1149 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1158 ScanMode mode = LyXName;
1159 ScanMode oldmode = LyXName;
1164 size_t const n = def.size();
1165 for (size_t i = 0; i != n; ++i) {
1169 else if (ichar == '=')
1171 else if (ichar == '<') {
1174 } else if (ichar == '>')
1176 else if (mode == LaTeXCmd)
1178 else if (mode == StarDesc)
1180 else if (ichar == '$')
1181 cs.hasQualifiedList = true;
1182 else if (ichar == '*')
1183 cs.hasStarredVersion = true;
1184 else if (ichar == '[' && cs.textAfter)
1185 cs.textBefore = true;
1186 else if (ichar == '[')
1187 cs.textAfter = true;
1188 else if (ichar != ']') {
1196 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1197 if (!alias.empty()) {
1198 vector<string> const aliases = getVectorFromString(alias);
1199 for (string const & s: aliases)
1200 cite_command_aliases_[s] = lyx_cmd;
1202 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1203 int size = int(stardesc.size());
1205 cs.stardesc = stardescs[0];
1207 cs.startooltip = stardescs[1];
1210 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1212 class_cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1214 class_cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1217 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1219 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1221 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1224 // If we do AddToCiteEngine, do not apply yet,
1225 // except if we have already a style to add something to
1226 bool apply_ay = !add;
1227 bool apply_num = !add;
1228 bool apply_def = !add;
1230 if (type & ENGINE_TYPE_AUTHORYEAR)
1231 apply_ay = !getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1232 if (type & ENGINE_TYPE_NUMERICAL)
1233 apply_num = !getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1234 if (type & ENGINE_TYPE_DEFAULT)
1235 apply_def = !getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1238 // Add the styles from AddToCiteEngine to the class' styles
1239 // (but only if they are not yet defined)
1240 for (auto const & cis : class_cite_styles_) {
1241 // Only consider the current CiteEngineType
1242 if (!(type & cis.first))
1244 for (auto const & ciss : cis.second) {
1245 bool defined = false;
1246 // Check if the style "name" is already def'ed
1247 for (auto const & av : getCiteStyles(cis.first))
1248 if (av.name == ciss.name)
1251 if (cis.first == ENGINE_TYPE_AUTHORYEAR && apply_ay)
1252 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(ciss);
1253 else if (cis.first == ENGINE_TYPE_NUMERICAL && apply_num)
1254 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(ciss);
1255 else if (cis.first == ENGINE_TYPE_DEFAULT && apply_def)
1256 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(ciss);
1260 if (type & ENGINE_TYPE_AUTHORYEAR && apply_ay)
1261 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1262 if (type & ENGINE_TYPE_NUMERICAL && apply_num)
1263 class_cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1264 if (type & ENGINE_TYPE_DEFAULT && apply_def)
1265 class_cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1270 int TextClass::readCiteEngineType(Lexer & lexrc) const
1272 static_assert(ENGINE_TYPE_DEFAULT ==
1273 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL),
1274 "Incorrect default engine type");
1275 if (!lexrc.next()) {
1276 lexrc.printError("No cite engine type given for token: `$$Token'.");
1277 return ENGINE_TYPE_DEFAULT;
1279 string const type = rtrim(lexrc.getString());
1280 if (compare_ascii_no_case(type, "authoryear") == 0)
1281 return ENGINE_TYPE_AUTHORYEAR;
1282 else if (compare_ascii_no_case(type, "numerical") == 0)
1283 return ENGINE_TYPE_NUMERICAL;
1284 else if (compare_ascii_no_case(type, "default") != 0) {
1285 string const s = "Unknown cite engine type `" + type
1286 + "' given for token: `$$Token',";
1287 lexrc.printError(s);
1289 return ENGINE_TYPE_DEFAULT;
1293 bool TextClass::readCiteFormat(Lexer & lexrc, ReadType rt)
1295 int const type = readCiteEngineType(lexrc);
1298 // Cite engine definitions do not overwrite existing
1299 // definitions from the class or a module
1300 bool const overwrite = rt != CITE_ENGINE;
1301 while (lexrc.isOK()) {
1303 etype = lexrc.getString();
1304 if (compare_ascii_no_case(etype, "end") == 0)
1309 definition = lexrc.getString();
1310 char initchar = etype[0];
1311 if (initchar == '#')
1313 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1314 bool defined = false;
1315 bool aydefined = false;
1316 bool numdefined = false;
1317 // Check if the macro is already def'ed
1318 for (auto const & cm : cite_macros_) {
1319 if (!(type & cm.first))
1321 if (cm.second.find(etype) != cm.second.end()) {
1322 if (type == cm.first)
1323 // defined as default or specific type
1325 if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1326 // defined for author-year
1328 else if (cm.first == ENGINE_TYPE_NUMERICAL)
1329 // defined for numerical
1333 if (!defined || overwrite) {
1334 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1335 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1336 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1337 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1338 if (type == ENGINE_TYPE_DEFAULT)
1339 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1342 bool defined = false;
1343 bool aydefined = false;
1344 bool numdefined = false;
1345 // Check if the format is already def'ed
1346 for (auto const & cm : cite_formats_) {
1347 if (!(type & cm.first))
1349 if (cm.second.find(etype) != cm.second.end()) {
1350 if (type == cm.first)
1351 // defined as default or specific type
1353 if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1354 // defined for author-year
1356 else if (cm.first == ENGINE_TYPE_NUMERICAL)
1357 // defined for numerical
1361 if (!defined || overwrite){
1362 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1363 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1364 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1365 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1366 if (type == ENGINE_TYPE_DEFAULT)
1367 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1375 bool TextClass::readFloat(Lexer & lexrc)
1392 FT_ALLOWED_PLACEMENT,
1399 LexerKeyword floatTags[] = {
1400 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1401 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1402 { "allowswide", FT_ALLOWS_WIDE },
1404 { "extension", FT_EXT },
1405 { "guiname", FT_NAME },
1406 { "htmlattr", FT_HTMLATTR },
1407 { "htmlstyle", FT_HTMLSTYLE },
1408 { "htmltag", FT_HTMLTAG },
1409 { "ispredefined", FT_PREDEFINED },
1410 { "listcommand", FT_LISTCOMMAND },
1411 { "listname", FT_LISTNAME },
1412 { "numberwithin", FT_WITHIN },
1413 { "placement", FT_PLACEMENT },
1414 { "refprefix", FT_REFPREFIX },
1415 { "requires", FT_REQUIRES },
1416 { "style", FT_STYLE },
1417 { "type", FT_TYPE },
1418 { "usesfloatpkg", FT_USESFLOAT }
1421 lexrc.pushTable(floatTags);
1425 docstring htmlstyle;
1431 string allowed_placement = "!htbpH";
1437 bool usesfloat = true;
1438 bool ispredefined = false;
1439 bool allowswide = true;
1440 bool allowssideways = true;
1442 bool getout = false;
1443 while (!getout && lexrc.isOK()) {
1444 int le = lexrc.lex();
1446 case Lexer::LEX_UNDEF:
1447 lexrc.printError("Unknown float tag `$$Token'");
1455 type = lexrc.getString();
1456 if (floatlist_.typeExist(type)) {
1457 Floating const & fl = floatlist_.getType(type);
1458 placement = fl.placement();
1460 within = fl.within();
1463 listname = fl.listName();
1464 usesfloat = fl.usesFloatPkg();
1465 ispredefined = fl.isPredefined();
1466 listcommand = fl.listCommand();
1467 refprefix = fl.refPrefix();
1472 name = lexrc.getString();
1476 placement = lexrc.getString();
1478 case FT_ALLOWED_PLACEMENT:
1480 allowed_placement = lexrc.getString();
1484 ext = lexrc.getString();
1488 within = lexrc.getString();
1489 if (within == "none")
1494 style = lexrc.getString();
1496 case FT_LISTCOMMAND:
1498 listcommand = lexrc.getString();
1502 refprefix = lexrc.getString();
1506 listname = lexrc.getString();
1510 usesfloat = lexrc.getBool();
1514 required = lexrc.getString();
1518 ispredefined = lexrc.getBool();
1520 case FT_ALLOWS_SIDEWAYS:
1522 allowssideways = lexrc.getBool();
1524 case FT_ALLOWS_WIDE:
1526 allowswide = lexrc.getBool();
1530 htmlattr = lexrc.getString();
1534 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1538 htmltag = lexrc.getString();
1548 // Here we have a full float if getout == true
1550 if (!usesfloat && listcommand.empty()) {
1551 // if this float uses the same auxfile as an existing one,
1552 // there is no need for it to provide a list command.
1553 bool found_ext = false;
1554 for (auto const & f : floatlist_) {
1555 if (f.second.ext() == ext) {
1561 LYXERR0("The layout does not provide a list command " <<
1562 "for the float `" << type << "'. LyX will " <<
1563 "not be able to produce a float list.");
1565 Floating fl(type, placement, ext, within, style, name,
1566 listname, listcommand, refprefix, allowed_placement,
1567 htmltag, htmlattr, htmlstyle, required, usesfloat,
1568 ispredefined, allowswide, allowssideways);
1569 floatlist_.newFloat(fl);
1570 // each float has its own counter
1571 counters_.newCounter(from_ascii(type), from_ascii(within),
1572 docstring(), docstring(),
1573 bformat(_("%1$s (Float)"), from_ascii(name)));
1574 // also define sub-float counters
1575 docstring const subtype = "sub-" + from_ascii(type);
1576 counters_.newCounter(subtype, from_ascii(type),
1577 "\\alph{" + subtype + "}", docstring(),
1578 bformat(_("Sub-%1$s (Float)"), from_ascii(name)));
1584 bool TextClass::readOutlinerName(Lexer & lexrc)
1589 type = lexrc.getString();
1591 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1595 name = lexrc.getDocString();
1597 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1600 outliner_names_[type] = name;
1605 string const & TextClass::prerequisites(string const & sep) const
1607 if (contains(prerequisites_, ',')) {
1608 vector<string> const pres = getVectorFromString(prerequisites_);
1609 prerequisites_ = getStringFromVector(pres, sep);
1611 return prerequisites_;
1615 bool TextClass::hasLayout(docstring const & n) const
1617 docstring const name = n.empty() ? defaultLayoutName() : n;
1619 return find_if(layoutlist_.begin(), layoutlist_.end(),
1620 LayoutNamesEqual(name))
1621 != layoutlist_.end();
1625 bool TextClass::hasInsetLayout(docstring const & n) const
1629 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1630 return it != insetlayoutlist_.end();
1634 Layout const & TextClass::operator[](docstring const & name) const
1636 LATTEST(!name.empty());
1639 find_if(begin(), end(), LayoutNamesEqual(name));
1642 LYXERR0("We failed to find the layout '" << name
1643 << "' in the layout list. You MUST investigate!");
1644 for (auto const & lay : *this)
1645 lyxerr << " " << to_utf8(lay.name()) << endl;
1647 // We require the name to exist
1648 static const Layout dummy;
1649 LASSERT(false, return dummy);
1656 Layout & TextClass::operator[](docstring const & name)
1658 LATTEST(!name.empty());
1659 // Safe to continue, given what we do below.
1661 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1664 LYXERR0("We failed to find the layout '" << to_utf8(name)
1665 << "' in the layout list. You MUST investigate!");
1666 for (auto const & lay : *this)
1667 LYXERR0(" " << to_utf8(lay.name()));
1669 // we require the name to exist
1671 // we are here only in release mode
1672 layoutlist_.push_back(createBasicLayout(name, true));
1673 it = find_if(begin(), end(), LayoutNamesEqual(name));
1680 bool TextClass::deleteLayout(docstring const & name)
1682 if (name == defaultLayoutName() || name == plainLayoutName())
1685 LayoutList::iterator it =
1686 remove_if(layoutlist_.begin(), layoutlist_.end(),
1687 LayoutNamesEqual(name));
1689 LayoutList::iterator end = layoutlist_.end();
1690 bool const ret = (it != end);
1691 layoutlist_.erase(it, end);
1696 bool TextClass::deleteInsetLayout(docstring const & name)
1698 return insetlayoutlist_.erase(name);
1702 // Load textclass info if not loaded yet
1703 bool TextClass::load(string const & path) const
1708 // Read style-file, provided path is searched before the system ones
1709 // If path is a file, it is loaded directly.
1710 FileName layout_file(path);
1711 if (!path.empty() && !layout_file.isReadableFile())
1712 layout_file = FileName(addName(path, name_ + ".layout"));
1713 if (layout_file.empty() || !layout_file.exists())
1714 layout_file = libFileSearch("layouts", name_, "layout");
1715 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1718 lyxerr << "Error reading `"
1719 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1720 << "'\n(Check `" << name_
1721 << "')\nCheck your installation and "
1722 "try Options/Reconfigure..."
1730 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1735 layoutlist_.push_back(createBasicLayout(n, true));
1740 string DocumentClass::forcedLayouts() const
1744 for (auto const & lay : *this) {
1745 if (lay.forcelocal > 0) {
1747 os << "Format " << LAYOUT_FORMAT << '\n';
1757 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1759 // FIXME The fix for the InsetLayout part of 4812 would be here:
1760 // Add the InsetLayout to the document class if it is not found.
1762 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1763 while (!n.empty()) {
1764 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1765 if (cit != cen && cit->first == n) {
1766 if (cit->second.obsoleted_by().empty())
1768 n = cit->second.obsoleted_by();
1769 return insetLayout(n);
1771 // If we have a generic prefix (e.g., "Note:"),
1772 // try if this one alone is found.
1773 size_t i = n.find(':');
1774 if (i == string::npos)
1778 // Layout "name" not found.
1779 return plainInsetLayout();
1783 InsetLayout const & DocumentClass::plainInsetLayout() {
1784 static const InsetLayout plain_insetlayout_;
1785 return plain_insetlayout_;
1789 docstring const & TextClass::defaultLayoutName() const
1791 return defaultlayout_;
1795 Layout const & TextClass::defaultLayout() const
1797 return operator[](defaultLayoutName());
1801 bool TextClass::isDefaultLayout(Layout const & layout) const
1803 return layout.name() == defaultLayoutName();
1807 bool TextClass::isPlainLayout(Layout const & layout) const
1809 return layout.name() == plainLayoutName();
1813 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1815 static Layout * defaultLayout = nullptr;
1817 if (defaultLayout) {
1818 defaultLayout->setUnknown(unknown);
1819 defaultLayout->setName(name);
1820 return *defaultLayout;
1823 static char const * s = "Margin Static\n"
1824 "LatexType Paragraph\n"
1827 "AlignPossible Left, Right, Center\n"
1828 "LabelType No_Label\n"
1830 istringstream ss(s);
1831 Lexer lex(textClassTags);
1833 defaultLayout = new Layout;
1834 defaultLayout->setUnknown(unknown);
1835 defaultLayout->setName(name);
1836 if (!readStyle(lex, *defaultLayout)) {
1837 // The only way this happens is because the hardcoded layout above
1841 return *defaultLayout;
1845 DocumentClassPtr getDocumentClass(
1846 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1847 string const & cengine, bool const clone)
1849 DocumentClassPtr doc_class =
1850 DocumentClassPtr(new DocumentClass(baseClass));
1851 for (auto const & mod : modlist) {
1852 LyXModule * lm = theModuleList[mod];
1854 docstring const msg =
1855 bformat(_("The module %1$s has been requested by\n"
1856 "this document but has not been found in the list of\n"
1857 "available modules. If you recently installed it, you\n"
1858 "probably need to reconfigure LyX.\n"), from_utf8(mod));
1860 frontend::Alert::warning(_("Module not available"), msg);
1863 if (!lm->isAvailable() && !clone) {
1864 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1865 docstring const msg =
1866 bformat(_("The module %1$s requires a package that is not\n"
1867 "available in your LaTeX installation, or a converter that\n"
1868 "you have not installed. LaTeX output may not be possible.\n"
1869 "Missing prerequisites:\n"
1871 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1872 from_utf8(mod), prereqs);
1873 frontend::Alert::warning(_("Package not available"), msg, true);
1875 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1876 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1877 docstring const msg =
1878 bformat(_("Error reading module %1$s\n"), from_utf8(mod));
1879 frontend::Alert::warning(_("Read Error"), msg);
1883 if (cengine.empty())
1886 LyXCiteEngine * ce = theCiteEnginesList[cengine];
1888 docstring const msg =
1889 bformat(_("The cite engine %1$s has been requested by\n"
1890 "this document but has not been found in the list of\n"
1891 "available engines. If you recently installed it, you\n"
1892 "probably need to reconfigure LyX.\n"), from_utf8(cengine));
1894 frontend::Alert::warning(_("Cite Engine not available"), msg);
1895 } else if (!ce->isAvailable() && !clone) {
1896 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1897 docstring const msg =
1898 bformat(_("The cite engine %1$s requires a package that is not\n"
1899 "available in your LaTeX installation, or a converter that\n"
1900 "you have not installed. LaTeX output may not be possible.\n"
1901 "Missing prerequisites:\n"
1903 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1904 from_utf8(cengine), prereqs);
1905 frontend::Alert::warning(_("Package not available"), msg, true);
1907 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1908 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1909 docstring const msg =
1910 bformat(_("Error reading cite engine %1$s\n"), from_utf8(cengine));
1911 frontend::Alert::warning(_("Read Error"), msg);
1919 /////////////////////////////////////////////////////////////////////////
1923 /////////////////////////////////////////////////////////////////////////
1925 DocumentClass::DocumentClass(LayoutFile const & tc)
1930 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1932 for (auto const & l : layoutlist_)
1933 if (l.latexname() == lay)
1939 bool DocumentClass::provides(string const & p) const
1941 return provides_.find(p) != provides_.end();
1945 bool DocumentClass::hasTocLevels() const
1947 return min_toclevel_ != Layout::NOT_IN_TOC;
1951 Layout const & DocumentClass::getTOCLayout() const
1953 // we're going to look for the layout with the minimum toclevel
1954 int minlevel = 1000;
1955 Layout const * lay = nullptr;
1956 for (auto const & l : *this) {
1957 int const level = l.toclevel;
1958 // we don't want Part or unnumbered sections
1959 if (level == Layout::NOT_IN_TOC || level < 0
1960 || level >= minlevel || l.counter.empty())
1967 // hmm. that is very odd, so we'll do our best.
1968 return operator[](defaultLayoutName());
1972 Layout const & DocumentClass::htmlTOCLayout() const
1974 if (html_toc_section_.empty())
1975 html_toc_section_ = getTOCLayout().name();
1976 return operator[](html_toc_section_);
1980 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1981 string const & entry, bool const punct, string const & fallback) const
1983 string default_format = "{%fullnames:author%[[%fullnames:author%, ]][[{%fullnames:editor%[[%fullnames:editor%, ed., ]]}]]}"
1984 "\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]]"
1985 "[[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
1987 default_format += ".";
1989 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1990 if (itype == cite_formats_.end())
1991 return default_format;
1992 map<string, string>::const_iterator it = itype->second.find(entry);
1993 if (it == itype->second.end() && !fallback.empty())
1994 it = itype->second.find(fallback);
1995 if (it == itype->second.end())
1996 return default_format;
1998 return it->second + ".";
2003 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
2004 string const & macro) const
2006 static string empty;
2007 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
2008 if (itype == cite_macros_.end())
2010 map<string, string>::const_iterator it = itype->second.find(macro);
2011 if (it == itype->second.end())
2017 vector<string> const DocumentClass::citeCommands(
2018 CiteEngineType const & type) const
2020 vector<CitationStyle> const styles = citeStyles(type);
2021 vector<string> cmds;
2022 for (auto const & cs : styles)
2023 cmds.push_back(cs.name);
2029 vector<CitationStyle> const & DocumentClass::citeStyles(
2030 CiteEngineType const & type) const
2032 static vector<CitationStyle> empty;
2033 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
2034 if (it == cite_styles_.end())
2040 /////////////////////////////////////////////////////////////////////////
2044 /////////////////////////////////////////////////////////////////////////
2046 ostream & operator<<(ostream & os, PageSides p)