diff --git a/lib/chkconfig.ltx b/lib/chkconfig.ltx index 87052e5496..289c30a7d8 100644 --- a/lib/chkconfig.ltx +++ b/lib/chkconfig.ltx @@ -284,6 +284,7 @@ \TestPackage{nicefrac} \TestPackage{nomencl} \TestPackage{pdfcolmk} +\TestPackage{polyglossia} \TestPackage{pdfpages} \TestPackage{prettyref} \TestPackage{preview} diff --git a/lib/doc/LaTeXConfig.lyx b/lib/doc/LaTeXConfig.lyx index c483837112..eace045a00 100644 --- a/lib/doc/LaTeXConfig.lyx +++ b/lib/doc/LaTeXConfig.lyx @@ -1,5 +1,5 @@ #LyX 2.0.0svn created this file. For more info see http://www.lyx.org/ -\lyxformat 401 +\lyxformat 408 \begin_document \begin_header \textclass article @@ -37,6 +37,7 @@ \use_indices false \paperorientation portrait \suppress_date false +\use_refstyle 0 \index Index \shortcut idx \color #008000 @@ -4276,6 +4277,48 @@ mongolian-babel Mongolian. \end_layout +\begin_layout Subsection +polyglossia +\end_layout + +\begin_layout Description +Found: +\begin_inset Info +type "package" +arg "polyglossia" +\end_inset + + +\end_layout + +\begin_layout Description +CTAN: +\family typewriter +macros/xetex/latex/polyglossia/ +\end_layout + +\begin_layout Description +Notes: The package +\family sans +polyglossia +\family default + provides a language interface specifically for XeTeX. + It aims to supersede the +\family sans +babel +\family default + package by using XeTeX's mutlilingual and multiscript facilities. + If installed, +\family sans +polyglossia +\family default + will by used for language handling instead of +\family sans +babel +\family default + when XeTeX is used as output format. +\end_layout + \begin_layout Subsection turkmen \end_layout diff --git a/src/Buffer.cpp b/src/Buffer.cpp index c7682d11eb..b78363eee4 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -1386,6 +1386,7 @@ void Buffer::writeLaTeXSource(odocstream & os, MacroSet parentMacros; listParentMacros(parentMacros, features); + runparams.use_polyglossia = features.usePolyglossia(); // Write the preamble runparams.use_babel = params().writeLaTeX(os, features, d->texrow, diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index e5c701ac9d..640f3544d6 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -1305,15 +1305,16 @@ bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features, ostringstream language_options; bool const use_babel = features.useBabel() && !tclass.provides("babel"); - if (use_babel) { + bool const use_polyglossia = features.usePolyglossia(); + bool const global = lyxrc.language_global_options; + if (use_babel || (use_polyglossia && global)) { language_options << features.getLanguages(); if (!language->babel().empty()) { if (!language_options.str().empty()) language_options << ','; language_options << language->babel(); } - if (lyxrc.language_global_options - && !features.needBabelLangOptions()) + if (global && !features.needBabelLangOptions()) clsoptions << language_options.str() << ','; } @@ -1721,15 +1722,15 @@ bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features, // If we use hyperref, jurabib, japanese, or vietnamese, we have to call babel before them. if (use_babel - && (features.isRequired("jurabib") - || features.isRequired("hyperref") - || features.isRequired("vietnamese") - || features.isRequired("japanese") ) ) { - // FIXME UNICODE - lyxpreamble += from_utf8(features.getBabelPresettings()); - lyxpreamble += from_utf8(babelCall(language_options.str(), - features.needBabelLangOptions())) + '\n'; - lyxpreamble += from_utf8(features.getBabelPostsettings()); + && (features.isRequired("jurabib") + || features.isRequired("hyperref") + || features.isRequired("vietnamese") + || features.isRequired("japanese"))) { + // FIXME UNICODE + lyxpreamble += from_utf8(features.getBabelPresettings()); + lyxpreamble += from_utf8(babelCall(language_options.str(), + features.needBabelLangOptions())) + '\n'; + lyxpreamble += from_utf8(features.getBabelPostsettings()); } // The optional packages; @@ -1877,6 +1878,7 @@ bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features, lyxpreamble += from_utf8(features.getBabelPostsettings()); } + // FIXME Polyglossia? docstring const i18npreamble = features.getTClassI18nPreamble(use_babel); if (!i18npreamble.empty()) lyxpreamble += i18npreamble + '\n'; @@ -1896,6 +1898,29 @@ bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features, os << "\\usepackage{xltxtra}\n"; texrow.newline(); } + // Polyglossia must be loaded after xltxtra + if (use_polyglossia) { + // call the package + os << "\\usepackage{polyglossia}\n"; + texrow.newline(); + // set the main language + os << "\\setdefaultlanguage"; + if (!language->polyglossiaOpts().empty()) + os << "[" << from_ascii(language->polyglossiaOpts()) << "]"; + os << "{" + from_ascii(language->polyglossia()) + "}\n"; + texrow.newline(); + // now setup the other languages + std::map const polylangs = + features.getPolyglossiaLanguages(); + for (std::map::const_iterator mit = polylangs.begin(); + mit != polylangs.end() ; ++mit) { + os << "\\setotherlanguage"; + if (!mit->second.empty()) + os << "[" << from_ascii(mit->second) << "]"; + os << "{" << from_ascii(mit->first) << "}\n"; + texrow.newline(); + } + } return use_babel; } diff --git a/src/Font.cpp b/src/Font.cpp index cf8ea61785..bf99b3e658 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -272,7 +272,18 @@ int Font::latexWriteStartChanges(odocstream & os, BufferParams const & bparams, bool env = false; int count = 0; - if (language()->babel() != base.language()->babel() && + + // polyglossia or babel? + if (runparams.use_polyglossia) { + if (!language()->polyglossia().empty()) { + string tmp = "\\text" + language()->polyglossia(); + if (!language()->polyglossiaOpts().empty()) + tmp += "[" + language()->polyglossiaOpts() + "]"; + tmp += "{"; + os << from_ascii(tmp); + count += tmp.length(); + } + } else if (language()->babel() != base.language()->babel() && language() != prev.language()) { if (language()->lang() == "farsi") { os << "\\textFR{"; diff --git a/src/LaTeXFeatures.cpp b/src/LaTeXFeatures.cpp index c6516995c9..530275860b 100644 --- a/src/LaTeXFeatures.cpp +++ b/src/LaTeXFeatures.cpp @@ -285,10 +285,23 @@ LaTeXFeatures::LaTeXFeatures(Buffer const & b, BufferParams const & p, bool LaTeXFeatures::useBabel() const { - return (lyxrc.language_package_selection != LyXRC::LP_NONE) && - ((bufferParams().language->lang() != lyxrc.default_language && - !bufferParams().language->babel().empty()) || - this->hasLanguages()); + return (lyxrc.language_package_selection != LyXRC::LP_NONE) + && !usePolyglossia() + && ((bufferParams().language->lang() != lyxrc.default_language + && !bufferParams().language->babel().empty()) + || this->hasLanguages()); +} + + +bool LaTeXFeatures::usePolyglossia() const +{ + return (lyxrc.language_package_selection == LyXRC::LP_AUTO) + && isRequired("xetex") + && isAvailable("polyglossia") + && ((bufferParams().language->lang() != lyxrc.default_language + && !bufferParams().language->polyglossia().empty()) + || this->hasLanguages()) + && this->hasPolyglossiaLanguages(); } @@ -474,6 +487,19 @@ bool LaTeXFeatures::hasLanguages() const } +bool LaTeXFeatures::hasPolyglossiaLanguages() const +{ + LanguageList::const_iterator const begin = UsedLanguages_.begin(); + for (LanguageList::const_iterator cit = begin; + cit != UsedLanguages_.end(); + ++cit) { + if ((*cit)->polyglossia().empty()) + return false; + } + return true; +} + + string LaTeXFeatures::getLanguages() const { ostringstream languages; @@ -490,6 +516,20 @@ string LaTeXFeatures::getLanguages() const } +std::map LaTeXFeatures::getPolyglossiaLanguages() const +{ + std::map languages; + + LanguageList::const_iterator const begin = UsedLanguages_.begin(); + for (LanguageList::const_iterator cit = begin; + cit != UsedLanguages_.end(); + ++cit) { + languages[(*cit)->polyglossia()] = (*cit)->polyglossiaOpts(); + } + return languages; +} + + set LaTeXFeatures::getEncodingSet(string const & doc_encoding) const { // This does only find encodings of languages supported by babel, but @@ -825,7 +865,7 @@ docstring const LaTeXFeatures::getMacros() const if (mustProvide("lyxarrow")) macros << lyxarrow_def << '\n'; - if (mustProvide("textgreek")) { + if (!usePolyglossia() && mustProvide("textgreek")) { // Avoid a LaTeX error if times fonts are used and the grtimes // package is installed but actual fonts are not (bug 6469). if (params_.fontsRoman == "times") @@ -839,7 +879,7 @@ docstring const LaTeXFeatures::getMacros() const macros << textgreek_def << '\n'; } - if (mustProvide("textcyr")) + if (!usePolyglossia() && mustProvide("textcyr")) macros << textcyr_def << '\n'; if (mustProvide("lyxmathsym")) diff --git a/src/LaTeXFeatures.h b/src/LaTeXFeatures.h index 9bb50f57cf..999c1fb460 100644 --- a/src/LaTeXFeatures.h +++ b/src/LaTeXFeatures.h @@ -101,9 +101,13 @@ public: void useLanguage(Language const *); /// bool hasLanguages() const; + /// check if all used languages are supported by polyglossia + bool hasPolyglossiaLanguages() const; /// std::string getLanguages() const; /// + std::map getPolyglossiaLanguages() const; + /// std::set getEncodingSet(std::string const & doc_encoding) const; /// void useLayout(docstring const & lyt); @@ -117,6 +121,8 @@ public: BufferParams const & bufferParams() const; /// the return value is dependent upon both LyXRC and LaTeXFeatures. bool useBabel() const; + /// + bool usePolyglossia() const; /// are we in a float? bool inFloat() const { return in_float_; } /// are we in a float? diff --git a/src/OutputParams.cpp b/src/OutputParams.cpp index 211a10c629..b575a2915a 100644 --- a/src/OutputParams.cpp +++ b/src/OutputParams.cpp @@ -21,7 +21,7 @@ namespace lyx { OutputParams::OutputParams(Encoding const * enc) : flavor(LATEX), math_flavor(NotApplicable), nice(false), moving_arg(false), inulemcmd(false), local_font(0), master_language(0), encoding(enc), - free_spacing(false), use_babel(false), + free_spacing(false), use_babel(false), use_polyglossia(false), use_indices(false), use_japanese(false), linelen(0), depth(0), exportdata(new ExportData), inComment(false), inTableCell(NO), inFloat(NONFLOAT), diff --git a/src/OutputParams.h b/src/OutputParams.h index 9ae7c3721c..6653a41a0f 100644 --- a/src/OutputParams.h +++ b/src/OutputParams.h @@ -136,6 +136,10 @@ public: */ bool use_babel; + /** Do we use polyglossia (instead of babel)? + */ + bool use_polyglossia; + /** Are we generating multiple indices? */ bool use_indices; diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index 326c5c9813..19caf37546 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -2369,12 +2369,16 @@ void Paragraph::latex(BufferParams const & bparams, open_font = false; } + string const running_lang = runparams.use_polyglossia ? + running_font.language()->polyglossia() : running_font.language()->babel(); // close babel's font environment before opening CJK. - if (!running_font.language()->babel().empty() && + string const lang_end_command = runparams.use_polyglossia ? + "\\end{$$lang}" : lyxrc.language_command_end; + if (!running_lang.empty() && font.language()->encoding()->package() == Encoding::CJK) { - string end_tag = subst(lyxrc.language_command_end, + string end_tag = subst(lang_end_command, "$$lang", - running_font.language()->babel()); + running_lang); os << from_ascii(end_tag); column += end_tag.length(); } diff --git a/src/ParagraphMetrics.cpp b/src/ParagraphMetrics.cpp index 0a90fb6925..a1a01a8c10 100644 --- a/src/ParagraphMetrics.cpp +++ b/src/ParagraphMetrics.cpp @@ -234,7 +234,7 @@ int ParagraphMetrics::singleWidth(pos_type pos, Font const & font) const c = par_->transformChar(c, pos); } else if (language->lang() == "hebrew" && Encodings::isHebrewComposeChar(c)) { - return 0; + return 0; } } return theFontMetrics(font).width(c); diff --git a/src/insets/InsetQuotes.cpp b/src/insets/InsetQuotes.cpp index 95a71abc45..4e47361bf7 100644 --- a/src/insets/InsetQuotes.cpp +++ b/src/insets/InsetQuotes.cpp @@ -267,18 +267,20 @@ int InsetQuotes::latex(odocstream & os, OutputParams const & runparams) const string qstr; if (language_ == FrenchQuotes && times_ == DoubleQuotes - && prefixIs(runparams.local_font->language()->code(), "fr")) { + && prefixIs(runparams.local_font->language()->code(), "fr") + && !runparams.use_polyglossia) { if (side_ == LeftQuote) qstr = "\\og "; //the spaces are important here else qstr = " \\fg{}"; //and here - } else if (lyxrc.fontenc == "T1") { + } else if (lyxrc.fontenc == "T1" && !runparams.use_polyglossia) { qstr = latex_quote_t1[times_][quoteind]; #ifdef DO_USE_DEFAULT_LANGUAGE } else if (doclang == "default") { #else } else if (!runparams.use_babel) { #endif + // these are also used by polyglossia qstr = latex_quote_ot1[times_][quoteind]; } else { qstr = latex_quote_babel[times_][quoteind]; diff --git a/src/output_latex.cpp b/src/output_latex.cpp index ea4d035367..c52cff0878 100644 --- a/src/output_latex.cpp +++ b/src/output_latex.cpp @@ -97,6 +97,16 @@ TeXDeeper(Buffer const & buf, } +string const getPolyglossiaEnvName(Language const * lang) +{ + string result = lang->polyglossia(); + if (result == "arabic") + // exceptional spelling; see polyglossia docs. + result = "Arabic"; + return result; +} + + ParagraphList::const_iterator TeXEnvironment(Buffer const & buf, Text const & text, @@ -131,29 +141,47 @@ TeXEnvironment(Buffer const & buf, ? (use_prev_env_language ? prev_env_language_ : priorpit->getParLanguage(bparams)) : doc_language; - if (par_language->babel() != prev_par_language->babel()) { + string par_lang = par_language->babel(); + string prev_par_lang = prev_par_language->babel(); + string doc_lang = doc_language->babel(); + string lang_begin_command = lyxrc.language_command_begin; + string lang_end_command = lyxrc.language_command_end; - if (!lyxrc.language_command_end.empty() && - prev_par_language->babel() != doc_language->babel() && - !prev_par_language->babel().empty()) { + if (runparams.use_polyglossia) { + par_lang = getPolyglossiaEnvName(par_language); + prev_par_lang = getPolyglossiaEnvName(prev_par_language); + doc_lang = getPolyglossiaEnvName(doc_language); + lang_begin_command = "\\begin{$$lang}"; + lang_end_command = "\\end{$$lang}"; + } + + if (par_lang != prev_par_lang) { + if (!lang_end_command.empty() && + prev_par_lang != doc_lang && + !prev_par_lang.empty()) { os << from_ascii(subst( - lyxrc.language_command_end, + lang_end_command, "$$lang", - prev_par_language->babel())) - // the '%' is necessary to prevent unwanted whitespace - << "%\n"; + prev_par_lang)) + // the '%' is necessary to prevent unwanted whitespace + << "%\n"; texrow.newline(); } if ((lyxrc.language_command_end.empty() || - par_language->babel() != doc_language->babel()) && - !par_language->babel().empty()) { + par_lang != doc_lang) && + !par_lang.empty()) { os << from_ascii(subst( - lyxrc.language_command_begin, + lang_begin_command, "$$lang", - par_language->babel())) - // the '%' is necessary to prevent unwanted whitespace - << "%\n"; + par_lang)); + if (runparams.use_polyglossia + && !par_language->polyglossiaOpts().empty()) + os << "[" + << from_ascii(par_language->polyglossiaOpts()) + << "]"; + // the '%' is necessary to prevent unwanted whitespace + os << "%\n"; texrow.newline(); } } @@ -441,7 +469,22 @@ ParagraphList::const_iterator TeXOnePar(Buffer const & buf, : priorpit->getParLanguage(bparams)) : outer_language; - if (par_language->babel() != prev_language->babel() + string par_lang = par_language->babel(); + string prev_lang = prev_language->babel(); + string doc_lang = doc_language->babel(); + string outer_lang = outer_language->babel(); + string lang_begin_command = lyxrc.language_command_begin; + string lang_end_command = lyxrc.language_command_end; + + if (runparams.use_polyglossia) { + par_lang = getPolyglossiaEnvName(par_language); + prev_lang = getPolyglossiaEnvName(prev_language); + doc_lang = getPolyglossiaEnvName(doc_language); + outer_lang = getPolyglossiaEnvName(outer_language); + lang_begin_command = "\\begin{$$lang}"; + lang_end_command = "\\end{$$lang}"; + } + if (par_lang != prev_lang // check if we already put language command in TeXEnvironment() && !(style.isEnvironment() && (pit == paragraphs.begin() || @@ -449,13 +492,13 @@ ParagraphList::const_iterator TeXOnePar(Buffer const & buf, priorpit->getDepth() <= pit->getDepth()) || priorpit->getDepth() < pit->getDepth()))) { - if (!lyxrc.language_command_end.empty() && - prev_language->babel() != outer_language->babel() && - !prev_language->babel().empty()) + if (!lang_end_command.empty() && + prev_lang != outer_lang && + !prev_lang.empty()) { - os << from_ascii(subst(lyxrc.language_command_end, + os << from_ascii(subst(lang_end_command, "$$lang", - prev_language->babel())) + prev_lang)) // the '%' is necessary to prevent unwanted whitespace << "%\n"; texrow.newline(); @@ -466,29 +509,30 @@ ParagraphList::const_iterator TeXOnePar(Buffer const & buf, // the previous one, if the current language is different than the // outer_language (which is currently in effect once the previous one // is closed). - if ((lyxrc.language_command_end.empty() || - par_language->babel() != outer_language->babel()) && - !par_language->babel().empty()) { + if ((lang_end_command.empty() || + par_lang != outer_lang) && + !par_lang.empty()) { // If we're inside an inset, and that inset is within an \L or \R // (or equivalents), then within the inset, too, any opposite // language paragraph should appear within an \L or \R (in addition // to, outside of, the normal language switch commands). // This behavior is not correct for ArabTeX, though. - if ( // not for ArabTeX - (par_language->lang() != "arabic_arabtex" && - outer_language->lang() != "arabic_arabtex") && - // are we in an inset? - runparams.local_font != 0 && - // is the inset within an \L or \R? - // - // FIXME: currently, we don't check this; this means that - // we'll have unnnecessary \L and \R commands, but that - // doesn't seem to hurt (though latex will complain) - // - // is this paragraph in the opposite direction? - runparams.local_font->isRightToLeft() != - par_language->rightToLeft() - ) { + if (!runparams.use_polyglossia && + // not for ArabTeX + (par_language->lang() != "arabic_arabtex" && + outer_language->lang() != "arabic_arabtex") && + // are we in an inset? + runparams.local_font != 0 && + // is the inset within an \L or \R? + // + // FIXME: currently, we don't check this; this means that + // we'll have unnnecessary \L and \R commands, but that + // doesn't seem to hurt (though latex will complain) + // + // is this paragraph in the opposite direction? + runparams.local_font->isRightToLeft() != + par_language->rightToLeft() + ) { // FIXME: I don't have a working copy of the Arabi package, so // I'm not sure if the farsi and arabic_arabi stuff is correct // or not... @@ -509,11 +553,16 @@ ParagraphList::const_iterator TeXOnePar(Buffer const & buf, // With CJK, the CJK tag has to be closed first (see below) if (runparams.encoding->package() != Encoding::CJK) { os << from_ascii(subst( - lyxrc.language_command_begin, + lang_begin_command, "$$lang", - par_language->babel())) + par_lang)); + if (runparams.use_polyglossia + && !par_language->polyglossiaOpts().empty()) + os << "[" + << from_ascii(par_language->polyglossiaOpts()) + << "]"; // the '%' is necessary to prevent unwanted whitespace - << "%\n"; + os << "%\n"; texrow.newline(); } } @@ -566,9 +615,9 @@ ParagraphList::const_iterator TeXOnePar(Buffer const & buf, // With CJK, the CJK tag had to be closed first (see above) if (runparams.encoding->package() == Encoding::CJK) { os << from_ascii(subst( - lyxrc.language_command_begin, + lang_begin_command, "$$lang", - par_language->babel())) + par_lang)) // the '%' is necessary to prevent unwanted whitespace << "%\n"; texrow.newline(); @@ -709,6 +758,7 @@ ParagraphList::const_iterator TeXOnePar(Buffer const & buf, // needed if we're within an \L or \R that we may have opened above (not // necessarily in this paragraph) and are about to close. bool closing_rtl_ltr_environment = + !runparams.use_polyglossia && // not for ArabTeX (par_language->lang() != "arabic_arabtex" && outer_language->lang() != "arabic_arabtex") && @@ -734,7 +784,7 @@ ParagraphList::const_iterator TeXOnePar(Buffer const & buf, } // when the paragraph uses CJK, the language has to be closed earlier if (font.language()->encoding()->package() != Encoding::CJK) { - if (lyxrc.language_command_end.empty()) { + if (lang_end_command.empty()) { // If this is a child, we should restore the // master language after the last paragraph. Language const * const current_language = @@ -742,18 +792,21 @@ ParagraphList::const_iterator TeXOnePar(Buffer const & buf, && runparams.master_language) ? runparams.master_language : outer_language; - if (!current_language->babel().empty()) { + string const current_lang = runparams.use_polyglossia ? + getPolyglossiaEnvName(current_language) + : current_language->babel(); + if (!current_lang.empty()) { os << from_ascii(subst( - lyxrc.language_command_begin, + lang_begin_command, "$$lang", - current_language->babel())); + current_lang)); pending_newline = true; } - } else if (!par_language->babel().empty()) { + } else if (!par_lang.empty()) { os << from_ascii(subst( - lyxrc.language_command_end, + lang_end_command, "$$lang", - par_language->babel())); + par_lang)); pending_newline = true; } } @@ -895,14 +948,25 @@ void latexParagraphs(Buffer const & buf, } // if "auto begin" is switched off, explicitly switch the // language on at start + string const mainlang = runparams.use_polyglossia ? + getPolyglossiaEnvName(bparams.language) + : bparams.language->babel(); + string const lang_begin_command = runparams.use_polyglossia ? + "\\begin{$$lang}" : lyxrc.language_command_begin; + if (maintext && !lyxrc.language_auto_begin && - !bparams.language->babel().empty()) { + !mainlang.empty()) { // FIXME UNICODE - os << from_utf8(subst(lyxrc.language_command_begin, + os << from_utf8(subst(lang_begin_command, "$$lang", - bparams.language->babel())) - << '\n'; - texrow.newline(); + mainlang)); + if (runparams.use_polyglossia + && !bparams.language->polyglossiaOpts().empty()) + os << "[" + << from_ascii(bparams.language->polyglossiaOpts()) + << "]"; + os << '\n'; + texrow.newline(); } ParagraphList::const_iterator lastpar; @@ -969,11 +1033,13 @@ void latexParagraphs(Buffer const & buf, // if "auto end" is switched off, explicitly close the language at the end // but only if the last par is in a babel language - if (maintext && !lyxrc.language_auto_end && !bparams.language->babel().empty() && + string const lang_end_command = runparams.use_polyglossia ? + "\\end{$$lang}" : lyxrc.language_command_end; + if (maintext && !lyxrc.language_auto_end && !mainlang.empty() && lastpar->getParLanguage(bparams)->encoding()->package() != Encoding::CJK) { - os << from_utf8(subst(lyxrc.language_command_end, + os << from_utf8(subst(lang_end_command, "$$lang", - bparams.language->babel())) + mainlang)) << '\n'; texrow.newline(); }